diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..b7353733 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,35 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..066b2d92 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.gitignore b/.gitignore index de1273c4..83c16aac 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,4 @@ HOWTO-web2py-devel .idea/* site-packages/ logs/ +__pycache__/ diff --git a/.gitmodules b/.gitmodules index 71525aff..9cf0e61e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "gluon/packages/dal"] path = gluon/packages/dal url = https://github.com/web2py/pydal.git +[submodule "gluon/packages/yatl"] + path = gluon/packages/yatl + url = https://github.com/web2py/yatl diff --git a/.travis.yml b/.travis.yml index 644e6211..83b3d7f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,20 +4,22 @@ sudo: required cache: pip -dist: "trusty" +dist: "xenial" + +services: + - mysql python: - '2.7' - '3.5' - '3.6' - '3.6-dev' + - '3.7' - '3.7-dev' - - 'pypy-5.3.1' - - 'pypy3.5-5.7.1-beta' + - 'pypy3.5' matrix: allow_failures: - - python: 'pypy3.5-5.7.1-beta' - python: '3.6-dev' - python: '3.7-dev' @@ -27,11 +29,9 @@ install: before_script: - pip install coverage - pip install codecov - - mysql -e 'create database pydal;' - - psql -c 'create database pydal;' -U postgres - - psql -c 'create extension postgis;' -U postgres -d pydal; - - psql -c 'SHOW SERVER_VERSION' -U postgres +before_install: + - mysql -e 'create database pydal;' script: export COVERAGE_PROCESS_START=gluon/tests/coverage.ini; ./web2py.py --run_system_tests --with_coverage @@ -41,9 +41,3 @@ after_success: notifications: email: true - -addons: - postgresql: "9.4" - apt: - packages: - - postgresql-9.4-postgis-2.3 diff --git a/CHANGELOG b/CHANGELOG index 4869fb15..47b185a4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,32 @@ -## 2.17.1 +## 2.19.0 +- new command line options (Thanks Paolo Pastori) + +OLD NAME NEW NAME +================== ================== +--debug --log_level +--nogui --no_gui +--ssl_private_key --server_key +--ssl_certificate --server_cert +--minthreads --min_threads +--maxthreads --max_threads +--profiler --profiler_dir +--run-cron --with_cron +--softcron --soft_cron +--cron --cron_run +--cronjob * --cron_job * +--test --run_doctests + --add_options + --interface + --crontab + +## 2.18.1-2.18.5 +- pydal 19.04 +- made template its own module (Yet Another Template Language) +- improved python 3.4-3.7 support +- better regular expressions +- bug fixes + +## 2.17.1-2 - pydal 18.08 - many small bug fixes diff --git a/Makefile b/Makefile index 870c51f9..beed2957 100644 --- a/Makefile +++ b/Makefile @@ -6,23 +6,24 @@ clean: rm -f httpserver.log rm -f parameters*.py rm -f -r applications/*/compiled - find ./ -name '*~' -exec rm -f {} \; - find ./ -name '*.orig' -exec rm -f {} \; - find ./ -name '*.rej' -exec rm -f {} \; - find ./ -name '#*' -exec rm -f {} \; - find ./ -name 'Thumbs.db' -exec rm -f {} \; - # find ./gluon/ -name '.*' -exec rm -f {} \; - find ./gluon/ -name '*class' -exec rm -f {} \; - find ./applications/admin/ -name '.*' -exec rm -f {} \; - find ./applications/examples/ -name '.*' -exec rm -f {} \; - find ./applications/welcome/ -name '.*' -exec rm -f {} \; - find ./ -name '*.pyc' -exec rm -f {} \; + find . -name '*~' -exec rm -f {} \; + find . -name '*.orig' -exec rm -f {} \; + find . -name '*.rej' -exec rm -f {} \; + find . -name '#*' -exec rm -f {} \; + find . -name 'Thumbs.db' -exec rm -f {} \; + # find . -name '.tox' -exec rm -rf {} \; + # find . -name '__pycache__' -exec rm -rf {} \; + find gluon/ -name '*class' -exec rm -f {} \; + find applications/admin/ -name '.*' -exec rm -f {} \; + find applications/examples/ -name '.*' -exec rm -f {} \; + find applications/welcome/ -name '.*' -exec rm -f {} \; + find . -name '*.pyc' -exec rm -f {} \; tests: - python web2py.py --run_system_tests + python web2py.py --verbose --run_system_tests coverage: coverage erase --rcfile=gluon/tests/coverage.ini export COVERAGE_PROCESS_START=gluon/tests/coverage.ini - python web2py.py --run_system_tests --with_coverage + python web2py.py --verbose --run_system_tests --with_coverage coverage combine --rcfile=gluon/tests/coverage.ini sleep 1 coverage html --rcfile=gluon/tests/coverage.ini @@ -44,9 +45,9 @@ rmfiles: rm -rf applications/examples/uploads/* src: ### Use semantic versioning - echo 'Version 2.17.1-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION + echo 'Version 2.18.5-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION ### rm -f all junk files - #make clean + make clean # make rmfiles ### make welcome layout and appadmin the default cp applications/welcome/views/appadmin.html applications/admin/views @@ -56,7 +57,7 @@ src: ### build web2py_src.zip echo '' > NEWINSTALL mv web2py_src.zip web2py_src_old.zip | echo 'no old' - cd ..; zip -r --exclude=**.git** web2py/web2py_src.zip web2py/web2py.py web2py/anyserver.py web2py/fabfile.py web2py/gluon/* web2py/extras/* web2py/handlers/* web2py/examples/* web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/MANIFEST.in web2py/scripts/*.sh web2py/scripts/*.py web2py/applications/admin web2py/applications/examples/ web2py/applications/welcome web2py/applications/__init__.py web2py/site-packages/__init__.py web2py/gluon/tests/*.sh web2py/gluon/tests/*.py + cd ..; zip -r --exclude=**.git** --exclude=**.tox** --exclude=**_pycache__** web2py/web2py_src.zip web2py/web2py.py web2py/anyserver.py web2py/fabfile.py web2py/gluon/* web2py/extras/* web2py/handlers/* web2py/examples/* web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/MANIFEST.in web2py/scripts/*.sh web2py/scripts/*.py web2py/applications/admin web2py/applications/examples/ web2py/applications/welcome web2py/applications/__init__.py web2py/site-packages/__init__.py web2py/gluon/tests/*.sh web2py/gluon/tests/*.py mdp: make src diff --git a/VERSION b/VERSION index 5cb3e1ea..67432273 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.17.1-stable+timestamp.2018.08.05.17.57.00 +Version 2.18.5-stable+timestamp.2019.04.07.21.13.59 diff --git a/anyserver.py b/anyserver.py index b935a1ed..261dd422 100644 --- a/anyserver.py +++ b/anyserver.py @@ -212,16 +212,16 @@ def mongrel2_handler(application, conn, debug=False): while True: if debug: - print "WAITING FOR REQUEST" + print("WAITING FOR REQUEST") # receive a request req = conn.recv() if debug: - print "REQUEST BODY: %r\n" % req.body + print("REQUEST BODY: %r\n" % req.body) if req.is_disconnect(): if debug: - print "DISCONNECT" + print("DISCONNECT") continue # effectively ignore the disconnect from the client # Set a couple of environment attributes a.k.a. header attributes @@ -247,7 +247,7 @@ def mongrel2_handler(application, conn, debug=False): environ['wsgi.input'] = req.body if debug: - print "ENVIRON: %r\n" % environ + print("ENVIRON: %r\n" % environ) # SimpleHandler needs file-like stream objects for # requests, errors and responses @@ -282,10 +282,10 @@ def mongrel2_handler(application, conn, debug=False): # return the response if debug: - print "RESPONSE: %r\n" % response + print("RESPONSE: %r\n" % response) if errors: if debug: - print "ERRORS: %r" % errors + print("ERRORS: %r" % errors) data = "%s\r\n\r\n%s" % (data, errors) conn.reply_http( req, data, code=code, status=status, headers=headers) @@ -355,8 +355,8 @@ def main(): dest='workers', help='number of workers number') (options, args) = parser.parse_args() - print 'starting %s on %s:%s...' % ( - options.server, options.ip, options.port) + print('starting %s on %s:%s...' % ( + options.server, options.ip, options.port)) run(options.server, options.ip, options.port, logging=options.logging, profiler=options.profiler_dir, options=options) diff --git a/applications/admin/controllers/appadmin.py b/applications/admin/controllers/appadmin.py index da050a5b..3a076002 100644 --- a/applications/admin/controllers/appadmin.py +++ b/applications/admin/controllers/appadmin.py @@ -30,7 +30,7 @@ except: if request.is_https: session.secure() -elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1") and \ +elif (remote_addr not in hosts) and (remote_addr != '127.0.0.1') and \ (request.function != 'manage'): raise HTTP(200, T('appadmin is disabled because insecure channel')) @@ -46,7 +46,7 @@ if request.function == 'manage': auth.table_permission()]) manager_role = manager_action.get('role', None) if manager_action else None if not (gluon.fileutils.check_credentials(request) or auth.has_membership(manager_role)): - raise HTTP(403, "Not authorized") + raise HTTP(403, 'Not authorized') menu = False elif (request.application == 'admin' and not session.authorized) or \ (request.application != 'admin' and not gluon.fileutils.check_credentials(request)): @@ -182,12 +182,12 @@ def select(): db = get_database(request) dbname = request.args[0] try: - is_imap = db._uri.startswith("imap://") + is_imap = db._uri.startswith('imap://') except (KeyError, AttributeError, TypeError): is_imap = False - regex = re.compile('(?P\w+)\.(?P\w+)=(?P\d+)') + regex = re.compile(r'(?P
\w+)\.(?P\w+)=(?P\d+)') if len(request.args) > 1 and hasattr(db[request.args[1]], '_primarykey'): - regex = re.compile('(?P
\w+)\.(?P\w+)=(?P.+)') + regex = re.compile(r'(?P
\w+)\.(?P\w+)=(?P.+)') if request.vars.query: match = regex.match(request.vars.query) if match: @@ -224,20 +224,20 @@ def select(): session.last_orderby = orderby session.last_query = request.vars.query form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px', - _name='query', _value=request.vars.query or '', _class="form-control", + _name='query', _value=request.vars.query or '', _class='form-control', requires=IS_NOT_EMPTY( - error_message=T("Cannot be empty")))), TR(T('Update:'), + error_message=T('Cannot be empty')))), TR(T('Update:'), INPUT(_name='update_check', _type='checkbox', value=False), INPUT(_style='width:400px', _name='update_fields', _value=request.vars.update_fields - or '', _class="form-control")), TR(T('Delete:'), INPUT(_name='delete_check', + or '', _class='form-control')), TR(T('Delete:'), INPUT(_name='delete_check', _class='delete', _type='checkbox', value=False), ''), - TR('', '', INPUT(_type='submit', _value=T('submit'), _class="btn btn-primary"))), + TR('', '', INPUT(_type='submit', _value=T('Submit'), _class='btn btn-primary'))), _action=URL(r=request, args=request.args)) tb = None if form.accepts(request.vars, formname=None): - regex = re.compile(request.args[0] + '\.(?P
\w+)\..+') + regex = re.compile(request.args[0] + r'\.(?P
\w+)\..+') match = regex.match(form.vars.query.strip()) if match: table = match.group('table') @@ -254,8 +254,8 @@ def select(): if is_imap: fields = [db[table][name] for name in - ("id", "uid", "created", "to", - "sender", "subject")] + ('id', 'uid', 'created', 'to', + 'sender', 'subject')] if orderby: rows = db(query, ignore_common_filters=True).select( *fields, limitby=(start, stop), @@ -271,10 +271,10 @@ def select(): # begin handle upload csv csv_table = table or request.vars.table if csv_table: - formcsv = FORM(str(T('or import from csv file')) + " ", + formcsv = FORM(str(T('or import from csv file')) + ' ', INPUT(_type='file', _name='csvfile'), INPUT(_type='hidden', _value=csv_table, _name='table'), - INPUT(_type='submit', _value=T('import'), _class="btn btn-primary")) + INPUT(_type='submit', _value=T('import'), _class='btn btn-primary')) else: formcsv = None if formcsv and formcsv.process().accepted: @@ -356,26 +356,26 @@ def state(): def ccache(): if is_gae: form = FORM( - P(TAG.BUTTON(T("Clear CACHE?"), _type="submit", _name="yes", _value="yes"))) + P(TAG.BUTTON(T('Clear CACHE?'), _type='submit', _name='yes', _value='yes'))) else: cache.ram.initialize() cache.disk.initialize() form = FORM( P(TAG.BUTTON( - T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")), + T('Clear CACHE?'), _type='submit', _name='yes', _value='yes')), P(TAG.BUTTON( - T("Clear RAM"), _type="submit", _name="ram", _value="ram")), + T('Clear RAM'), _type='submit', _name='ram', _value='ram')), P(TAG.BUTTON( - T("Clear DISK"), _type="submit", _name="disk", _value="disk")), + T('Clear DISK'), _type='submit', _name='disk', _value='disk')), ) if form.accepts(request.vars, session): - session.flash = "" + session.flash = '' if is_gae: if request.vars.yes: cache.ram.clear() - session.flash += T("Cache Cleared") + session.flash += T('Cache Cleared') else: clear_ram = False clear_disk = False @@ -387,10 +387,10 @@ def ccache(): clear_disk = True if clear_ram: cache.ram.clear() - session.flash += T("Ram Cleared") + session.flash += T('Ram Cleared') if clear_disk: cache.disk.clear() - session.flash += T("Disk Cleared") + session.flash += T('Disk Cleared') redirect(URL(r=request)) try: @@ -436,7 +436,7 @@ def ccache(): gae_stats['ratio'] = ((gae_stats['hits'] * 100) / (gae_stats['hits'] + gae_stats['misses'])) except ZeroDivisionError: - gae_stats['ratio'] = T("?") + gae_stats['ratio'] = T('?') gae_stats['oldest'] = GetInHMS(time.time() - gae_stats['oldest_item_age']) total.update(gae_stats) else: @@ -502,7 +502,7 @@ def ccache(): TR(TD(B(T('Key'))), TD(B(T('Time in Cache (h:m:s)')))), *[TR(TD(k[0]), TD('%02d:%02d:%02d' % k[1])) for k in keys], **dict(_class='cache-keys', - _style="border-collapse: separate; border-spacing: .5em;")) + _style='border-collapse: separate; border-spacing: .5em;')) if not is_gae: ram['keys'] = key_table(ram['keys']) @@ -536,26 +536,26 @@ def table_template(table): # This is horribe HTML but the only one graphiz understands rows = [] cellpadding = 4 - color = "#000000" - bgcolor = "#FFFFFF" - face = "Helvetica" - face_bold = "Helvetica Bold" + color = '#000000' + bgcolor = '#FFFFFF' + face = 'Helvetica' + face_bold = 'Helvetica Bold' border = 0 rows.append(TR(TD(FONT(table, _face=face_bold, _color=bgcolor), _colspan=3, _cellpadding=cellpadding, - _align="center", _bgcolor=color))) + _align='center', _bgcolor=color))) for row in db[table]: rows.append(TR(TD(FONT(row.name, _color=color, _face=face_bold), - _align="left", _cellpadding=cellpadding, + _align='left', _cellpadding=cellpadding, _border=border), TD(FONT(row.type, _color=color, _face=face), - _align="left", _cellpadding=cellpadding, + _align='left', _cellpadding=cellpadding, _border=border), TD(FONT(types(row), _color=color, _face=face), - _align="center", _cellpadding=cellpadding, + _align='center', _cellpadding=cellpadding, _border=border))) - return "< %s >" % TABLE(*rows, **dict(_bgcolor=bgcolor, _border=1, + return '< %s >' % TABLE(*rows, **dict(_bgcolor=bgcolor, _border=1, _cellborder=0, _cellspacing=0) ).xml() @@ -632,15 +632,15 @@ def hooks(): if len(functions): method_hooks.append({'name': op, 'functions':functions}) if len(method_hooks): - tables.append({'name': "%s.%s" % (db_str, t), 'slug': IS_SLUG()("%s.%s" % (db_str,t))[0], 'method_hooks':method_hooks}) + tables.append({'name': '%s.%s' % (db_str, t), 'slug': IS_SLUG()('%s.%s' % (db_str,t))[0], 'method_hooks':method_hooks}) # Render ul_main = UL(_class='nav nav-list') for t in tables: ul_main.append(A(t['name'], _onclick="collapse('a_%s')" % t['slug'])) - ul_t = UL(_class='nav nav-list', _id="a_%s" % t['slug'], _style='display:none') + ul_t = UL(_class='nav nav-list', _id='a_%s' % t['slug'], _style='display:none') for op in t['method_hooks']: ul_t.append(LI(op['name'])) - ul_t.append(UL([LI(A(f['funcname'], _class="editor_filelink", _href=f['url']if 'url' in f else None, **{'_data-lineno':f['lineno']-1})) for f in op['functions']])) + ul_t.append(UL([LI(A(f['funcname'], _class='editor_filelink', _href=f['url']if 'url' in f else None, **{'_data-lineno':f['lineno']-1})) for f in op['functions']])) ul_main.append(ul_t) return ul_main @@ -650,11 +650,11 @@ def hooks(): # ########################################################### def d3_graph_model(): - """ See https://www.facebook.com/web2py/posts/145613995589010 from Bruno Rocha + ''' See https://www.facebook.com/web2py/posts/145613995589010 from Bruno Rocha and also the app_admin bg_graph_model function - Create a list of table dicts, called "nodes" - """ + Create a list of table dicts, called 'nodes' + ''' nodes = [] links = [] @@ -670,10 +670,10 @@ def d3_graph_model(): elif f_type == 'string': disp = field.length elif f_type == 'id': - disp = "PK" + disp = 'PK' elif f_type.startswith('reference') or \ f_type.startswith('list:reference'): - disp = "FK" + disp = 'FK' else: disp = ' ' fields.append(dict(name=field.name, type=field.type, disp=disp)) @@ -685,7 +685,7 @@ def d3_graph_model(): links.append(dict(source=tablename, target = referenced_table)) - nodes.append(dict(name=tablename, type="table", fields = fields)) + nodes.append(dict(name=tablename, type='table', fields = fields)) # d3 v4 allows individual modules to be specified. The complete d3 library is included below. response.files.append(URL('admin','static','js/d3.min.js')) diff --git a/applications/admin/controllers/default.py b/applications/admin/controllers/default.py index 28198d2a..bf06d401 100644 --- a/applications/admin/controllers/default.py +++ b/applications/admin/controllers/default.py @@ -166,9 +166,9 @@ def check_version(): new_version, version = check_new_version(request.env.web2py_version, WEB2PY_VERSION_URL) - if new_version == -1: + if new_version in (-1, -2): return A(T('Unable to check for upgrades'), _href=WEB2PY_URL) - elif new_version != True: + elif not new_version: return A(T('web2py is up to date'), _href=WEB2PY_URL) elif platform.system().lower() in ('windows', 'win32', 'win64') and os.path.exists("web2py.exe"): return SPAN('You should upgrade to %s' % version.split('(')[0]) @@ -562,7 +562,11 @@ def enable(): os.unlink(filename) return SPAN(T('Disable'), _style='color:green') else: - safe_open(filename, 'wb').write('disabled: True\ntime-disabled: %s' % request.now) + if PY2: + safe_open(filename, 'wb').write('disabled: True\ntime-disabled: %s' % request.now) + else: + str_ = 'disabled: True\ntime-disabled: %s' % request.now + safe_open(filename, 'wb').write(str_.encode('utf-8')) return SPAN(T('Enable'), _style='color:red') @@ -642,7 +646,10 @@ def edit(): # show settings tab and save prefernces if 'settings' in request.vars: if request.post_vars: # save new preferences - post_vars = request.post_vars.items() + if PY2: + post_vars = request.post_vars.items() + else: + post_vars = list(request.post_vars.items()) # Since unchecked checkbox are not serialized, we must set them as false by hand to store the correct preference in the settings post_vars += [(opt, 'false') for opt in preferences if opt not in request.post_vars] if config.save(post_vars): @@ -807,8 +814,11 @@ def edit(): if len(request.args) > 2 and request.args[1] == 'controllers': controller = (request.args[2])[:-3] - functions = find_exposed_functions(data) - functions = functions and sorted(functions) or [] + try: + functions = find_exposed_functions(data) + functions = functions and sorted(functions) or [] + except SyntaxError as err: + functions = ['SyntaxError:Line:%d' % err.lineno] else: (controller, functions) = (None, None) @@ -1110,7 +1120,7 @@ def design(): defines = {} for m in models: data = safe_read(apath('%s/models/%s' % (app, m), r=request)) - defines[m] = regex_tables.findall(data) + defines[m] = re.findall(REGEX_DEFINE_TABLE, data, re.MULTILINE) defines[m].sort() # Get all controllers @@ -1120,8 +1130,11 @@ def design(): functions = {} for c in controllers: data = safe_read(apath('%s/controllers/%s' % (app, c), r=request)) - items = find_exposed_functions(data) - functions[c] = items and sorted(items) or [] + try: + items = find_exposed_functions(data) + functions[c] = items and sorted(items) or [] + except SyntaxError as err: + functions[c] = ['SyntaxError:Line:%d' % err.lineno] # Get all views views = sorted( @@ -1131,12 +1144,12 @@ def design(): include = {} for c in views: data = safe_read(apath('%s/views/%s' % (app, c), r=request)) - items = regex_extend.findall(data) + items = re.findall(REGEX_EXTEND, data, re.MULTILINE) if items: extend[c] = items[0][1] - items = regex_include.findall(data) + items = re.findall(REGEX_INCLUDE, data) include[c] = [i[1] for i in items] # Get all modules @@ -1258,8 +1271,11 @@ def plugin(): functions = {} for c in controllers: data = safe_read(apath('%s/controllers/%s' % (app, c), r=request)) - items = find_exposed_functions(data) - functions[c] = items and sorted(items) or [] + try: + items = find_exposed_functions(data) + functions[c] = items and sorted(items) or [] + except SyntaxError as err: + functions[c] = ['SyntaxError:Line:%d' % err.lineno] # Get all views views = sorted( @@ -1269,11 +1285,11 @@ def plugin(): include = {} for c in views: data = safe_read(apath('%s/views/%s' % (app, c), r=request)) - items = regex_extend.findall(data) + items = re.findall(REGEX_EXTEND, data, re.MULTILINE) if items: extend[c] = items[0][1] - items = regex_include.findall(data) + items = re.findall(REGEX_INCLUDE, data) include[c] = [i[1] for i in items] # Get all modules diff --git a/applications/admin/cron/crontab b/applications/admin/cron/crontab deleted file mode 100644 index 3a32c0dc..00000000 --- a/applications/admin/cron/crontab +++ /dev/null @@ -1,2 +0,0 @@ -10 * * * * root **applications/admin/cron/expire_sessions.py - diff --git a/applications/admin/languages/af.py b/applications/admin/languages/af.py old mode 100755 new mode 100644 index 6351ae09..d4ef48bc --- a/applications/admin/languages/af.py +++ b/applications/admin/languages/af.py @@ -16,7 +16,23 @@ '(something like "it-it")': '(iets soos "it-it")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': 'Soek: **%s** lêre', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'Abort': 'Abort', 'About': 'oor', 'About application': 'Oor program', @@ -45,6 +61,7 @@ 'application is compiled and cannot be designed': 'application is compiled and cannot be designed', 'Application name:': 'Program naam:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'are not used', 'are not used yet': 'are not used yet', 'Are you sure you want to delete file "%s"?': 'Are you sure you want to delete file "%s"?', @@ -59,6 +76,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.', 'ATTENTION: you cannot edit the running application!': 'ATTENTION: you cannot edit the running application!', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available Databases and Tables': 'Available Databases and Tables', 'back': 'back', @@ -85,6 +103,7 @@ 'Change Admin Password': 'Change Admin Password', 'Change admin password': 'verander admin wagwoord', 'change editor settings': 'change editor settings', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'check all', 'Check for upgrades': 'soek vir upgrades', @@ -95,8 +114,10 @@ 'Clear CACHE?': 'Clear CACHE?', 'Clear DISK': 'Clear DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Click row to expand traceback', 'Click row to view a ticket': 'Click row to view a ticket', +'Client IP': 'Client IP', 'code': 'code', 'Code listing': 'Code listing', 'collapse/expand all': 'collapse/expand all', @@ -152,6 +173,7 @@ 'Deploy to PythonAnywhere': 'Deploy to PythonAnywhere', 'Deployment form': 'Deployment form', 'Deployment Interface': 'Deployment Interface', +'Description': 'Description', 'Description:': 'Description:', 'design': 'design', 'Detailed traceback description': 'Detailed traceback description', @@ -178,6 +200,7 @@ 'Download layouts from repository': 'Download layouts from repository', 'download plugins': 'aflaai plugins', 'Download plugins from repository': 'Download plugins from repository', +'E-mail': 'E-mail', 'Edit': 'wysig', 'edit all': 'edit all', 'Edit application': 'Wysig program', @@ -189,6 +212,9 @@ 'Editing Plural Forms File': 'Editing Plural Forms File', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Enable', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -225,8 +251,10 @@ 'filter': 'filter', 'Find Next': 'Find Next', 'Find Previous': 'Find Previous', +'First name': 'First name', 'Form has errors': 'Form has errors', 'Frames': 'Frames', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Functions with no doctests will result in [passed] tests.', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -241,6 +269,10 @@ 'Goto': 'Goto', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Help': 'hulp', 'here': 'here', 'Hide/Show Translated strings': 'Hide/Show Translated strings', @@ -257,11 +289,13 @@ 'Import/Export': 'Import/Export', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'includes', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'inspect attributes': 'inspect attributes', 'Install': 'installeer', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'Geinstalleerde apps', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': 'internal error', @@ -270,21 +304,31 @@ 'Invalid action': 'Invalid action', 'Invalid application name': 'Invalid application name', 'invalid circular reference': 'invalid circular reference', +'Invalid email': 'Invalid email', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'invalid password', +'Invalid password': 'Invalid password', 'invalid password.': 'invalid password.', 'Invalid Query': 'Invalid Query', 'invalid request': 'invalid request', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'invalid ticket', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': 'language file "%(filename)s" created/updated', 'Language files (static strings) updated': 'Language files (static strings) updated', 'languages': 'tale', 'Languages': 'Tale', +'Last name': 'Last name', 'Last Revision': 'Last Revision', 'Last saved on:': 'Last saved on:', 'License for': 'Lisensie vir', @@ -298,7 +342,11 @@ 'Local Apps': 'Local Apps', 'locals': 'locals', 'Locals##debug': 'Locals##debug', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'Login': 'Login', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'Login to the Administrative Interface', 'Login/Register': 'Login/Register', @@ -319,10 +367,12 @@ 'Modules': 'Modules', 'modules': 'modules', 'Multi User Mode': 'Multi User Mode', +'Name': 'Name', 'new application "%s" created': 'new application "%s" created', 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'New Application Wizard', 'New application wizard': 'Nuwe app wizard', +'New password': 'New password', 'new plugin installed': 'new plugin installed', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': 'New Record', @@ -341,6 +391,8 @@ 'Not Authorized': 'Not Authorized', 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'Open new app in new window': 'Open new app in new window', 'OpenShift Deployment Interface': 'OpenShift Deployment Interface', @@ -348,6 +400,7 @@ 'or alternatively': 'or alternatively', 'Or Get from URL:': 'Or Get from URL:', 'or import from csv file': 'or import from csv file', +'Origin': 'Origin', 'Original/Translation': 'Original/Translation', 'Overview': 'Overview', 'Overwrite installed app': 'skryf oor geinstalleerde program', @@ -355,7 +408,12 @@ 'Pack compiled': 'Pack compiled', 'Pack custom': 'Pack custom', 'pack plugin': 'pack plugin', +'Password': 'Password', 'password changed': 'password changed', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -363,6 +421,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Please', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" deleted', 'Plugin "%s" in application': 'Plugin "%s" in application', @@ -378,6 +437,7 @@ 'previous %s rows': 'previous %s rows', 'Private files': 'Private files', 'private files': 'private files', +'Profile updated': 'Profile updated', 'Project Progress': 'Project Progress', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -393,19 +453,35 @@ 'Ram Cleared': 'Ram Cleared', 'Rapid Search': 'Rapid Search', 'Record': 'Record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'record does not exist', 'Record id': 'Record id', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'register': 'register', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', +'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Reload routes', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'Remove compiled', 'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', 'Replace': 'Replace', 'Replace All': 'Replace All', 'Repository (%s)': 'Repository (%s)', 'request': 'request', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', +'Reset Password key': 'Reset Password key', 'Resolve Conflict file': 'Resolve Conflict file', 'response': 'response', 'restart': 'restart', @@ -441,6 +517,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'shell', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'site', 'Size of cache:': 'Size of cache:', @@ -490,7 +567,9 @@ 'There are no views': 'There are no views', 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'Hierdie lêre is sonder veranderinge geserved, jou images gaan hier', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', "This page can commit your changes to an openshift app repo and push them to your cloud instance. This assumes that you've already created the application instance using the web2py skeleton and have that repo somewhere on a filesystem that this web2py instance can access. This functionality requires GitPython installed and on the python path of the runtime that web2py is operating in.": "This page can commit your changes to an openshift app repo and push them to your cloud instance. This assumes that you've already created the application instance using the web2py skeleton and have that repo somewhere on a filesystem that this web2py instance can access. This functionality requires GitPython installed and on the python path of the runtime that web2py is operating in.", @@ -502,6 +581,7 @@ 'Ticket ID': 'Ticket ID', 'Ticket Missing': 'Ticket Missing', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', +'Timestamp': 'Timestamp', 'to previous version.': 'to previous version.', 'To create a plugin, name a file/folder plugin_[name]': 'Om n plugin te skep, noem n lêer/gids plugin_[name]', 'To emulate a breakpoint programatically, write:': 'To emulate a breakpoint programatically, write:', @@ -514,6 +594,7 @@ 'try something like': 'try something like', 'Try the mobile interface': 'Try the mobile interface', 'try view': 'try view', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'Unable to check for upgrades', @@ -527,6 +608,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': 'unable to parse csv file', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'unable to uninstall "%s"', 'unable to upgrade because "%s"': 'unable to upgrade because "%s"', 'uncheck all': 'uncheck all', @@ -547,10 +629,25 @@ 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.', 'Use an url:': 'Gebruik n url:', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'variables', +'Verify Password': 'Verify Password', 'Version': 'Version', 'Versioning': 'Versioning', 'versioning': 'versioning', @@ -569,14 +666,20 @@ 'web2py Recent Tweets': 'web2py Onlangse Tweets', 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py upgraded; please restart it', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Working...': 'Working...', 'WSGI reference name': 'WSGI reference name', 'YES': 'YES', 'Yes': 'Yes', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/bg.py b/applications/admin/languages/bg.py old mode 100755 new mode 100644 index c841bec6..ab460ad5 --- a/applications/admin/languages/bg.py +++ b/applications/admin/languages/bg.py @@ -16,7 +16,23 @@ '(something like "it-it")': '(something like "it-it")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': 'Searching: **%s** files', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': 'A new version of web2py is available', 'A new version of web2py is available: %s': 'A new version of web2py is available: %s', 'Abort': 'Abort', @@ -50,6 +66,7 @@ 'application is compiled and cannot be designed': 'application is compiled and cannot be designed', 'Application name:': 'Application name:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'are not used', 'are not used yet': 'are not used yet', 'Are you sure you want to delete file "%s"?': 'Are you sure you want to delete file "%s"?', @@ -66,6 +83,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.', 'ATTENTION: you cannot edit the running application!': 'ATTENTION: you cannot edit the running application!', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available databases and tables': 'Available databases and tables', 'Available Databases and Tables': 'Available Databases and Tables', @@ -94,6 +112,7 @@ 'Change Admin Password': 'Change Admin Password', 'Change admin password': 'change admin password', 'change editor settings': 'change editor settings', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'check all', 'Check for upgrades': 'check for upgrades', @@ -106,9 +125,11 @@ 'Clear RAM': 'Clear RAM', 'click here for online examples': 'щракни тук за онлайн примери', 'click here for the administrative interface': 'щракни тук за административния интерфейс', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Click row to expand traceback', 'Click row to view a ticket': 'Click row to view a ticket', 'click to check for upgrades': 'click to check for upgrades', +'Client IP': 'Client IP', 'code': 'code', 'Code listing': 'Code listing', 'collapse/expand all': 'collapse/expand all', @@ -168,6 +189,7 @@ 'Deploy to PythonAnywhere': 'Deploy to PythonAnywhere', 'Deployment form': 'Deployment form', 'Deployment Interface': 'Deployment Interface', +'Description': 'Description', 'Description:': 'Description:', 'design': 'дизайн', 'DESIGN': 'DESIGN', @@ -196,6 +218,7 @@ 'Download layouts from repository': 'Download layouts from repository', 'download plugins': 'download plugins', 'Download plugins from repository': 'Download plugins from repository', +'E-mail': 'E-mail', 'EDIT': 'EDIT', 'Edit': 'edit', 'edit all': 'edit all', @@ -211,6 +234,9 @@ 'Editing Plural Forms File': 'Editing Plural Forms File', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Enable', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -250,8 +276,10 @@ 'filter': 'filter', 'Find Next': 'Find Next', 'Find Previous': 'Find Previous', +'First name': 'First name', 'Form has errors': 'Form has errors', 'Frames': 'Frames', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Functions with no doctests will result in [passed] tests.', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -267,6 +295,10 @@ 'Goto': 'Goto', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Hello World': 'Здравей, свят', 'Help': 'help', 'here': 'here', @@ -285,6 +317,7 @@ 'Import/Export': 'Import/Export', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'includes', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'insert new': 'insert new', 'insert new %s': 'insert new %s', @@ -292,6 +325,7 @@ 'Install': 'install', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'Installed applications', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': 'internal error', @@ -300,15 +334,24 @@ 'Invalid action': 'Invalid action', 'Invalid application name': 'Invalid application name', 'invalid circular reference': 'invalid circular reference', +'Invalid email': 'Invalid email', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'invalid password', +'Invalid password': 'Invalid password', 'invalid password.': 'invalid password.', 'Invalid Query': 'Невалидна заявка', 'invalid request': 'невалидна заявка', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'invalid ticket', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': 'language file "%(filename)s" created/updated', @@ -316,6 +359,7 @@ 'languages': 'languages', 'Languages': 'Languages', 'languages updated': 'languages updated', +'Last name': 'Last name', 'Last Revision': 'Last Revision', 'Last saved on:': 'Last saved on:', 'License for': 'License for', @@ -329,8 +373,12 @@ 'Local Apps': 'Local Apps', 'locals': 'locals', 'Locals##debug': 'Locals##debug', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'Login': 'Login', 'login': 'login', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'Login to the Administrative Interface', 'Login/Register': 'Login/Register', @@ -351,10 +399,12 @@ 'Modules': 'Modules', 'modules': 'modules', 'Multi User Mode': 'Multi User Mode', +'Name': 'Name', 'new application "%s" created': 'new application "%s" created', 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'New Application Wizard', 'New application wizard': 'New application wizard', +'New password': 'New password', 'new plugin installed': 'new plugin installed', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': 'New Record', @@ -374,6 +424,8 @@ 'Not Authorized': 'Not Authorized', 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'Open new app in new window': 'Open new app in new window', 'OpenShift Deployment Interface': 'OpenShift Deployment Interface', @@ -383,6 +435,7 @@ 'or import from csv file': 'or import from csv file', 'or provide app url:': 'or provide app url:', 'or provide application url:': 'or provide application url:', +'Origin': 'Origin', 'Original/Translation': 'Original/Translation', 'Overview': 'Overview', 'Overwrite installed app': 'overwrite installed app', @@ -391,7 +444,12 @@ 'Pack custom': 'Pack custom', 'pack plugin': 'pack plugin', 'PAM authenticated user, cannot change password here': 'PAM authenticated user, cannot change password here', +'Password': 'Password', 'password changed': 'password changed', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -399,6 +457,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Please', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" deleted', 'Plugin "%s" in application': 'Plugin "%s" in application', @@ -415,6 +474,7 @@ 'previous 100 rows': 'previous 100 rows', 'Private files': 'Private files', 'private files': 'private files', +'Profile updated': 'Profile updated', 'Project Progress': 'Project Progress', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -431,20 +491,36 @@ 'Rapid Search': 'Rapid Search', 'Record': 'Record', 'record': 'record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'записът не съществува', 'record id': 'record id', 'Record id': 'Record id', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'register': 'register', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', +'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Reload routes', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'remove compiled', 'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', 'Replace': 'Replace', 'Replace All': 'Replace All', 'Repository (%s)': 'Repository (%s)', 'request': 'request', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', +'Reset Password key': 'Reset Password key', 'Resolve Conflict file': 'Resolve Conflict file', 'response': 'response', 'restart': 'restart', @@ -483,6 +559,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'shell', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'site', 'Size of cache:': 'Size of cache:', @@ -537,7 +614,9 @@ 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'These files are served without processing, your images go here', 'these files are served without processing, your images go here': 'these files are served without processing, your images go here', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', 'This is the %(filename)s template': 'This is the %(filename)s template', @@ -550,6 +629,7 @@ 'Ticket ID': 'Ticket ID', 'Ticket Missing': 'Ticket Missing', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', +'Timestamp': 'Timestamp', 'TM': 'TM', 'to previous version.': 'to previous version.', 'To create a plugin, name a file/folder plugin_[name]': 'To create a plugin, name a file/folder plugin_[name]', @@ -565,6 +645,7 @@ 'try something like': 'try something like', 'Try the mobile interface': 'Try the mobile interface', 'try view': 'try view', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'Unable to check for upgrades', @@ -580,6 +661,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': 'не е възможна обработката на csv файла', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'unable to uninstall "%s"', 'unable to upgrade because "%s"': 'unable to upgrade because "%s"', 'uncheck all': 'uncheck all', @@ -603,10 +685,25 @@ 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.', 'Use an url:': 'Use an url:', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'variables', +'Verify Password': 'Verify Password', 'Version': 'Version', 'Versioning': 'Versioning', 'versioning': 'versioning', @@ -626,6 +723,7 @@ 'web2py Recent Tweets': 'web2py Recent Tweets', 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py upgraded; please restart it', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py': 'Добре дошъл в web2py', 'Working...': 'Working...', 'WSGI reference name': 'WSGI reference name', @@ -633,8 +731,13 @@ 'Yes': 'Yes', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/cs.py b/applications/admin/languages/cs.py old mode 100755 new mode 100644 index e4667c24..69d00e95 --- a/applications/admin/languages/cs.py +++ b/applications/admin/languages/cs.py @@ -19,8 +19,23 @@ '(something like "it-it")': '(například "cs-cs")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', '@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(soubor **gluon/contrib/plural_rules/%s.py** nenalezen)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': 'Hledání: **%s** %%{soubor}', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'Abort': 'Abort', 'About': 'O programu', 'About application': 'O aplikaci', @@ -54,6 +69,7 @@ 'application is compiled and cannot be designed': 'application is compiled and cannot be designed', 'Application name:': 'Název aplikace:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'nepoužita', 'are not used yet': 'ještě nepoužita', 'Are you sure you want to delete file "%s"?': 'Are you sure you want to delete file "%s"?', @@ -68,6 +84,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.', 'ATTENTION: you cannot edit the running application!': 'ATTENTION: you cannot edit the running application!', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available Databases and Tables': 'Dostupné databáze a tabulky', 'back': 'zpět', @@ -107,6 +124,7 @@ 'Clear CACHE?': 'Vymazat CACHE?', 'Clear DISK': 'Vymazat DISK', 'Clear RAM': 'Vymazat RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Pro rozbalení stopy, klikněte na řádek', 'Click row to view a ticket': 'Pro zobrazení chyby (ticketu), klikněte na řádku...', 'Client IP': 'IP adresa klienta', @@ -220,6 +238,9 @@ 'Editor': 'Editor', 'Email Address': 'Email Address', 'Email and SMS': 'Email a SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Odblokovat', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -268,6 +289,7 @@ 'Forms and Validators': 'Formuláře a validátory', 'Frames': 'Frames', 'Free Applications': 'Aplikace zdarma', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Functions with no doctests will result in [passed] tests.', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -284,7 +306,9 @@ 'graph model': 'graph model', 'Graph Model': 'Graph Model', 'Group %(group_id)s created': 'Skupina %(group_id)s vytvořena', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'ID skupiny', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Groups': 'Skupiny', 'Hello World': 'Ahoj světe', 'Help': 'Nápověda', @@ -304,6 +328,7 @@ 'Import/Export': 'Import/Export', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'zahrnuje', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'Index': 'Index', 'insert new': 'vložit nový záznam ', @@ -312,6 +337,7 @@ 'Install': 'Instalovat', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'Nainstalované aplikace', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interakce v %s, na řádce %s', 'Interactive console': 'Interaktivní příkazová řádka', 'internal error': 'internal error', @@ -323,19 +349,26 @@ 'invalid circular reference': 'invalid circular reference', 'Invalid email': 'Neplatný email', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'Invalid password': 'Nesprávné heslo', 'invalid password': 'invalid password', 'invalid password.': 'neplatné heslo', 'Invalid Query': 'Neplatný dotaz', 'invalid request': 'Neplatný požadavek', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'invalid ticket', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Is Active': 'Je aktivní', 'It is %s %%{day} today.': 'Dnes je to %s %%{den}.', 'Key': 'Klíč', 'Key bindings': 'Vazby klíčů', 'Key bindings for ZenCoding Plugin': 'Key bindings for ZenCoding Plugin', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': 'language file "%(filename)s" created/updated', @@ -361,10 +394,12 @@ 'Local Apps': 'Local Apps', 'locals': 'locals', 'Locals##debug': 'Lokální proměnné', +'Log In': 'Log In', 'Logged in': 'Přihlášení proběhlo úspěšně', 'Logged out': 'Odhlášení proběhlo úspěšně', 'login': 'přihlásit se', 'Login': 'Přihlásit se', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'Přihlásit se do Správce aplikací', 'Login/Register': 'Login/Register', @@ -442,7 +477,10 @@ 'Password': 'Heslo', 'password': 'heslo', 'password changed': 'password changed', +'Password changed': 'Password changed', "Password fields don't match": 'Hesla se neshodují', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -450,6 +488,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Prosím', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" deleted', 'Plugin "%s" in application': 'Plugin "%s" in application', @@ -468,6 +507,7 @@ 'Private files': 'Soukromé soubory', 'private files': 'soukromé soubory', 'profile': 'profil', +'Profile updated': 'Profile updated', 'Project Progress': 'Vývoj projektu', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -487,14 +527,24 @@ 'Readme': 'Nápověda', 'Recipes': 'Postupy jak na to', 'Record': 'Záznam', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'záznam neexistuje', 'Record ID': 'ID záznamu', 'Record id': 'id záznamu', +'Record Updated': 'Record Updated', 'refresh': 'obnovte', 'register': 'registrovat', 'Register': 'Zaregistrovat se', 'Registration identifier': 'Registrační identifikátor', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Registrační klíč', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'reload': 'reload', 'Reload routes': 'Znovu nahrát cesty', 'Remember me (for 30 days)': 'Zapamatovat na 30 dní', @@ -504,6 +554,7 @@ 'Replace All': 'Zaměnit vše', 'Repository (%s)': 'Repository (%s)', 'request': 'request', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', 'Reset Password key': 'Reset registračního klíče', @@ -545,6 +596,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Bod přerušení nastaven v souboru %s na řádce %s: %s', 'shell': 'příkazová řádka', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'Správa aplikací', 'Size of cache:': 'Velikost cache:', @@ -601,7 +653,9 @@ 'These files are not served, they are only available from within your app': 'Tyto soubory jsou klientům nepřístupné. K dispozici jsou pouze v rámci aplikace.', 'These files are served without processing, your images go here': 'Tyto soubory jsou servírovány bez přídavné logiky, sem patří např. obrázky.', 'This App': 'Tato aplikace', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is a copy of the scaffolding application': 'Toto je kopie aplikace skelet.', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', @@ -630,6 +684,7 @@ 'Try the mobile interface': 'Zkuste rozhraní pro mobilní zařízení', 'try view': 'try view', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type python statement in here and hit Return (Enter) to execute it.': 'Type python statement in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', @@ -644,6 +699,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': 'csv soubor nedá sa zpracovat', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'unable to uninstall "%s"', 'unable to upgrade because "%s"': 'unable to upgrade because "%s"', 'uncheck all': 'vše odznačit', @@ -663,14 +719,21 @@ 'upload plugin file:': 'nahrát soubor modulu:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Použijte (...)&(...) pro AND, (...)|(...) pro OR a ~(...) pro NOT pro sestavení složitějších dotazů.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': 'Uživatel %(id)s přihlášen', 'User %(id)s Logged-out': 'Uživatel %(id)s odhlášen', 'User %(id)s Password changed': 'Uživatel %(id)s změnil heslo', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', 'User %(id)s Profile updated': 'Uživatel %(id)s upravil profil', 'User %(id)s Registered': 'Uživatel %(id)s se zaregistroval', 'User %(id)s Username retrieved': 'Uživatel %(id)s si nachal zaslat přihlašovací jméno', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'ID uživatele', 'Username': 'Přihlašovací jméno', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'variables', @@ -696,6 +759,7 @@ 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py upgraded; please restart it', 'Welcome': 'Vítejte', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py': 'Vitejte ve web2py', 'Welcome to web2py!': 'Vítejte ve web2py!', 'Which called the function %s located in the file %s': 'která zavolala funkci %s v souboru (kontroléru) %s.', @@ -708,9 +772,14 @@ 'You can inspect variables using the console bellow': 'Níže pomocí příkazové řádky si můžete prohlédnout proměnné', 'You can inspect variables using the console below': 'You can inspect variables using the console below', 'You can modify this application and adapt it to your needs': 'Tuto aplikaci si můžete upravit a přizpůsobit ji svým potřebám.', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'Je třeba nejprve nastavit a dojít až na', 'You only need these if you have already registered': 'You only need these if you have already registered', 'You visited the url %s': 'Navštívili jste stránku %s,', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Aplikace bude blokována než se klikne na jedno z tlačítek (další, krok, pokračovat, atd.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/de.py b/applications/admin/languages/de.py old mode 100755 new mode 100644 index da4fa1a9..65e2c821 --- a/applications/admin/languages/de.py +++ b/applications/admin/languages/de.py @@ -2,92 +2,108 @@ { '!langcode!': 'de', '!langname!': 'Deutsch', -'"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"Update" ist ein optionaler Ausdruck wie "Feld1 = \'newvalue". JOIN Ergebnisse können nicht aktualisiert oder gelöscht werden', -'"User Exception" debug mode. ': '"User Exception" debug mode. ', +'"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"Update" ist ein optionaler Ausdruck wie "feld1=\'newvalue\'". JOIN Ergebnisse können nicht aktualisiert oder gelöscht werden', +'"User Exception" debug mode. ': '"Benutzerausnahme" Debug-Modus.', '%s': '%s', -'%s %%{row} deleted': '%s %%{row} Zeilen gelöscht', -'%s %%{row} updated': '%s %%{row} Zeilen aktualisiert', -'%s selected': '%s selected', -'%s students registered': '%s students registered', +'%s %%{row} deleted': '%s %%{Reihe} gelöscht', +'%s %%{row} updated': '%s %%{Reihe} aktualisiert', +'%s selected': '%s ausgewählt', +'%s students registered': '%s Studenten registriert', '%Y-%m-%d': '%d.%m.%Y', '%Y-%m-%d %H:%M:%S': '%d.%m.%Y %H:%M:%S', -'(requires internet access)': '(Benötigt Internetzugang)', -'(requires internet access, experimental)': '(Benötigt Internetzugang)', +'(requires internet access)': '(Erfordert Internetzugang)', +'(requires internet access, experimental)': '(Erfordert Internetzugang, experimentell)', '(something like "it-it")': '(so etwas wie "it-it")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', '@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(Datei **gluon/contrib/plural_rules/%s.py** wurde nicht gefunden)', -'@markmin\x01An error occured, please [[reload %s]] the page': 'Ein Fehler ist aufgetreten, bitte [[reload %s]] sie die Seite erneut', -'@markmin\x01Searching: **%s** %%{file}': '@markmin\x01Suche: **%s** Dateien', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'Ein Fehler ist aufgetreten, bitte [[laden %s]] Sie die Seite neu', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Searching: **%s** %%{file}': 'Suche: **% s ** %% {Datei}', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': 'Eine neue Version von web2py ist verfügbar', 'A new version of web2py is available: %s': 'Eine neue Version von web2py ist verfügbar: %s', -'Abort': 'Abbrechen', +'Abort': 'Verwerfen', 'About': 'Über', 'About application': 'Über die Anwendung', -'Accept Terms': 'Accept Terms', -'Add breakpoint': 'Add breakpoint', +'Accept Terms': 'Nutzerbedingungen akzeptieren', +'Add breakpoint': 'Breakpoint hinzufügen', 'Additional code for your application': 'Zusätzlicher Code für Ihre Anwendung', -'Admin design page': 'Admin design page', -'admin disabled because no admin password': 'admin ist deaktiviert, weil kein Admin-Passwort gesetzt ist', -'admin disabled because not supported on google app engine': 'admin disabled because not supported on google app engine', -'admin disabled because not supported on google apps engine': 'admin ist deaktiviert, es existiert dafür keine Unterstützung auf der google apps engine', -'admin disabled because too many invalid login attempts': 'admin disabled because too many invalid login attempts', -'admin disabled because unable to access password file': 'admin ist deaktiviert, weil kein Zugriff auf die Passwortdatei besteht', +'Admin design page': 'Admin-Design-Seite', +'admin disabled because no admin password': 'Admin ist deaktiviert, da kein Admin-Passwort gesetzt ist', +'admin disabled because not supported on google app engine': 'Admin deaktiviert, da auf Google App Engine nicht unterstützt', +'admin disabled because not supported on google apps engine': 'Admin deaktiviert, da es auf Google Apps Engine nicht unterstützt wird.', +'admin disabled because too many invalid login attempts': 'Admin deaktiviert, weil zu viele ungültige Anmeldeversuche', +'admin disabled because unable to access password file': 'Admin deaktiviert, da der Zugriff auf die Kennwortdatei nicht möglich ist', 'Admin is disabled because insecure channel': 'Appadmin ist deaktiviert, wegen der Benutzung eines unsicheren Kanals', 'Admin is disabled because unsecure channel': 'Appadmin ist deaktiviert, wegen der Benutzung eines unsicheren Kanals', 'Admin language': 'Admin-Sprache', -'Admin versioning page': 'Admin versioning page', -'administrative interface': 'Administrative Schnittstelle', +'Admin versioning page': 'Admin-Versionierungsseite', +'administrative interface': 'Administratives Interface', 'Administrator Password:': 'Administrator Passwort:', 'An error occured, please %s the page': 'Ein Fehler ist aufgetereten, bitte %s die Seite', -'and rename it (required):': 'und benenne sie um (erforderlich):', -'and rename it:': ' und benenne sie um:', -'App does not exist or you are not authorized': 'App does not exist or you are not authorized', +'and rename it (required):': 'und benenne sie se um (erforderlich):', +'and rename it:': ' und benenne sie es um:', +'App does not exist or you are not authorized': 'App existiert nicht oder Sie sind nicht autorisiert', 'appadmin': 'appadmin', 'appadmin is disabled because insecure channel': 'Appadmin ist deaktiviert, wegen der Benutzung eines unsicheren Kanals', 'Application': 'Anwendung', 'application "%s" uninstalled': 'Anwendung "%s" deinstalliert', -'Application cannot be generated in demo mode': 'Application cannot be generated in demo mode', +'Application cannot be generated in demo mode': 'Anwendung kann nicht im Demo-Modus erstellt werden', 'application compiled': 'Anwendung kompiliert', -'Application exists already': 'Application exists already', +'Application exists already': 'Anwendung existiert bereits', 'application is compiled and cannot be designed': 'Die Anwendung ist kompiliert und kann deswegen nicht mehr geändert werden', -'Application name:': 'Name der Anwendung:', -'Application updated via git pull': 'Application updated via git pull', +'Application name:': 'Anwendungsname:', +'Application updated via git pull': 'Die Anwendung wurde über Git Pull updated', +'Apply changes': 'Apply changes', 'are not used': 'werden nicht verwendet', 'are not used yet': 'werden bisher nicht verwendet', -'Are you sure you want to delete file "%s"?': 'Sind Sie sich sicher, dass Sie diese Datei löschen wollen "%s"?', -'Are you sure you want to delete plugin "%s"?': 'Are you sure you want to delete plugin "%s"?', +'Are you sure you want to delete file "%s"?': 'Sind Sie sich sicher, dass Sie die Datei "%s" löschen wollen?', +'Are you sure you want to delete plugin "%s"?': 'Möchten Sie das Plugin "%s" wirklich löschen?', 'Are you sure you want to delete this object?': 'Sind Sie sich sicher, dass Sie dieses Objekt löschen wollen?', -'Are you sure you want to uninstall application "%s"': 'Sind Sie sich sicher, dass Sie diese Anwendung deinstallieren wollen "%s"', -'Are you sure you want to uninstall application "%s"?': 'Sind Sie sich sicher, dass Sie diese Anwendung deinstallieren wollen "%s"?', +'Are you sure you want to uninstall application "%s"': 'Sind Sie sich sicher, dass Sie die Anwendung "%s" deinstallieren wollen', +'Are you sure you want to uninstall application "%s"?': 'Sind Sie sich sicher, dass Sie die Anwendung "%s" deinstallieren wollen?', 'Are you sure you want to upgrade web2py now?': 'Sind Sie sich sicher, dass Sie web2py jetzt upgraden möchten?', -'Are you sure?': 'Are you sure?', -'arguments': 'arguments', +'Are you sure?': 'Bist du sicher?', +'arguments': 'Argumente', 'at char %s': 'bei Zeichen %s', 'at line %s': 'in Linie %s', -'ATTENTION:': 'ATTENTION:', +'ATTENTION:': 'BEACHTUNG:', 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ACHTUNG: Die Einwahl benötigt eine sichere (HTTPS) Verbindung. Es sei denn sie läuft Lokal (localhost).', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ACHTUNG: Testen ist nicht threadsicher. Führen Sie also nicht mehrere Tests gleichzeitig aus.', 'ATTENTION: This is an experimental feature and it needs more testing.': 'ACHTUNG: Dies ist eine experimentelle Funktion und benötigt noch weitere Tests.', 'ATTENTION: you cannot edit the running application!': 'ACHTUNG: Eine laufende Anwendung kann nicht editiert werden!', 'Authentication': 'Authentifizierung', -'Autocomplete Python Code': 'Autocomplete Python Code', +'Authentication code': 'Authentication code', +'Autocomplete Python Code': 'Python-Code automatisch vervollständigen', 'Available databases and tables': 'Verfügbare Datenbanken und Tabellen', -'Available Databases and Tables': 'Available Databases and Tables', +'Available Databases and Tables': 'Verfügbare Datenbanken und Tabellen', 'back': 'Zurück', -'Back to the plugins list': 'Back to the plugins list', -'Back to wizard': 'Back to wizard', +'Back to the plugins list': 'Zurück zur Plugin-Liste', +'Back to wizard': 'Zurück zum Wizard', 'Basics': 'Basics', 'beautify': 'Verschönern', -'Begin': 'Begin', +'Begin': 'Start', 'breakpoint': 'breakpoint', 'Breakpoints': 'Breakpoints', 'breakpoints': 'breakpoints', -'Bulk Register': 'Bulk Register', -'Bulk Student Registration': 'Bulk Student Registration', -'Cache': 'Cache', +'Bulk Register': 'Masse-registrierung', +'Bulk Student Registration': 'Massen-Studenten registrierung', +'Cache': 'Zwischenspeicher', 'cache': 'Zwischenspeicher', -'Cache Cleared': 'Cache Cleared', +'Cache Cleared': 'Cache geleert', 'Cache Keys': 'Cache Keys', 'cache, errors and sessions cleaned': 'Zwischenspeicher (cache), Fehler und Sitzungen (sessions) gelöscht', 'call': 'Aufruf', @@ -95,44 +111,46 @@ 'Cancel': 'Abbrechen', 'Cannot be empty': 'Darf nicht leer sein', 'Cannot compile: there are errors in your app. Debug it, correct errors and try again.': 'Nicht Kompilierbar: Es sind Fehler in der Anwendung. Beseitigen Sie die Fehler und versuchen Sie es erneut.', -'Cannot compile: there are errors in your app:': 'Cannot compile: there are errors in your app:', +'Cannot compile: there are errors in your app:': 'Kompilieren nicht möglich: Es gibt Fehler in Ihrer App:', 'cannot create file': 'Kann Datei nicht erstellen', 'cannot upload file "%(filename)s"': 'Kann Datei nicht Hochladen "%(filename)s"', -'Change Admin Password': 'Change Admin Password', +'Change Admin Password': 'Admin-Passwort ändern', 'Change admin password': 'Administrator-Passwort ändern', 'change editor settings': 'Editoreinstellungen ändern', 'Change Password': 'Passwort ändern', 'change password': 'Passwort ändern', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'alles auswählen', 'Check for upgrades': 'Versionsüberprüfung', 'Check to delete': 'Markiere zum löschen', 'Checking for upgrades...': 'Überprüfe auf Updates...', 'Clean': 'Leeren', -'Clear': 'Clear', -'Clear CACHE?': 'Clear CACHE?', -'Clear DISK': 'Clear DISK', -'Clear RAM': 'Clear RAM', +'Clear': 'Leeren', +'Clear CACHE?': 'CACHE leeren?', +'Clear DISK': 'DISK leeren', +'Clear RAM': 'RAM leeren', 'click here for online examples': 'hier klicken für online Beispiele', 'click here for the administrative interface': 'hier klicken für die Administrationsoberfläche ', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Klicke auf die Zeile für Fehlerverfolgung', -'Click row to view a ticket': 'Click row to view a ticket', +'Click row to view a ticket': 'Klicke auf eine Zeile, um ein Ticket anzusehen', 'click to check for upgrades': 'hier klicken um nach Upgrades zu suchen', 'Client IP': 'Client IP', -'code': 'code', -'Code listing': 'Code listing', +'code': 'Code', +'Code listing': 'Codeauflistung', 'collapse/expand all': 'alles zu- bzw. aufklappen', -'Command': 'Command', -'Comment:': 'Comment:', +'Command': 'Befehl', +'Comment:': 'Kommentar:', 'Commit': 'Commit', -'Commit form': 'Commit form', -'Committed files': 'Committed files', +'Commit form': 'Formular ausfüllen', +'Committed files': 'Festgelegte Dateien', 'Compile': 'kompilieren', -'Compile (all or nothing)': 'Compile (all or nothing)', -'Compile (skip failed views)': 'Compile (skip failed views)', +'Compile (all or nothing)': 'Kompilieren (alles oder nichts)', +'Compile (skip failed views)': 'Kompilieren (Fehlgeschlagene Ansichten überspringen)', 'compiled application removed': 'kompilierte Anwendung gelöscht', -'Condition': 'Condition', -'continue': 'continue', +'Condition': 'Bedingung', +'continue': 'fortsetzen', 'Controller': 'Controller', 'Controllers': 'Controllers', 'controllers': 'controllers', @@ -142,11 +160,11 @@ 'create file with filename:': 'Erzeuge Datei mit Dateinamen:', 'create new application:': 'Erzeuge neue Anwendung:', 'Create new simple application': 'Erzeuge neue Anwendung', -'Create/Upload': 'Create/Upload', +'Create/Upload': 'Erstellen/Uploaden', 'created by': 'Erstellt von', -'Created by:': 'Created by:', -'Created On': 'Created On', -'Created on:': 'Created on:', +'Created by:': 'Erstellt von:', +'Created On': 'Erstellt am', +'Created on:': 'Erstellt am:', 'crontab': 'crontab', 'Current request': 'Aktuelle Anfrage (request)', 'Current response': 'Aktuelle Antwort (response)', @@ -157,7 +175,7 @@ 'data uploaded': 'Daten hochgeladen', 'Database': 'Datenbank', 'database': 'Datenbank', -'Database %s select': 'Database %s select', +'Database %s select': 'Database %s ausgewählt', 'database %s select': 'Datenbank %s ausgewählt', 'Database administration': 'Database administration', 'database administration': 'Datenbankadministration', @@ -173,49 +191,49 @@ 'delete plugin': 'Plugin löschen', 'Delete this file (you will be asked to confirm deletion)': 'Diese Datei löschen (mit Bestätigungsdialog)', 'Delete:': 'Löschen:', -'deleted after first hit': 'deleted after first hit', +'deleted after first hit': 'nach dem ersten Treffer gelöscht', 'Demo': 'Demo', 'Deploy': 'Installieren', 'Deploy on Google App Engine': 'Auf Google App Engine installieren', 'Deploy to OpenShift': 'Auf OpenShift installieren', -'Deploy to pythonanywhere': 'Deploy to pythonanywhere', -'Deploy to PythonAnywhere': 'Deploy to PythonAnywhere', +'Deploy to pythonanywhere': 'Bereitstellen für pythonanywhere', +'Deploy to PythonAnywhere': 'Bereitstellen für PythonAnywhere', 'Deployment form': 'Deployment form', 'Deployment Interface': 'Deployment Interface', 'Description': 'Beschreibung', -'Description:': 'Description:', +'Description:': 'Beschreibung:', 'design': 'design', 'DESIGN': 'DESIGN', 'Design for': 'Design für', 'Detailed traceback description': 'Detaillierte traceback Beschreibung', -'details': 'details', -'direction: ltr': 'direction: ltr', -'directory not found': 'directory not found', +'details': 'Details', +'direction: ltr': 'Richtung: ltr', +'directory not found': 'Verzeichnis nicht gefunden', 'Disable': 'Deaktivieren', -'Disabled': 'Disabled', -'disabled in demo mode': 'disabled in demo mode', -'disabled in GAE mode': 'disabled in GAE mode', -'disabled in multi user mode': 'disabled in multi user mode', +'Disabled': 'Deaktiviert', +'disabled in demo mode': 'Im Demo Modus deaktiviert', +'disabled in GAE mode': 'Im GAE Modus deaktiviert', +'disabled in multi user mode': 'Im Multi-User-Modus deaktiviert', 'DISK': 'DISK', 'Disk Cache Keys': 'Disk Cache Keys', -'Disk Cleared': 'Disk Cleared', -'Display line numbers': 'Display line numbers', -'DO NOT use the "Pack compiled" feature.': 'DO NOT use the "Pack compiled" feature.', +'Disk Cleared': 'Disk geleert', +'Display line numbers': 'Zeilennummern anzeigen', +'DO NOT use the "Pack compiled" feature.': 'Verwenden Sie NICHT die Funktion "Pack kompiliert".', 'docs': 'docs', 'Docs': 'Docs', 'documentation': 'Dokumentation', 'done!': 'fertig!', 'Downgrade': 'Downgrade', 'Download .w2p': 'Download .w2p', -'Download as .exe': 'Download as .exe', +'Download as .exe': 'Downloade als .exe', 'download layouts': 'Layouts herunterladen', -'Download layouts from repository': 'Download layouts from repository', +'Download layouts from repository': 'Layouts aus dem Repository herunterladen', 'download plugins': 'Plugins herunterladen', -'Download plugins from repository': 'Download plugins from repository', +'Download plugins from repository': 'Plugins aus dem Repository herunterladen', 'E-mail': 'E-mail', 'EDIT': 'BEARBEITEN', 'Edit': 'Bearbeiten', -'edit all': 'edit all', +'edit all': 'Alles bearbeiten', 'Edit application': 'Bearbeite Anwendung', 'edit controller': 'Bearbeite Controller', 'edit controller:': 'bearbeite Controller:', @@ -228,12 +246,15 @@ 'Editing file': 'Bearbeite Datei', 'Editing file "%s"': 'Bearbeite Datei "%s"', 'Editing Language file': 'Sprachdatei bearbeiten', -'Editing Plural Forms File': 'Editing Plural Forms File', +'Editing Plural Forms File': 'Bearbeiten von Plural formen Datei', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Aktivieren', -'Enable Close-Tag': 'Enable Close-Tag', -'Enable Code Folding': 'Enable Code Folding', +'Enable Close-Tag': 'Aktiviere Close-Tag', +'Enable Code Folding': 'Aktiviere Code Folding', 'Enterprise Web Framework': 'Enterprise Web Framework', 'Error': 'Fehler', 'Error logs for "%(app)s"': 'Fehlerprotokoll für "%(app)s"', @@ -241,8 +262,8 @@ 'Error ticket': 'Error ticket', 'Errors': 'Fehlermeldungen', 'escape': 'escape', -'Exception %(extype)s: %(exvalue)s': 'Exception %(extype)s: %(exvalue)s', -'Exception %s': 'Exception %s', +'Exception %(extype)s: %(exvalue)s': 'Ausnahme %(extype)s: %(exvalue)s', +'Exception %s': 'Ausnahme %s', 'Exception instance attributes': 'Attribute der Ausnahmeinstanz', 'Exit Fullscreen': 'Vollbild beenden', 'Expand Abbreviation': 'Kürzel erweitern', @@ -254,7 +275,7 @@ 'extends': 'erweitert', 'failed to compile file because:': 'Datei konnte nicht kompiliert werden, da:', 'failed to reload module': 'neu laden des Moduls fehlgeschlagen', -'failed to reload module because:': 'failed to reload module because:', +'failed to reload module because:': 'Modul konnte nicht geladen werden, weil:', 'File': 'Datei', 'file "%(filename)s" created': 'Datei "%(filename)s" erstellt', 'file "%(filename)s" deleted': 'Datei "%(filename)s" gelöscht', @@ -263,166 +284,187 @@ 'file "%s" of %s restored': 'Datei "%s" von %s wiederhergestellt', 'file changed on disk': 'Datei auf Festplatte geändert', 'file does not exist': 'Datei existiert nicht', -'file not found': 'file not found', +'file not found': 'Datei nicht gefunden', 'file saved on %(time)s': 'Datei gespeichert am %(time)s', 'file saved on %s': 'Datei gespeichert auf %s', -'filename': 'filename', -'Filename': 'Filename', -'Files added': 'Files added', +'filename': 'dateiname', +'Filename': 'Dateiname', +'Files added': 'Dateien hinzugefügt', 'filter': 'Filter', 'Find Next': 'Nächster', 'Find Previous': 'Vorheriger', 'First name': 'Vorname', -'Form has errors': 'Form has errors', +'Form has errors': 'Das Formular hat Fehler', 'Frames': 'Frames', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Funktionen ohne doctests erzeugen [passed] in Tests', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', -'GAE Password': 'GAE Password', -'Generate': 'Generate', -'Get from URL:': 'Get from URL:', +'GAE Password': 'GAE Passwort', +'Generate': 'Generieren', +'Get from URL:': 'Von URL abrufen:', 'Git Pull': 'Git Pull', 'Git Push': 'Git Push', -'Globals##debug': 'Globals##debug', +'Globals##debug': 'Globales##debug', 'Go to Matching Pair': 'Gehe zum übereinstimmenden Paar', 'go!': 'go!', 'Google App Engine Deployment Interface': 'Google App Engine Deployment Interface', -'Google Application Id': 'Google Application Id', -'Goto': 'Goto', +'Google Application Id': 'Google-Anwendungs-ID', +'Goto': 'Gehe zu', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'Gruppen ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Hello World': 'Hallo Welt', 'Help': 'Hilfe', -'here': 'here', +'here': 'Hier', 'Hide/Show Translated strings': 'Zeige/Verstecke übersetzte Strings', -'Highlight current line': 'Highlight current line', -'Hits': 'Hits', +'Highlight current line': 'Aktuelle Zeile markieren', +'Hits': 'Treffer', 'Home': 'Startseite', -'honored only if the expression evaluates to true': 'honored only if the expression evaluates to true', +'honored only if the expression evaluates to true': 'Wird nur berücksichtigt, wenn der Ausdruck als true ausgewertet wird', 'htmledit': 'htmledit', -'If start the downgrade, be patient, it may take a while to rollback': 'If start the downgrade, be patient, it may take a while to rollback', -'If start the upgrade, be patient, it may take a while to download': 'If start the upgrade, be patient, it may take a while to download', -'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\n\t\tA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\n\t\tA green title indicates that all tests (if defined) passed. In this case test results are not shown.', +'If start the downgrade, be patient, it may take a while to rollback': 'Wenn Sie das Downgrade starten, haben Sie etwas Geduld. Das Rollback kann einige Zeit dauern', +'If start the upgrade, be patient, it may take a while to download': 'Wenn Sie das Upgrade starten, haben Sie etwas Geduld. Das Herunterladen kann einige Zeit dauern', +'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\n\t\tA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\r\n\t\tA green title indicates that all tests (if defined) passed. In this case test results are not shown.', 'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'Falls der obere Test eine Fehler-Ticketnummer enthält deutet das auf einen Fehler in der Ausführung des Controllers hin, noch bevor der Doctest ausgeführt werden konnte. Gewöhnlich Führen fehlerhafte Einrückungen oder fehlerhafter Code ausserhalb der Funktion zu solchen Fehlern. Ein grüner Titel deutet darauf hin, dass alle Test(wenn sie vorhanden sind) erfolgreich durchlaufen wurden. In diesem Fall werden die Testresultate nicht angezeigt.', 'If you answer "yes", be patient, it may take a while to download': 'Wenn Sie mit "Ja" antworten, seien Sie bitte geduldig. Der Download könnte eine Weile dauern.', 'If you answer yes, be patient, it may take a while to download': 'Wenn Sie mit Ja antworten, seien Sie bitte geduldig. Der Download könnte eine Weile dauern.', -'if your application uses a database other than sqlite you will then have to configure its DAL in pythonanywhere.': 'if your application uses a database other than sqlite you will then have to configure its DAL in pythonanywhere.', -'import': 'import', +'if your application uses a database other than sqlite you will then have to configure its DAL in pythonanywhere.': 'Wenn Ihre Anwendung eine andere Datenbank als sqlite verwendet, müssen Sie ihre DAL in pythonanywhere konfigurieren.', +'import': 'Import', 'Import/Export': 'Importieren/Exportieren', -'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', +'In development, use the default Rocket webserver that is currently supported by this debugger.': 'Verwenden Sie in der Entwicklung den Standard-Rocket-Webserver, der derzeit von diesem Debugger unterstützt wird.', 'includes': 'Einfügen', -'Indent with tabs': 'Indent with tabs', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', +'Indent with tabs': 'Einrücken mit Tabulatoren', 'Index': 'Index', 'index': 'index', 'insert new': 'neu Einfügen', 'insert new %s': 'neu Einfügen %s', 'inspect attributes': 'Attribute inspizieren', 'Install': 'Installieren', -'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', +'Installation of %(plugin)s for %(app)s': 'Installation von %(Plugin)s für %(App)s', 'Installed applications': 'Installierte Anwendungen', -'Interaction at %s line %s': 'Interaction at %s line %s', -'Interactive console': 'Interactive console', +'Insufficient privileges': 'Insufficient privileges', +'Interaction at %s line %s': 'Interaktion bei %s Zeile %s', +'Interactive console': 'Interaktive Konsole', 'internal error': 'Interner Fehler', -'internal error: %s': 'internal error: %s', +'internal error: %s': 'interner Error: %s', 'Internal State': 'Interner Status', 'Invalid action': 'Ungültige Aktion', -'Invalid application name': 'Invalid application name', -'invalid circular reference': 'invalid circular reference', +'Invalid application name': 'Ungültiger Anwendungsname', +'invalid circular reference': 'ungültiger Zirkelverweis', 'Invalid email': 'Ungültige Email', -'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid git repository specified.': 'Ungültiges git-Repository angegeben.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'Ungültiges Passwort', -'invalid password.': 'invalid password.', +'Invalid password': 'Invalid password', +'invalid password.': 'Ungültiges Passwort.', 'Invalid Query': 'Ungültige Abfrage', 'invalid request': 'Ungültige Anfrage', -'Invalid request': 'Invalid request', -'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', +'Invalid request': 'Ungültige Anfrage', +'Invalid reset password': 'Invalid reset password', +'invalid table names (auth_* tables already defined)': 'Ungültige Tabellennamen (auth_* Tabellen bereits definiert)', 'invalid ticket': 'Ungültiges Ticket', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', 'Key bindings': 'Tastenbelegungen', -'Key bindings for ZenCoding Plugin': 'Key bindings for ZenCoding Plugin', +'Key bindings for ZenCoding Plugin': 'Tastenbelegungen für das ZenCoding Plugin', 'Key bindings for ZenConding Plugin': 'Tastenbelegungen für das ZenConding Plugin', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Tastenkombination', -'kill process': 'kill process', +'kill process': 'Prozess zu töten', 'language file "%(filename)s" created/updated': 'Sprachdatei "%(filename)s" erstellt/aktualisiert', 'Language files (static strings) updated': 'Sprachdatei (statisch Strings) aktualisiert', 'languages': 'Sprachen', 'Languages': 'Sprachen', 'languages updated': 'Sprachen aktualisiert', 'Last name': 'Nachname', -'Last Revision': 'Last Revision', +'Last Revision': 'Letzete Revision', 'Last saved on:': 'Zuletzt gespeichert am:', 'Layout': 'Layout', 'License for': 'Lizenz für', -'License:': 'License:', -'Line Nr': 'Line Nr', -'Line number': 'Line number', -'lists by exception': 'lists by exception', +'License:': 'Lizenz:', +'Line Nr': 'Zeilen Nr', +'Line number': 'Zeilennummer', +'lists by exception': 'Listen nach Ausnahme', 'lists by ticket': 'nach Ticket aufgelistet', -'Loading...': 'Loading...', +'Loading...': 'Laden...', 'loading...': 'lade...', -'Local Apps': 'Local Apps', -'locals': 'locals', -'Locals##debug': 'Locals##debug', +'Local Apps': 'Lokale Apps', +'locals': 'lokales', +'Locals##debug': 'Lokales##debug', 'located in the file': 'befindet sich in der Datei', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'login': 'anmelden', 'Login': 'Anmelden', -'Login successful': 'Login successful', +'Login disabled by administrator': 'Login disabled by administrator', +'Login successful': 'Login erfolgreich', 'Login to the Administrative Interface': 'An das Administrations-Interface anmelden', -'Login/Register': 'Login/Register', +'Login/Register': 'Login/Registrieren', 'Logout': 'Abmelden', -'lost password': 'lost password', +'lost password': 'Passwort vergessen', 'Lost Password': 'Passwort vergessen', 'lost password?': 'Passwort vergessen?', 'Main Menu': 'Hauptmenü', 'Manage': 'Verwalten', -'Manage %(action)s': 'Manage %(action)s', -'Manage Access Control': 'Manage Access Control', -'Manage Admin Users/Students': 'Manage Admin Users/Students', -'Manage Cache': 'Manage Cache', -'Manage Students': 'Manage Students', +'Manage %(action)s': '%(action)s verwalten', +'Manage Access Control': 'Zugriffskontrolle verwalten', +'Manage Admin Users/Students': 'Admin-User/Studenten verwalten', +'Manage Cache': 'Cache verwalten', +'Manage Students': 'Studenten verwalten', 'Match Pair': 'Paare finden', -'Memberships': 'Memberships', +'Memberships': 'Mitgliedschaften', 'Menu Model': 'Menü Modell', 'merge': 'verbinden', 'Merge Lines': 'Zeilen zusammenfügen', 'Models': 'Modelle', 'models': 'Modelle', -'Modified On': 'Modified On', +'Modified On': 'Geändert am', 'Modules': 'Module', 'modules': 'Module', -'Multi User Mode': 'Multi User Mode', +'Multi User Mode': 'Multi-User-Modus', 'Name': 'Name', 'new application "%s" created': 'neue Anwendung "%s" erzeugt', -'new application "%s" imported': 'new application "%s" imported', -'New Application Wizard': 'New Application Wizard', +'new application "%s" imported': 'Neue Anwendung "%s" importiert', +'New Application Wizard': 'Neuer Anwendungs Assistent', 'New application wizard': 'Neue Anwendung per Assistent', +'New password': 'New password', 'new plugin installed': 'Neues Plugin wurde installiert', -'New plugin installed: %s': 'New plugin installed: %s', +'New plugin installed: %s': 'Neues Plugin installiert: %s', 'New Record': 'Neuer Datensatz', 'new record inserted': 'Neuer wurde Datensatz eingefügt', 'New simple application': 'Neue einfache Anwendung', -'next': 'next', -'next %s rows': 'next %s rows', +'next': 'Nächste', +'next %s rows': 'nächste %s Zeilen', 'next 100 rows': 'nächsten 100 Zeilen', 'Next Edit Point': 'nächster Bearbeitungsschritt', 'NO': 'NEIN', -'no changes': 'no changes', +'no changes': 'keine Änderungen', 'No databases in this application': 'Keine Datenbank in dieser Anwendung', -'No Interaction yet': 'No Interaction yet', -'no match': 'no match', +'No Interaction yet': 'Noch keine Interaktion', +'no match': 'keine Übereinstimmung', 'no package selected': 'Kein Paket ausgewählt', -'no permission to uninstall "%s"': 'no permission to uninstall "%s"', +'no permission to uninstall "%s"': 'Keine Berechtigung zum Deinstallieren von "%s"', 'No ticket_storage.txt found under /private folder': 'Kein ticket_storage.txt unter /private folder gefunden', -'Node:': 'Node:', -'Not Authorized': 'Not Authorized', -'Not supported': 'Not supported', -'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', -"On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", +'Node:': 'Hinweis:', +'Not Authorized': 'Nicht berechtigt', +'Not supported': 'Nicht unterstützt', +'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Hinweis: Wenn Sie einen Fehler mit dem Github-Statuscode 128 erhalten, stellen Sie sicher, dass das System und der Account, von dem Sie das Deployment durchführen, über einen entsprechenden ssh-Schlüssel im Account "openshift" verfügen.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', +"On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": 'In der Produktion müssen Sie Ihren Webserver so konfigurieren, dass er einen Prozess und mehrere Threads verwendet, um diesen Debugger zu verwenden.', 'online designer': 'Online Designer', -'Open new app in new window': 'Open new app in new window', +'Open new app in new window': 'Neue App in neuem Fenster öffnen', 'OpenShift Deployment Interface': 'OpenShift Deployment Interface', -'OpenShift Output': 'OpenShift Output', +'OpenShift Output': 'OpenShift Ausgabe', 'or alternatively': 'oder Alternativ', 'Or Get from URL:': 'oder von folgender URL herunterladen:', 'or import from csv file': 'oder importieren von cvs Datei', @@ -430,144 +472,164 @@ 'or provide application url:': 'oder geben Sie eine Anwendungs-URL an:', 'Origin': 'Herkunft', 'Original/Translation': 'Original/übersetzung', -'Overview': 'Overview', +'Overview': 'Übersicht', 'Overwrite installed app': 'Installierte Anwendungen überschreiben', 'Pack all': 'Verpacke alles', 'Pack compiled': 'Verpacke kompiliert', 'Pack custom': 'Verpacke individuell', 'pack plugin': 'Plugin verpacken', 'Password': 'Passwort', -'password changed': 'password changed', -'Past revisions': 'Past revisions', -'Path to appcfg.py': 'Path to appcfg.py', -'Path to local openshift repo root.': 'Path to local openshift repo root.', +'password changed': 'passwort geändert', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', +'Past revisions': 'Vergangene Revisionen', +'Path to appcfg.py': 'Pfad zu appcfg.py', +'Path to local openshift repo root.': 'Pfad zum lokalen OpenShift-Repo-root.', 'Peeking at file': 'Dateiansicht', -'Permission': 'Permission', -'Permissions': 'Permissions', -'Please': 'Please', +'Permission': 'Berechtigung', +'Permissions': 'Berechtigungen', +'Please': 'Bitte', +'please input your password again': 'please input your password again', 'please wait!': 'Bitte warten!', -'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', -'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" deleted', +'Please wait, giving pythonanywhere a moment...': 'Bitte warten Sie, und geben Sie pythonanywhere einen Moment Zeit...', +'plugin "%(plugin)s" deleted': 'Plugin "%(plugin) s" wurde gelöscht', 'Plugin "%s" in application': 'Plugin "%s" in Anwendung', -'plugin not specified': 'plugin not specified', -'Plugin page': 'Plugin page', +'plugin not specified': 'Plugin nicht angegeben', +'Plugin page': 'Plugin seite', 'plugins': 'plugins', 'Plugins': 'Plugins', -'Plural Form #%s': 'Plural Form #%s', -'Plural-Forms:': 'Plural-Forms:', +'Plural Form #%s': 'Plural Formen #%s', +'Plural-Forms:': 'Plural Formen:', 'Powered by': 'Unterstützt von', -'Preferences saved correctly': 'Preferences saved correctly', -'Preferences saved on session only': 'Preferences saved on session only', -'previous %s rows': 'previous %s rows', +'Preferences saved correctly': 'Einstellungen korrekt gespeichert', +'Preferences saved on session only': 'Einstellungen nur in der Sitzung gespeichert', +'previous %s rows': 'vorherige %s Zeilen', 'previous 100 rows': 'vorherige 100 zeilen', 'Previous Edit Point': 'vorheriger Bearbeitungsschritt', -'Private files': 'Private files', -'private files': 'private files', +'Private files': 'Private Dateien', +'private files': 'private Dateien', +'Profile updated': 'Profile updated', 'Project Progress': 'Projekt Fortschritt', 'Pull': 'Pull', -'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', -'Pull is not possible because you have unmerged files. Fix them up in the work tree, and then try again.': 'Pull is not possible because you have unmerged files. Fix them up in the work tree, and then try again.', +'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull fehlgeschlagen, bestimmte Dateien konnten nicht ausgecheckt werden. Überprüfen Sie die Protokolle für Details.', +'Pull is not possible because you have unmerged files. Fix them up in the work tree, and then try again.': 'Pull ist nicht möglich, weil Sie nicht gespeicherte Dateien haben. Reparieren Sie sie im Arbeitsbaum und versuchen Sie es erneut.', 'Push': 'Push', -'Push failed, there are unmerged entries in the cache. Resolve merge issues manually and try again.': 'Push failed, there are unmerged entries in the cache. Resolve merge issues manually and try again.', -'pygraphviz library not found': 'pygraphviz library not found', +'Push failed, there are unmerged entries in the cache. Resolve merge issues manually and try again.': 'Push fehlgeschlagen, es sind nicht fusionierte Einträge im Cache. Beheben Sie fusionierungsprobleme manuell und versuchen Sie es erneut.', +'pygraphviz library not found': 'pygraphviz Bibliothek nicht gefunden', 'PythonAnywhere Apps': 'PythonAnywhere Apps', -'PythonAnywhere Password': 'PythonAnywhere Password', +'PythonAnywhere Password': 'PythonAnywhere Passwort', 'Query:': 'Abfrage:', 'RAM': 'RAM', 'RAM Cache Keys': 'RAM Cache Keys', -'Ram Cleared': 'Ram Cleared', +'Ram Cleared': 'Ram geleert', 'Rapid Search': 'Schnelle Suche', 'Record': 'Record', 'record': 'Datensatz', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'Datensatz existiert nicht', 'record id': 'Datensatz id', 'Record ID': 'Datensatz ID', 'Record id': 'Record id', -'refresh': 'refresh', +'Record Updated': 'Record Updated', +'refresh': 'Aktualisieren', 'Register': 'Registrieren', 'register': 'Registrierung', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Registrierungsschlüssel', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'reload': 'Neu laden', 'Reload routes': 'Routen neu laden', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'Bytecode löschen', -'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', +'Removed Breakpoint on %s at line %s': 'Breakpoint entfernt an %s auf Zeile %s', 'Replace': 'Ersetzen', 'Replace All': 'Alle Ersetzen', -'Repository (%s)': 'Repository (%s)', +'Repository (%s)': 'Speiche-Archiv (%s)', 'request': 'Anfrage', -'requires distutils, but not installed': 'requires distutils, but not installed', -'requires python-git, but not installed': 'requires python-git, but not installed', +'Request reset password': 'Request reset password', +'requires distutils, but not installed': 'erfordert distutils, aber nicht installiert', +'requires python-git, but not installed': 'rfordert Python-Git, aber nicht installiert', 'Reset Password key': 'Passwortschlüssel zurücksetzen', 'Resolve Conflict file': 'bereinige Konflikt-Datei', 'response': 'Antwort', -'restart': 'restart', +'restart': 'Neustart', 'restore': 'wiederherstellen', -'return': 'return', -'Revert': 'Revert', +'return': 'Zurück', +'Revert': 'Zurückkehren', 'revert': 'zurückkehren', -'reverted to revision %s': 'reverted to revision %s', +'reverted to revision %s': 'Zurück zur revision %s', 'Revision %s': 'Revision %s', 'Revision:': 'Revision:', 'Role': 'Rolle', -'Roles': 'Roles', +'Roles': 'Rollen', 'Rows in table': 'Zeilen in Tabelle', -'Rows in Table': 'Rows in Table', +'Rows in Table': 'Zeilen in der Tabelle', 'Rows selected': 'Zeilen ausgewählt', 'rules are not defined': 'Regeln sind nicht definiert', -'Run tests': 'Run tests', -'Run tests in this file': 'Run tests in this file', +'Run tests': 'Tests ausführen', +'Run tests in this file': 'Führen Sie Tests in dieser Datei aus', "Run tests in this file (to run all files, you may also use the button labelled 'test')": "Tests in dieser Datei ausführen (um alle Dateien auszuführen, kann auch der Button 'test' genutzt werden)", 'Running on %s': 'läuft auf %s', 'Save': 'Speichern', 'save': 'Speichern', 'Save file:': 'Speichere Datei:', 'Save file: %s': 'Speichere Datei: %s', -'Save model as...': 'Save model as...', +'Save model as...': 'Modell speichern als...', 'Save via Ajax': 'via Ajax speichern', 'Saved file hash:': 'Gespeicherter Datei-Hash:', -'Screenshot %s': 'Screenshot %s', -'Search': 'Search', +'Screenshot %s': 'Bildschirmfoto %s', +'Search': 'Suche', 'Select Files to Package': 'Dateien zum Paketieren wählen', 'selected': 'ausgewählt(e)', 'session': 'Sitzung', 'session expired': 'Sitzung abgelaufen', -'Session saved correctly': 'Session saved correctly', -'Session saved on session only': 'Session saved on session only', -'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', +'Session saved correctly': 'Sitzung korrekt gespeichert', +'Session saved on session only': 'Sitzung gespeichert auf Nur Sitzung', +'Set Breakpoint on %s at line %s: %s': 'Setze Breakpoint auf %s an Linie %s: %s', 'shell': 'Shell', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', -'Singular Form': 'Singular Form', +'Sign Up': 'Sign Up', +'Singular Form': 'Einzahl', 'Site': 'Seite', -'Size of cache:': 'Size of cache:', -'skip to generate': 'skip to generate', +'Size of cache:': 'Größe des Caches:', +'skip to generate': 'überspringen, um zu generieren', 'some files could not be removed': 'einige Dateien konnten nicht gelöscht werden', -'Something went wrong please wait a few minutes before retrying': 'Something went wrong please wait a few minutes before retrying', -'Sorry, could not find mercurial installed': 'Sorry, could not find mercurial installed', -'source : db': 'source : db', +'Something went wrong please wait a few minutes before retrying': 'Es ist ein Fehler aufgetreten. Bitte warten Sie einige Minuten, bevor Sie es erneut versuchen', +'Sorry, could not find mercurial installed': 'Leider konnte mercurial nicht installiert werden', +'source : db': 'Quelle : db', 'source : filesystem': 'Quelle : Dateisystem', -'Start a new app': 'Start a new app', +'Start a new app': 'Starten Sie eine neue App', 'Start searching': 'Suche beginnen', 'Start wizard': 'Assistent starten', 'state': 'Status', 'Static': 'Statisch', 'static': 'statische Dateien', 'Static files': 'statische Dateien', -'Statistics': 'Statistics', -'Step': 'Step', -'step': 'step', -'stop': 'stop', +'Statistics': 'Statistiken', +'Step': 'Schritt', +'step': 'schritt', +'stop': 'Halt', 'Stylesheet': 'Stylesheet', 'submit': 'Absenden', -'Submit': 'Submit', -'successful': 'successful', +'Submit': 'Einreichen', +'successful': 'erfolgreich', 'Sure you want to delete this object?': 'Wollen Sie das Objekt wirklich löschen?', 'switch to : db': 'wechsel zu : db', -'switch to : filesystem': 'switch to : filesystem', -'Tab width (# characters)': 'Tab width (# characters)', +'switch to : filesystem': 'wechseln zu : filesystem', +'Tab width (# characters)': 'Tabulatorbreite (# characters)', 'table': 'Tabelle', -'Table': 'Table', +'Table': 'Tabelle', 'Table name': 'Tabellen Name', -'Temporary': 'Temporary', +'Temporary': 'Temporär', 'test': 'Test', 'test_def': 'test_def', 'test_for': 'test_for', @@ -576,8 +638,8 @@ 'Testing application': 'Teste die Anwendung', 'Testing controller': 'Teste Controller', 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'Die "query" ist eine Bedingung wie "db.table1.field1 == \'Wert\'". Etwas wie "db.table1.field1 db.table2.field2 ==" führt zu einem SQL JOIN.', -'The app exists, was created by wizard, continue to overwrite!': 'The app exists, was created by wizard, continue to overwrite!', -'The app exists, was NOT created by wizard, continue to overwrite!': 'The app exists, was NOT created by wizard, continue to overwrite!', +'The app exists, was created by wizard, continue to overwrite!': 'Die App existiert, wurde von Wizard erstellt, weiter überschreiben!', +'The app exists, was NOT created by wizard, continue to overwrite!': 'Die App existiert, wurde NICHT vom Assistenten erstellt, weiter überschreiben!', 'the application logic, each URL path is mapped in one exposed function in the controller': 'Die Logik der Anwendung, jeder URL-Pfad wird auf eine Funktion abgebildet die der Controller zur Verfügung stellt', 'The application logic, each URL path is mapped in one exposed function in the controller': 'Die Logik der Anwendung, jeder URL-Pfad wird auf eine Funktion abgebildet die der Controller zur Verfügung stellt', 'the data representation, define database tables and sets': 'Die Datenrepräsentation definiert Mengen von Tabellen und Datenbanken', @@ -585,39 +647,41 @@ 'The output of the file is a dictionary that was rendered by the view': 'Die Ausgabe der Datei ist ein "dictionary" und wurde vom "view" gerendert', 'The presentations layer, views are also known as templates': 'Die Präsentationsschicht, Views sind auch bekannt als Vorlagen/Templates', 'the presentations layer, views are also known as templates': 'Die Präsentationsschicht, Views sind auch bekannt als Vorlagen/Templates', -'Theme': 'Theme', +'Theme': 'Thema', 'There are no controllers': 'Keine Controller vorhanden', 'There are no models': 'Keine Modelle vorhanden', 'There are no modules': 'Keine Module vorhanden', 'There are no plugins': 'Keine Plugins vorhanden', 'There are no private files': 'Keine privaten Dateien vorhanden', 'There are no static files': 'Keine statischen Dateien vorhanden', -'There are no translators': 'There are no translators', +'There are no translators': 'Es gibt keine Übersetzer', 'There are no translators, only default language is supported': 'Keine übersetzungen vorhanden, nur die voreingestellte Sprache wird Unterstützt', 'There are no views': 'Keine Views vorhanden', 'These files are not served, they are only available from within your app': 'Diese Dateien werden nicht ausgeliefert, sie sind nur innerhalb Ihrer App verfügbar', 'These files are served without processing, your images go here': 'Diese Dateien werden ohne Verarbeitung ausgeliefert. Beispielsweise Bilder kommen hier hin.', 'these files are served without processing, your images go here': 'Diese Dateien werden ohne Verarbeitung ausgeliefert. Beispielsweise Bilder kommen hier hin.', -"This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +"This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": 'Dieser Debugger funktioniert möglicherweise nicht ordnungsgemäß, wenn Sie keinen Thread-Webserver haben oder mehrere Daemon-Prozesse verwenden.', +'This email already has an account': 'This email already has an account', 'This is a copy of the scaffolding application': 'Dies ist eine Kopie einer Grundgerüst-Anwendung', -'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', -'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', +'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'Dies ist eine experimentelle Funktion und muss noch getestet werden. Wenn Sie sich für ein Downgrade entscheiden, tun Sie dies auf eigene Gefahr', +'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'Dies ist eine experimentelle Funktion und muss noch getestet werden. Wenn Sie sich für ein Upgrade entscheiden, tun Sie dies auf eigene Gefahr', 'This is the %(filename)s template': 'Dies ist das Template %(filename)s', -"This page can commit your changes to an openshift app repo and push them to your cloud instance. This assumes that you've already created the application instance using the web2py skeleton and have that repo somewhere on a filesystem that this web2py instance can access. This functionality requires GitPython installed and on the python path of the runtime that web2py is operating in.": "This page can commit your changes to an openshift app repo and push them to your cloud instance. This assumes that you've already created the application instance using the web2py skeleton and have that repo somewhere on a filesystem that this web2py instance can access. This functionality requires GitPython installed and on the python path of the runtime that web2py is operating in.", -'This page can upload your application to the Google App Engine computing cloud. Mind that you must first create indexes locally and this is done by installing the Google appserver and running the app locally with it once, or there will be errors when selecting records. Attention: deployment may take long time, depending on the network speed. Attention: it will overwrite your app.yaml. DO NOT SUBMIT TWICE.': 'This page can upload your application to the Google App Engine computing cloud. Mind that you must first create indexes locally and this is done by installing the Google appserver and running the app locally with it once, or there will be errors when selecting records. Attention: deployment may take long time, depending on the network speed. Attention: it will overwrite your app.yaml. DO NOT SUBMIT TWICE.', -'this page to see if a breakpoint was hit and debug interaction is required.': 'this page to see if a breakpoint was hit and debug interaction is required.', -'This will pull changes from the remote repo for application "%s"?': 'This will pull changes from the remote repo for application "%s"?', -'This will push changes to the remote repo for application "%s".': 'This will push changes to the remote repo for application "%s".', +"This page can commit your changes to an openshift app repo and push them to your cloud instance. This assumes that you've already created the application instance using the web2py skeleton and have that repo somewhere on a filesystem that this web2py instance can access. This functionality requires GitPython installed and on the python path of the runtime that web2py is operating in.": 'Auf dieser Seite können Sie Ihre Änderungen in ein OpenShift-App-Repo übernehmen und in Ihre Cloud-Instanz verschieben. Dies setzt voraus, dass Sie die Anwendungsinstanz bereits mit dem web2py-Skelett erstellt haben und dieses Repo auf einem Dateisystem haben, auf das diese web2py-Instanz zugreifen kann. Für diese Funktionalität muss GitPython installiert sein und sich auf dem Python-Pfad der Runtime befinden, in der web2py ausgeführt wird.', +'This page can upload your application to the Google App Engine computing cloud. Mind that you must first create indexes locally and this is done by installing the Google appserver and running the app locally with it once, or there will be errors when selecting records. Attention: deployment may take long time, depending on the network speed. Attention: it will overwrite your app.yaml. DO NOT SUBMIT TWICE.': 'Diese Seite kann Ihre Anwendung in die Cloud der Google App Engine Computing hochladen. Beachten Sie, dass Sie zunächst Indexe lokal erstellen müssen. Dies geschieht, indem Sie den Google Appserver installieren und die App einmalig lokal ausführen. Andernfalls treten Fehler bei der Auswahl von Datensätzen auf. Achtung: Die Bereitstellung kann abhängig von der Netzwerkgeschwindigkeit sehr lange dauern. Achtung: Ihre app.yaml wird überschrieben. SENDEN SIE NICHT ZWEIMAL EIN.', +'this page to see if a breakpoint was hit and debug interaction is required.': 'Auf dieser Seite können Sie sehen, ob ein breakpoint getroffen wurde und eine Debug-Interaktion erforderlich ist.', +'This will pull changes from the remote repo for application "%s"?': 'Dadurch werden Änderungen vom Remote-Repo für die Anwendung "% s" übernommen?', +'This will push changes to the remote repo for application "%s".': 'Dadurch werden Änderungen an das Remote-Repo für die Anwendung "% s" übertragen.', 'Ticket': 'Ticket', 'Ticket ID': 'Ticket ID', -'Ticket Missing': 'Ticket Missing', -'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', +'Ticket Missing': 'Ticket fehlt', +'Time in Cache (h:m:s)': 'Zeit im Cache (h:m:s)', 'Timestamp': 'Zeitstempel', 'TM': 'TM', 'to previous version.': 'zu einer früheren Version.', 'To create a plugin, name a file/folder plugin_[name]': 'Um ein Plugin zu erstellen benennen Sie eine(n) Datei/Ordner plugin_[Name]', -'To emulate a breakpoint programatically, write:': 'To emulate a breakpoint programatically, write:', -'to use the debugger!': 'to use the debugger!', +'To emulate a breakpoint programatically, write:': 'Um einen breapoint programmatisch zu emulieren, schreiben Sie:', +'to use the debugger!': 'um den Debugger zuverwenden!', 'toggle breakpoint': 'Breakpoint aktivieren/deaktivieren', 'Toggle comment': ' Kommentar ein-/ausblenden', 'Toggle Fullscreen': 'Vollbild ein-/ausschalten', @@ -628,31 +692,33 @@ 'try something like': 'Versuchen Sie so etwas wie', 'Try the mobile interface': 'Testen Sie das Interface für Handys', 'try view': 'Versuche view', -'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', -'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', +'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Geben Sie den PDB-Debuggerbefehl hier ein und drücken Sie die Eingabetaste, um ihn auszuführen.', +'Type some Python code in here and hit Return (Enter) to execute it.': 'Geben Sie hier einen Python-Code ein und drücken Sie die Eingabetaste, um ihn auszuführen.', 'Unable to check for upgrades': 'Überprüfen von Upgrades nicht möglich', 'unable to create application "%s"': 'Erzeugen von Anwendung "%s" nicht möglich', 'unable to delete file "%(filename)s"': 'Löschen von Dateien "%(filename)s" nicht möglich', -'unable to delete file plugin "%(plugin)s"': 'unable to delete file plugin "%(plugin)s"', -'Unable to determine the line number!': 'Unable to determine the line number!', +'unable to delete file plugin "%(plugin)s"': 'Datei-Plugin "%(plugin)s" kann nicht gelöscht werden', +'Unable to determine the line number!': 'Die Zeilennummer kann nicht ermittelt werden!', 'Unable to download': 'Herunterladen nicht möglich', 'Unable to download app': 'Herunterladen der Anwendung nicht möglich', -'Unable to download app because:': 'Unable to download app because:', -'unable to download layout': 'unable to download layout', -'unable to download plugin: %s': 'unable to download plugin: %s', -'Unable to download the list of plugins': 'Unable to download the list of plugins', -'unable to install plugin "%s"': 'unable to install plugin "%s"', +'Unable to download app because:': 'App kann nicht heruntergeladen werden, weil:', +'unable to download layout': 'Layout kann nicht heruntergeladen werden', +'unable to download plugin: %s': 'Folgendes Plugin konnte nicht heruntergeladen werden: %s', +'Unable to download the list of plugins': 'Die Liste der Plugins kann nicht heruntergeladen werden', +'unable to install plugin "%s"': 'Plugin "%s" kann nicht installiert werden', 'unable to parse csv file': 'Analysieren der cvs Datei nicht möglich', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'Deinstallieren von "%s" nicht möglich', -'unable to upgrade because "%s"': 'unable to upgrade because "%s"', +'unable to upgrade because "%s"': 'Upgrade wegen "%s" nicht möglich', 'uncheck all': 'Auswahl entfernen', 'Uninstall': 'Deinstallieren', -'Unsupported webserver working mode: %s': 'Unsupported webserver working mode: %s', +'Unsupported webserver working mode: %s': 'Nicht unterstützte Webserver-Arbeitsmodell: %s', 'update': 'Aktualisieren', 'update all languages': 'Aktualisiere alle Sprachen', 'Update:': 'Aktualisiere:', 'Upgrade': 'Upgrade', -'upgrade now to %s': 'upgrade now to %s', +'upgrade now to %s': 'Upgrade jetzt zu %s', 'upgrade web2py now': 'web2py jetzt upgraden', 'upload': 'upload', 'Upload': 'Upload', @@ -667,11 +733,25 @@ 'Use an url:': 'Verwende URL:', 'user': 'Nutzer', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'Benutzer ID', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', -'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', +'Using the shell may lock the database to other users of this app.': 'Durch die Verwendung des Shell kann die Datenbank für andere Benutzer dieser App gesperrt werden.', 'variables': 'Variablen', +'Verify Password': 'Verify Password', 'Version': 'Version', 'Version %s.%s.%s (%s) %s': 'Version %s.%s.%s (%s) %s', 'Versioning': 'Versionsverwaltung', @@ -680,35 +760,41 @@ 'view': 'Ansicht', 'Views': 'Ansichten', 'views': 'Ansichten', -'Warning!': 'Warning!', -'WARNING:': 'WARNING:', -'WARNING: The following views could not be compiled:': 'WARNING: The following views could not be compiled:', +'Warning!': 'Warnung!', +'WARNING:': 'WARNUNG:', +'WARNING: The following views could not be compiled:': 'WARNUNG: Die folgenden Ansichten konnten nicht kompiliert werden:', 'Web Framework': 'Web Framework', -'web2py Admin Password': 'web2py Admin Password', -'web2py apps to deploy': 'web2py apps to deploy', +'web2py Admin Password': 'Administratorkennwort für web2py', +'web2py apps to deploy': 'Web2py-Apps zur Bereitstellung', 'web2py Debugger': 'web2py Debugger', -'web2py downgrade': 'web2py downgrade', +'web2py downgrade': 'web2py Downgrade', 'web2py is up to date': 'web2py ist aktuell', -'web2py online debugger': 'web2py online debugger', +'web2py online debugger': 'web2py Online-Debugger', 'web2py Recent Tweets': 'Neuste Tweets von web2py', -'web2py upgrade': 'web2py upgrade', -'web2py upgraded; please restart it': 'web2py upgraded; please restart it', +'web2py upgrade': 'web2py-Upgrade', +'web2py upgraded; please restart it': 'web2py aktualisiert bitte neu starten', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': 'Willkommen %s', 'Welcome to web2py': 'Willkommen zu web2py', 'Which called the function': 'welche die Funktion aufrief', -'Working...': 'Working...', +'Working...': 'Arbeitet...', 'Wrap with Abbreviation': 'mit Kürzel einhüllen', -'WSGI reference name': 'WSGI reference name', +'WSGI reference name': 'WSGI Referenzname', 'xml': 'xml', 'YES': 'JA', -'Yes': 'Yes', +'Yes': 'JA', 'You are successfully running web2py': 'web2by wird erfolgreich ausgeführt', -'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', -'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'Sie können Breakpoint auch im Bearbeitungsfenster festlegen und entfernen, indem Sie die Schaltfläche Breakpoint umschalten verwenden', +'You can inspect variables using the console below': 'Sie können Variablen mit der folgenden Konsole überprüfen', 'You can modify this application and adapt it to your needs': 'Sie können diese Anwendung verändern und Ihren Bedürfnissen anpassen', -'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', +'You have one more login attempt before you are locked out': 'Sie haben einen weiteren Anmeldeversuch, bevor Sie gesperrt werden', 'You need to set up and reach a': 'You need to set up and reach a', -'You only need these if you have already registered': 'You only need these if you have already registered', +'You only need these if you have already registered': 'Sie benötigen diese nur, wenn Sie sich bereits registriert haben.', 'You visited the url': 'Sie besuchten die URL', -'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Ihre Bewerbung wird blockiert, bis Sie auf eine Aktionsschaltfläche klicken (next, step, continue usw.).', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/en.py b/applications/admin/languages/en.py old mode 100755 new mode 100644 index 0d5dcf5c..9ab4aa93 --- a/applications/admin/languages/en.py +++ b/applications/admin/languages/en.py @@ -15,6 +15,23 @@ '(something like "it-it")': '(something like "it-it")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Searching: **%s** %%{file}': 'Searching: **%s** %%{file}', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'Abort': 'Abort', 'About': 'About', 'About application': 'About application', @@ -43,6 +60,7 @@ 'application is compiled and cannot be designed': 'application is compiled and cannot be designed', 'Application name:': 'Application name:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'are not used', 'are not used yet': 'are not used yet', 'Are you sure you want to delete file "%s"?': 'Are you sure you want to delete file "%s"?', @@ -57,6 +75,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.', 'ATTENTION: you cannot edit the running application!': 'ATTENTION: you cannot edit the running application!', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available Databases and Tables': 'Available Databases and Tables', 'back': 'back', @@ -83,6 +102,7 @@ 'Change Admin Password': 'Change Admin Password', 'Change admin password': 'Change admin password', 'change editor settings': 'change editor settings', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'check all', 'Check for upgrades': 'Check for upgrades', @@ -93,8 +113,10 @@ 'Clear CACHE?': 'Clear CACHE?', 'Clear DISK': 'Clear DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Click row to expand traceback', 'Click row to view a ticket': 'Click row to view a ticket', +'Client IP': 'Client IP', 'code': 'code', 'Code listing': 'Code listing', 'collapse/expand all': 'collapse/expand all', @@ -150,6 +172,7 @@ 'Deploy to PythonAnywhere': 'Deploy to PythonAnywhere', 'Deployment form': 'Deployment form', 'Deployment Interface': 'Deployment Interface', +'Description': 'Description', 'Description:': 'Description:', 'design': 'design', 'Detailed traceback description': 'Detailed traceback description', @@ -176,6 +199,7 @@ 'Download layouts from repository': 'Download layouts from repository', 'download plugins': 'download plugins', 'Download plugins from repository': 'Download plugins from repository', +'E-mail': 'E-mail', 'Edit': 'Edit', 'edit all': 'edit all', 'Edit application': 'Edit application', @@ -187,6 +211,9 @@ 'Editing Plural Forms File': 'Editing Plural Forms File', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Enable', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -223,8 +250,10 @@ 'filter': 'filter', 'Find Next': 'Find Next', 'Find Previous': 'Find Previous', +'First name': 'First name', 'Form has errors': 'Form has errors', 'Frames': 'Frames', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Functions with no doctests will result in [passed] tests.', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -239,6 +268,10 @@ 'Goto': 'Goto', 'Graph Model': 'Graph Model', 'graph model': 'graph model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Help': 'Help', 'here': 'here', 'Hide/Show Translated strings': 'Hide/Show Translated strings', @@ -255,11 +288,13 @@ 'Import/Export': 'Import/Export', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'includes', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'inspect attributes': 'inspect attributes', 'Install': 'Install', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'Installed applications', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': 'internal error', @@ -268,21 +303,31 @@ 'Invalid action': 'Invalid action', 'Invalid application name': 'Invalid application name', 'invalid circular reference': 'invalid circular reference', +'Invalid email': 'Invalid email', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'invalid password', +'Invalid password': 'Invalid password', 'invalid password.': 'invalid password.', 'Invalid Query': 'Invalid Query', 'invalid request': 'invalid request', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'invalid ticket', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': 'language file "%(filename)s" created/updated', 'Language files (static strings) updated': 'Language files (static strings) updated', 'languages': 'languages', 'Languages': 'Languages', +'Last name': 'Last name', 'Last Revision': 'Last Revision', 'Last saved on:': 'Last saved on:', 'License for': 'License for', @@ -295,7 +340,11 @@ 'Local Apps': 'Local Apps', 'locals': 'locals', 'Locals##debug': 'Locals##debug', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'Login': 'Login', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'Login to the Administrative Interface', 'Login/Register': 'Login/Register', @@ -316,10 +365,12 @@ 'Modules': 'Modules', 'modules': 'modules', 'Multi User Mode': 'Multi User Mode', +'Name': 'Name', 'new application "%s" created': 'new application "%s" created', 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'New Application Wizard', 'New application wizard': 'New application wizard', +'New password': 'New password', 'new plugin installed': 'new plugin installed', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': 'New Record', @@ -338,6 +389,8 @@ 'Not Authorized': 'Not Authorized', 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'Open new app in new window': 'Open new app in new window', 'OpenShift Deployment Interface': 'OpenShift Deployment Interface', @@ -345,6 +398,7 @@ 'or alternatively': 'or alternatively', 'Or Get from URL:': 'Or Get from URL:', 'or import from csv file': 'or import from csv file', +'Origin': 'Origin', 'Original/Translation': 'Original/Translation', 'Overview': 'Overview', 'Overwrite installed app': 'Overwrite installed app', @@ -352,7 +406,12 @@ 'Pack compiled': 'Pack compiled', 'Pack custom': 'Pack custom', 'pack plugin': 'pack plugin', +'Password': 'Password', 'password changed': 'password changed', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -360,6 +419,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Please', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" deleted', 'Plugin "%s" in application': 'Plugin "%s" in application', @@ -375,6 +435,7 @@ 'previous %s rows': 'previous %s rows', 'Private files': 'Private files', 'private files': 'private files', +'Profile updated': 'Profile updated', 'Project Progress': 'Project Progress', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -390,19 +451,35 @@ 'Ram Cleared': 'Ram Cleared', 'Rapid Search': 'Rapid Search', 'Record': 'Record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'record does not exist', 'Record id': 'Record id', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'register': 'register', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', +'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Reload routes', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'Remove compiled', 'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', 'Replace': 'Replace', 'Replace All': 'Replace All', 'Repository (%s)': 'Repository (%s)', 'request': 'request', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', +'Reset Password key': 'Reset Password key', 'Resolve Conflict file': 'Resolve Conflict file', 'response': 'response', 'restart': 'restart', @@ -438,6 +515,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'shell', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'Site', 'Size of cache:': 'Size of cache:', @@ -486,7 +564,9 @@ 'There are no views': 'There are no views', 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'These files are served without processing, your images go here', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', "This page can commit your changes to an openshift app repo and push them to your cloud instance. This assumes that you've already created the application instance using the web2py skeleton and have that repo somewhere on a filesystem that this web2py instance can access. This functionality requires GitPython installed and on the python path of the runtime that web2py is operating in.": "This page can commit your changes to an openshift app repo and push them to your cloud instance. This assumes that you've already created the application instance using the web2py skeleton and have that repo somewhere on a filesystem that this web2py instance can access. This functionality requires GitPython installed and on the python path of the runtime that web2py is operating in.", @@ -498,6 +578,7 @@ 'Ticket ID': 'Ticket ID', 'Ticket Missing': 'Ticket Missing', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', +'Timestamp': 'Timestamp', 'to previous version.': 'to previous version.', 'To create a plugin, name a file/folder plugin_[name]': 'To create a plugin, name a file/folder plugin_[name]', 'To emulate a breakpoint programatically, write:': 'To emulate a breakpoint programatically, write:', @@ -510,6 +591,7 @@ 'try something like': 'try something like', 'Try the mobile interface': 'Try the mobile interface', 'try view': 'try view', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'Unable to check for upgrades', @@ -523,6 +605,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': 'unable to parse csv file', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'unable to uninstall "%s"', 'unable to upgrade because "%s"': 'unable to upgrade because "%s"', 'uncheck all': 'uncheck all', @@ -541,10 +624,25 @@ 'upload plugin file:': 'upload plugin file:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'variables', +'Verify Password': 'Verify Password', 'Version': 'Version', 'Versioning': 'Versioning', 'Views': 'Views', @@ -561,14 +659,20 @@ 'web2py online debugger': 'web2py online debugger', 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py upgraded; please restart it', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Working...': 'Working...', 'WSGI reference name': 'WSGI reference name', 'YES': 'YES', 'Yes': 'Yes', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/es.py b/applications/admin/languages/es.py old mode 100755 new mode 100644 index 46ee5a27..eac5f33e --- a/applications/admin/languages/es.py +++ b/applications/admin/languages/es.py @@ -15,10 +15,23 @@ '(something like "it-it")': '(algo como "it-it")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', '@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(archivo **gluon/contrib/plural_rules/%s.py** no se ha encontrado)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'Ocurrió un error, por favor [[recargue %s]] la página', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", '@markmin\x01Number of entries: **%s**': 'Número de entradas: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': 'Buscando: **%s** archivos', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': 'Hay una nueva versión de web2py disponible', 'A new version of web2py is available: %s': 'Hay una nueva versión de web2py disponible: %s', 'Abort': 'Abort', @@ -54,6 +67,7 @@ 'application is compiled and cannot be designed': 'la aplicación está compilada y no puede ser modificada', 'Application name:': 'Nombre de la aplicación:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'are not used', 'are not used yet': 'are not used yet', 'Are you sure you want to delete file "%s"?': '¿Está seguro que desea eliminar el archivo "%s"?', @@ -70,6 +84,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATENCIÓN: Inicio de sesión requiere una conexión segura (HTTPS) o localhost.', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATENCIÓN: NO EJECUTE VARIAS PRUEBAS SIMULTANEAMENTE, NO SON THREAD SAFE.', 'ATTENTION: you cannot edit the running application!': 'ATENCIÓN: ¡no puede modificar la aplicación que se ejecuta!', +'Authentication code': 'Authentication code', 'Autocomplete': 'Autocompletar', 'Autocomplete Python Code': 'Autocompletar código Python', 'Available databases and tables': 'Bases de datos y tablas disponibles', @@ -101,6 +116,7 @@ 'Change admin password': 'cambie contraseña admin', 'change editor settings': 'cambiar la configuración del editor', 'Change Password': 'Cambie Contraseña', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'marcar todos', 'Check for upgrades': 'buscar actualizaciones', @@ -113,6 +129,7 @@ 'Clear RAM': 'Limpiar RAM', 'click here for online examples': 'haga click aquí para ver ejemplos en línea', 'click here for the administrative interface': 'haga click aquí para usar la interfaz administrativa', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Click en la fila para expandir el rastreo', 'Click row to view a ticket': 'Click row to view a ticket', 'click to check for upgrades': 'haga clic para buscar actualizaciones', @@ -233,6 +250,9 @@ 'Editing ul': 'Editando ul', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Habilitar', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -277,6 +297,7 @@ 'First name': 'Nombre', 'Form has errors': 'Form has errors', 'Frames': 'Frames', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Funciones sin doctests equivalen a pruebas [aceptadas].', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -291,7 +312,10 @@ 'Goto': 'Goto', 'graph model': 'graficación del modelo', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'ID de Grupo', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Hello World': 'Hola Mundo', 'Help': 'ayuda', 'here': 'aquí', @@ -311,6 +335,7 @@ 'Import/Export': 'Importar/Exportar', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'incluye', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'insert new': 'inserte nuevo', 'insert new %s': 'inserte nuevo %s', @@ -319,6 +344,7 @@ 'Installation of %(plugin)s for %(app)s': 'Instalación de %(plugin)s para %(app)s', 'Installation of %(plugin)s for %(app)s app': 'Instalación de %(plugin)s para %(app)s app', 'Installed applications': 'Aplicaciones instaladas', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interacción en %s línea %s', 'Interactive console': 'Terminal interactiva', 'internal error': 'error interno', @@ -329,16 +355,24 @@ 'invalid circular reference': 'invalid circular reference', 'Invalid email': 'Correo inválido', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'contraseña inválida', +'Invalid password': 'Invalid password', 'invalid password.': 'contraseña inválida.', 'Invalid Query': 'Consulta inválida', 'invalid request': 'solicitud inválida', 'Invalid request': 'Petición inválida', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'tiquete inválido', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', 'Key bindings': 'Key bindings', 'Key bindings for ZenCoding Plugin': 'Key bindings para el Plugin ZenCoding', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Atajos de teclado', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': 'archivo de lenguaje "%(filename)s" creado/actualizado', @@ -360,8 +394,12 @@ 'Local Apps': 'Local Apps', 'locals': 'locals', 'Locals##debug': 'Locals', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'login': 'inicio de sesión', 'Login': 'Inicio de sesión', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'Inicio de sesión para la Interfaz Administrativa', 'Login/Register': 'Login/Register', @@ -389,6 +427,7 @@ 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'New Application Wizard', 'New application wizard': 'Asistente para nueva aplicación', +'New password': 'New password', 'new plugin installed': 'nuevo plugin instalado', 'New plugin installed: %s': 'Nuevo plugin instalado: %s', 'New plugin installed: web2py.plugin.attachment.w2p': 'Nuevo plugin instalado: web2py.plugin.attachment.w2p', @@ -413,6 +452,8 @@ 'Not Authorized': 'Not Authorized', 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'online designer': 'diseñador en línea', 'Open new app in new window': 'Open new app in new window', @@ -434,6 +475,10 @@ 'PAM authenticated user, cannot change password here': 'usuario autenticado por PAM, no puede cambiar la contraseña aquí', 'Password': 'Contraseña', 'password changed': 'contraseña cambiada', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -441,6 +486,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Por favor', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'Plugin': 'Plugin', 'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" eliminado', @@ -458,6 +504,7 @@ 'previous 100 rows': '100 filas anteriores', 'Private files': 'Archivos privados', 'private files': 'archivos privados', +'Profile updated': 'Profile updated', 'Project Progress': 'Progreso del Proyecto', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -474,16 +521,28 @@ 'Rapid Search': 'Búsqueda rápida', 'Record': 'Record', 'record': 'registro', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'el registro no existe', 'record id': 'id de registro', 'Record ID': 'ID de Registro', 'Record id': 'Record id', +'Record Updated': 'Record Updated', 'refresh': 'recargar', 'register': 'register', 'Register': 'Regístrese', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Contraseña de Registro', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'reload': 'recargar', 'Reload routes': 'Recargar rutas', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'eliminar compilados', 'Removed Breakpoint on %s at line %s': 'Eliminado punto de ruptura en %s en la línea %s', 'Replace': 'Reemplazar', @@ -491,8 +550,10 @@ 'Repository (%s)': 'Repositorio (%s)', 'Repository: %s': 'Repositorio: %s', 'request': 'petición', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', +'Reset Password key': 'Reset Password key', 'Resolve Conflict file': 'archivo Resolución de Conflicto', 'response': 'respuesta', 'restart': 'restart', @@ -532,6 +593,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Establecer punto de ruptura en %s en la línea %s: %s', 'shell': 'shell', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'sitio', 'Size of cache:': 'Size of cache:', @@ -588,7 +650,9 @@ 'These files are not served, they are only available from within your app': 'Estos archivos no se proveen, ellos sólo están disponibles para su aplicación', 'These files are served without processing, your images go here': 'Estos archivos se proveen sin procesar, sus imágenes van aquí', 'these files are served without processing, your images go here': 'estos archivos se proveen sin procesar, sus imágenes van aquí', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', 'This is the %(filename)s template': 'Está es la plantilla %(filename)s', @@ -617,6 +681,7 @@ 'try something like': 'intente algo como', 'Try the mobile interface': 'Pruebe la interfaz móvil', 'try view': 'Pruebe la vista', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Escriba algún código Python aquí y teclee la tecla Enter para ejecutarlo', 'Unable to check for upgrades': 'No es posible verificar la existencia de actualizaciones', @@ -633,6 +698,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': 'no es posible analizar el archivo CSV', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'no es posible instalar "%s"', 'unable to upgrade because "%s"': 'no es posible actualizar porque "%s"', 'uncheck all': 'desmarcar todos', @@ -655,11 +721,25 @@ 'upload plugin file:': 'suba un archivo de plugin:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) para AND, (...)|(...) para OR, y ~(...) para NOT, para crear consultas más complejas.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'ID de Usuario', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'variables', +'Verify Password': 'Verify Password', 'Version': 'Versión', 'Versioning': 'Versiones', 'versioning': 'versiones', @@ -679,6 +759,7 @@ 'web2py Recent Tweets': 'Tweets Recientes de web2py', 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py actualizado; favor reiniciar', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py': 'Bienvenido a web2py', 'Working...': 'Working...', 'WSGI reference name': 'WSGI reference name', @@ -687,8 +768,13 @@ 'You are going to install': 'Vas a instalar', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'Puedes inspeccionar las variables utilizando la terminal de abajo', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'Necesitas configurar y obtener un', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Tu aplicación será bloqueada hasta que des click en un botón de acción (siguiente, paso, continuar, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/fr.py b/applications/admin/languages/fr.py old mode 100755 new mode 100644 index 0588e8b3..6b310f92 --- a/applications/admin/languages/fr.py +++ b/applications/admin/languages/fr.py @@ -21,9 +21,24 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': 'Cherche: **%s** fichiers', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available: %s': 'Une nouvelle version de web2py est disponible: %s ', 'A new version of web2py is available: Version 1.68.2 (2009-10-21 09:59:29)\n': 'Une nouvelle version de web2py est disponible: Version 1.68.2 (2009-10-21 09:59:29)\r\n', 'Abort': 'Abort', @@ -58,6 +73,7 @@ 'application is compiled and cannot be designed': "l'application est compilée et ne peut être modifiée", 'Application name:': "Nom de l'application:", 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'ne sont pas utilisé', 'are not used yet': 'ne sont pas encore utilisé', 'Are you sure you want to delete file "%s"?': 'Êtes-vous sûr de vouloir supprimer le fichier «%s»?', @@ -73,6 +89,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATTENTION: nécessite une connexion sécurisée (HTTPS) ou être en localhost. ', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTION: les tests ne sont pas thread-safe DONC NE PAS EFFECTUER DES TESTS MULTIPLES SIMULTANÉMENT.', 'ATTENTION: you cannot edit the running application!': "ATTENTION: vous ne pouvez pas modifier l'application qui tourne!", +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available databases and tables': 'Bases de données et tables disponibles', 'Available Databases and Tables': 'Available Databases and Tables', @@ -102,6 +119,7 @@ 'Change Admin Password': 'Change Admin Password', 'Change admin password': 'Changer le mot de passe admin', 'change editor settings': 'change editor settings', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'tout sélectionner', 'Check for upgrades': 'Vérifier les mises à jour', @@ -112,9 +130,11 @@ 'Clear CACHE?': 'Effacer le CACHE?', 'Clear DISK': 'Effacer le DISQUE', 'Clear RAM': 'Effacer la RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Click row to expand traceback', 'Click row to view a ticket': 'Click row to view a ticket', 'click to check for upgrades': 'Cliquez pour vérifier les mises jour', +'Client IP': 'Client IP', 'code': 'code', 'Code listing': 'Code listing', 'collapse/expand all': 'tout réduire/agrandir', @@ -174,6 +194,7 @@ 'Deploy to PythonAnywhere': 'Deploy to PythonAnywhere', 'Deployment form': 'Deployment form', 'Deployment Interface': 'Deployment Interface', +'Description': 'Description', 'Description:': 'Description:', 'design': 'conception', 'Detailed traceback description': 'Detailed traceback description', @@ -201,6 +222,7 @@ 'Download layouts from repository': 'Download layouts from repository', 'download plugins': 'télécharger plugins', 'Download plugins from repository': 'Download plugins from repository', +'E-mail': 'E-mail', 'EDIT': 'MODIFIER', 'Edit': 'Modifier', 'edit all': 'tout modifier', @@ -216,6 +238,9 @@ 'Editing Plural Forms File': 'Modifier le fichier du formulaire pluriel', 'Editor': 'Éditeur', 'Email Address': 'Adresse courriel', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Activer', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -254,8 +279,10 @@ 'filter': 'filtre', 'Find Next': 'Trouver les suivants', 'Find Previous': 'Trouver les précédents', +'First name': 'First name', 'Form has errors': 'Le formulaire comporte des erreurs', 'Frames': 'Frames', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Des fonctions sans doctests entraîneront des tests [passed] .', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -270,6 +297,10 @@ 'Goto': 'Goto', 'graph model': 'représentation graphique du modèle', 'Graph Model': 'Représentation graphique du modèle', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Help': 'aide', 'here': 'ici', 'Hide/Show Translated strings': 'Hide/Show Translated strings', @@ -288,6 +319,7 @@ 'Import/Export': 'Importer/Exporter', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'inclus', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'index': 'index', 'insert new': 'insérer nouveau', @@ -296,6 +328,7 @@ 'Install': 'Installer', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'Applications installées', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': 'erreur interne', @@ -304,21 +337,31 @@ 'Invalid action': 'Action non valide', 'Invalid application name': 'Invalid application name', 'invalid circular reference': 'invalid circular reference', +'Invalid email': 'Invalid email', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'mot de passe invalide', +'Invalid password': 'Invalid password', 'invalid password.': 'invalid password.', 'Invalid Query': 'Requête non valide', 'invalid request': 'Demande incorrecte', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'ticket non valide', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Clé', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': 'fichier de langue "%(filename)s" créé/mis à jour', 'Language files (static strings) updated': 'Fichiers de langue (chaînes statiques) mis à jour ', 'languages': 'langues', 'Languages': 'Langues', +'Last name': 'Last name', 'Last Revision': 'Dernière révision', 'Last saved on:': 'Dernière sauvegarde le:', 'License for': 'Licence pour', @@ -332,8 +375,12 @@ 'Local Apps': 'Local Apps', 'locals': 'locals', 'Locals##debug': 'Locals##debug', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'Login': 'Connexion', 'login': 'connexion', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': "Se connecter à l'interface d'administration", 'Login/Register': 'Login/Register', @@ -355,10 +402,12 @@ 'Modules': 'Modules', 'modules': 'modules', 'Multi User Mode': 'Multi User Mode', +'Name': 'Name', 'new application "%s" created': 'nouvelle application "%s" créée', 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'New Application Wizard', 'New application wizard': 'Assistant nouvelle application', +'New password': 'New password', 'new plugin installed': 'nouveau plugin installé', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': 'Nouvelle Entrée', @@ -379,6 +428,8 @@ 'Not supported': 'Pas supporté', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', 'Number of entries: **%s**': 'Number of entries: **%s**', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'Open new app in new window': 'Open new app in new window', 'OpenShift Deployment Interface': 'OpenShift Deployment Interface', @@ -388,6 +439,7 @@ 'or import from csv file': 'ou importer depuis un fichier CSV ', 'or provide app url:': "ou fournir l'URL de l'app:", 'or provide application url:': "ou fournir l'URL de l'application:", +'Origin': 'Origin', 'Original/Translation': 'Original / Traduction', 'Overview': 'Overview', 'Overwrite installed app': "Écraser l'application installée", @@ -396,7 +448,12 @@ 'Pack custom': 'Pack custom', 'pack plugin': 'paquet plugin', 'PAM authenticated user, cannot change password here': 'Utilisateur authentifié par PAM, vous ne pouvez pas changer le mot de passe ici', +'Password': 'Password', 'password changed': 'mot de passe modifié', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -404,6 +461,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'SVP', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" supprimé', 'Plugin "%s" in application': 'Plugin "%s" dans l\'application', @@ -420,6 +478,7 @@ 'previous 100 rows': '100 lignes précédentes', 'Private files': 'Private files', 'private files': 'private files', +'Profile updated': 'Profile updated', 'Project Progress': 'Project Progress', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -437,20 +496,36 @@ 'Rapid Search': 'Recherche rapide', 'Record': 'Enregistrement', 'record': 'enregistrement', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': "l'entrée n'existe pas", 'record id': 'id entrée', 'Record id': 'Record id', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'register': 'register', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', +'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Reload routes', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'retirer compilé', 'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', 'Replace': 'Remplacer', 'Replace All': 'Tout remplacer', 'Repository (%s)': 'Repository (%s)', 'request': 'request', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', +'Reset Password key': 'Reset Password key', 'Resolve Conflict file': 'Résoudre les conflits de fichiers', 'response': 'response', 'restart': 'redémarrer', @@ -490,6 +565,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'shell', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'Site', 'Size of cache:': 'Taille de la mémoire cache:', @@ -544,7 +620,9 @@ 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'Ces fichiers sont renvoyés sans traitement, vos images viennent ici', 'these files are served without processing, your images go here': 'ces fichiers sont servis sans transformation, vos images vont ici', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', 'This is the %(filename)s template': 'Ceci est le modèle %(filename)s ', @@ -557,6 +635,7 @@ 'Ticket ID': 'Identifiant du Billet', 'Ticket Missing': 'Billet manquant', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', +'Timestamp': 'Timestamp', 'TM': 'MD', 'to previous version.': 'à la version précédente.', 'To create a plugin, name a file/folder plugin_[name]': 'Pour créer un plugin, créer un fichier /dossier plugin_[nom]', @@ -572,6 +651,7 @@ 'try something like': 'essayez quelque chose comme', 'Try the mobile interface': "Essayer l'interface pour mobile", 'try view': 'essayer la vue', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'Impossible de vérifier les mises à jour', @@ -588,6 +668,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': "impossible d'analyser les fichiers CSV", +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'impossible de désinstaller "%s"', 'unable to upgrade because "%s"': 'impossible de mettre à jour car "%s"', 'uncheck all': 'tout décocher', @@ -613,10 +694,25 @@ 'Use an url:': 'Utiliser une url:', 'user': 'utilisateur', 'User': 'Utilisateur', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', 'Username': "Nom d'utilisateur", +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Utilisateurs', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'variables', +'Verify Password': 'Verify Password', 'Version': 'Version', 'Versioning': 'Versioning', 'versioning': 'versioning', @@ -636,14 +732,20 @@ 'web2py Recent Tweets': 'Tweets récents sur web2py ', 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py mis à jour; veuillez le redémarrer', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Working...': 'Travail en cours...', 'WSGI reference name': 'WSGI reference name', 'YES': 'OUI', 'Yes': 'Oui', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/he.py b/applications/admin/languages/he.py old mode 100755 new mode 100644 index 11353984..f724678f --- a/applications/admin/languages/he.py +++ b/applications/admin/languages/he.py @@ -16,7 +16,23 @@ '(something like "it-it")': '(למשל "it-it")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': 'Searching: **%s** files', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available: %s': 'גירסא חדשה של web2py זמינה: %s', 'Abort': 'Abort', 'About': 'אודות', @@ -48,6 +64,7 @@ 'application is compiled and cannot be designed': 'לא ניתן לערוך אפליקציה מקומפלת', 'Application name:': 'Application name:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'are not used', 'are not used yet': 'are not used yet', 'Are you sure you want to delete file "%s"?': 'האם אתה בטוח שברצונך למחוק את הקובץ "%s"?', @@ -63,6 +80,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'לתשומת ליבך: ניתן להתחבר רק בערוץ מאובטח (HTTPS) או מlocalhost', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'לתשומת ליבך: אין לערוך מספר בדיקות במקביל, שכן הן עשויות להפריע זו לזו', 'ATTENTION: you cannot edit the running application!': 'לתשומת ליבך: לא ניתן לערוך אפליקציה בזמן הרצתה', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available databases and tables': 'מסדי נתונים וטבלאות זמינים', 'Available Databases and Tables': 'Available Databases and Tables', @@ -90,6 +108,7 @@ 'Change Admin Password': 'Change Admin Password', 'Change admin password': 'סיסמת מנהל שונתה', 'change editor settings': 'change editor settings', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'סמן הכל', 'Check for upgrades': 'check for upgrades', @@ -100,9 +119,11 @@ 'Clear CACHE?': 'Clear CACHE?', 'Clear DISK': 'Clear DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Click row to expand traceback', 'Click row to view a ticket': 'Click row to view a ticket', 'click to check for upgrades': 'לחץ כדי לחפש עדכונים', +'Client IP': 'Client IP', 'code': 'קוד', 'Code listing': 'Code listing', 'collapse/expand all': 'collapse/expand all', @@ -162,6 +183,7 @@ 'Deploy to PythonAnywhere': 'Deploy to PythonAnywhere', 'Deployment form': 'Deployment form', 'Deployment Interface': 'Deployment Interface', +'Description': 'Description', 'Description:': 'Description:', 'design': 'עיצוב', 'Detailed traceback description': 'Detailed traceback description', @@ -188,6 +210,7 @@ 'Download layouts from repository': 'Download layouts from repository', 'download plugins': 'download plugins', 'Download plugins from repository': 'Download plugins from repository', +'E-mail': 'E-mail', 'EDIT': 'ערוך!', 'Edit': 'ערוך', 'edit all': 'edit all', @@ -202,6 +225,9 @@ 'Editing Plural Forms File': 'Editing Plural Forms File', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Enable', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -239,8 +265,10 @@ 'filter': 'filter', 'Find Next': 'Find Next', 'Find Previous': 'Find Previous', +'First name': 'First name', 'Form has errors': 'Form has errors', 'Frames': 'Frames', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'פונקציות שלא הוגדר להן doctest ירשמו כבדיקות ש[עברו בהצלחה].', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -255,6 +283,10 @@ 'Goto': 'Goto', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Help': 'עזרה', 'here': 'here', 'Hide/Show Translated strings': 'Hide/Show Translated strings', @@ -272,6 +304,7 @@ 'Import/Export': 'יבא\\יצא', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'מכיל', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'insert new': 'הכנס נוסף', 'insert new %s': 'הכנס %s נוסף', @@ -279,6 +312,7 @@ 'Install': 'התקן', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'אפליקציות מותקנות', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': 'שגיאה מובנית', @@ -287,21 +321,31 @@ 'Invalid action': 'הוראה לא קיימת', 'Invalid application name': 'Invalid application name', 'invalid circular reference': 'invalid circular reference', +'Invalid email': 'Invalid email', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'סיסמא שגויה', +'Invalid password': 'Invalid password', 'invalid password.': 'invalid password.', 'Invalid Query': 'שאילתה לא תקינה', 'invalid request': 'בקשה לא תקינה', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'דו"ח שגיאה לא קיים', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': 'קובץ השפה "%(filename)s" נוצר\\עודכן', 'Language files (static strings) updated': 'קובץ השפה (מחרוזות סטאטיות) עודכן', 'languages': 'שפות', 'Languages': 'שפות', +'Last name': 'Last name', 'Last Revision': 'Last Revision', 'Last saved on:': 'לאחרונה נשמר בתאריך:', 'License for': 'רשיון עבור', @@ -315,8 +359,12 @@ 'Local Apps': 'Local Apps', 'locals': 'locals', 'Locals##debug': 'Locals##debug', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'Login': 'התחבר', 'login': 'התחבר', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'התחבר לממשק המנהל', 'Login/Register': 'Login/Register', @@ -337,10 +385,12 @@ 'Modules': 'מודולים', 'modules': 'מודולים', 'Multi User Mode': 'Multi User Mode', +'Name': 'Name', 'new application "%s" created': 'האפליקציה "%s" נוצרה', 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'New Application Wizard', 'New application wizard': 'New application wizard', +'New password': 'New password', 'new plugin installed': 'פלאגין חדש הותקן', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': 'רשומה חדשה', @@ -360,6 +410,8 @@ 'Not Authorized': 'Not Authorized', 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'Open new app in new window': 'Open new app in new window', 'OpenShift Deployment Interface': 'OpenShift Deployment Interface', @@ -368,6 +420,7 @@ 'Or Get from URL:': 'Or Get from URL:', 'or import from csv file': 'או יבא מקובץ csv', 'or provide app url:': 'או ספק כתובת url של אפליקציה', +'Origin': 'Origin', 'Original/Translation': 'מקור\\תרגום', 'Overview': 'Overview', 'Overwrite installed app': 'התקן על גבי אפלקציה מותקנת', @@ -376,7 +429,12 @@ 'Pack custom': 'Pack custom', 'pack plugin': 'ארוז תוסף', 'PAM authenticated user, cannot change password here': 'שינוי סיסמא באמצעות PAM אינו יכול להתבצע כאן', +'Password': 'Password', 'password changed': 'סיסמא שונתה', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -384,6 +442,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Please', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin "%(plugin)s" deleted': 'תוסף "%(plugin)s" נמחק', 'Plugin "%s" in application': 'פלאגין "%s" של אפליקציה', @@ -400,6 +459,7 @@ 'previous 100 rows': '100 הרשומות הקודמות', 'Private files': 'Private files', 'private files': 'private files', +'Profile updated': 'Profile updated', 'Project Progress': 'Project Progress', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -416,20 +476,36 @@ 'Rapid Search': 'Rapid Search', 'Record': 'Record', 'record': 'רשומה', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'הרשומה אינה קיימת', 'record id': 'מזהה רשומה', 'Record id': 'Record id', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'register': 'register', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', +'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Reload routes', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'הסר מקומפל', 'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', 'Replace': 'Replace', 'Replace All': 'Replace All', 'Repository (%s)': 'Repository (%s)', 'request': 'request', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', +'Reset Password key': 'Reset Password key', 'Resolve Conflict file': 'הסר קובץ היוצר קונפליקט', 'response': 'response', 'restart': 'restart', @@ -467,6 +543,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'שורת פקודה', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'אתר', 'Size of cache:': 'Size of cache:', @@ -521,7 +598,9 @@ 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'These files are served without processing, your images go here', 'these files are served without processing, your images go here': 'אלו הם קבצים הנשלחים מהשרת ללא עיבוד. הכנס את התמונות כאן', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', 'This is the %(filename)s template': 'זוהי תבנית הקובץ %(filename)s ', @@ -534,6 +613,7 @@ 'Ticket ID': 'Ticket ID', 'Ticket Missing': 'Ticket Missing', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', +'Timestamp': 'Timestamp', 'TM': 'סימן רשום', 'to previous version.': 'אין גירסא קודמת', 'To create a plugin, name a file/folder plugin_[name]': 'כדי ליצור תוסף, קרא לקובץ או סיפריה בשם לפי התבנית plugin_[name]', @@ -549,6 +629,7 @@ 'try something like': 'נסה משהו כמו', 'Try the mobile interface': 'Try the mobile interface', 'try view': 'try view', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'לא ניתן היה לבדוק אם יש שדרוגים', @@ -563,6 +644,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': 'לא הצלחתי לנתח את הקלט של קובץ csv', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'לא ניתן להסיר את "%s"', 'unable to upgrade because "%s"': 'לא ניתן היה לשדרג כי "%s"', 'uncheck all': 'הסר סימון מהכל', @@ -586,10 +668,25 @@ 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'השתמש ב (...)&(...) עבור תנאי AND, (...)|(...) עבור תנאי OR ו~(...) עבור תנאי NOT ליצירת שאילתות מורכבות', 'Use an url:': 'Use an url:', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'משתנים', +'Verify Password': 'Verify Password', 'Version': 'גירסא', 'Versioning': 'Versioning', 'versioning': 'מנגנון גירסאות', @@ -609,14 +706,20 @@ 'web2py Recent Tweets': 'ציוצים אחרונים של web2py', 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py שודרגה; נא אתחל אותה', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Working...': 'Working...', 'WSGI reference name': 'WSGI reference name', 'YES': 'כן', 'Yes': 'Yes', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/it.py b/applications/admin/languages/it.py old mode 100755 new mode 100644 index 64d16f5e..910f1a68 --- a/applications/admin/languages/it.py +++ b/applications/admin/languages/it.py @@ -16,9 +16,23 @@ '(something like "it-it")': '(qualcosa simile a "it-it")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', '@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': 'Searching: **%s** files', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available: %s': 'È disponibile una nuova versione di web2py: %s', 'Abort': 'Abort', 'About': 'informazioni', @@ -51,6 +65,7 @@ 'application is compiled and cannot be designed': "l'applicazione è compilata e non si può modificare", 'Application name:': 'Application name:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'are not used', 'are not used yet': 'are not used yet', 'Are you sure you want to delete file "%s"?': 'Confermi di voler cancellare il file "%s"?', @@ -66,6 +81,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': "ATTENZIONE: L'accesso richiede una connessione sicura (HTTPS) o l'esecuzione di web2py in locale (connessione su localhost)", 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTZIONE: NON ESEGUIRE PIÙ TEST IN PARALLELO (I TEST NON SONO "THREAD SAFE")', 'ATTENTION: you cannot edit the running application!': "ATTENZIONE: non puoi modificare l'applicazione correntemente in uso ", +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available databases and tables': 'Database e tabelle disponibili', 'Available Databases and Tables': 'Available Databases and Tables', @@ -94,6 +110,7 @@ 'Change admin password': 'change admin password', 'change editor settings': 'change editor settings', 'change password': 'cambia password', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'controlla tutto', 'Check for upgrades': 'check for upgrades', @@ -106,9 +123,11 @@ 'Clear RAM': 'Clear RAM', 'click here for online examples': 'clicca per vedere gli esempi', 'click here for the administrative interface': "clicca per l'interfaccia amministrativa", +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Click row to expand traceback', 'Click row to view a ticket': 'Click row to view a ticket', 'click to check for upgrades': 'clicca per controllare presenza di aggiornamenti', +'Client IP': 'Client IP', 'code': 'code', 'Code listing': 'Code listing', 'collapse/expand all': 'collapse/expand all', @@ -172,6 +191,7 @@ 'Deploy to PythonAnywhere': 'Deploy to PythonAnywhere', 'Deployment form': 'Deployment form', 'Deployment Interface': 'Deployment Interface', +'Description': 'Description', 'Description:': 'Description:', 'design': 'progetta', 'Detailed traceback description': 'Detailed traceback description', @@ -198,6 +218,7 @@ 'Download layouts from repository': 'Download layouts from repository', 'download plugins': 'download plugins', 'Download plugins from repository': 'Download plugins from repository', +'E-mail': 'E-mail', 'EDIT': 'MODIFICA', 'Edit': 'modifica', 'edit all': 'edit all', @@ -213,9 +234,13 @@ 'Editing Plural Forms File': 'Editing Plural Forms File', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Enable', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', +'Enter an integer between %(min)g and %(max)g': 'Enter an integer between %(min)g and %(max)g', 'Enterprise Web Framework': 'Enterprise Web Framework', 'Error': 'Error', 'Error logs for "%(app)s"': 'Log degli errori per "%(app)s"', @@ -251,8 +276,10 @@ 'filter': 'filter', 'Find Next': 'Find Next', 'Find Previous': 'Find Previous', +'First name': 'First name', 'Form has errors': 'Form has errors', 'Frames': 'Frames', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'I test delle funzioni senza "doctests" risulteranno sempre [passed].', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -268,6 +295,10 @@ 'Goto': 'Goto', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Hello World': 'Salve Mondo', 'Help': 'aiuto', 'here': 'here', @@ -286,6 +317,7 @@ 'Import/Export': 'Importa/Esporta', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'include', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'Index': 'Indice', 'insert new': 'inserisci nuovo', @@ -294,6 +326,7 @@ 'Install': 'installa', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'Applicazioni installate', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': 'errore interno', @@ -302,23 +335,33 @@ 'Invalid action': 'Azione non valida', 'Invalid application name': 'Invalid application name', 'invalid circular reference': 'invalid circular reference', +'Invalid email': 'Invalid email', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'password non valida', +'Invalid password': 'Invalid password', 'invalid password.': 'invalid password.', 'Invalid Query': 'Richiesta (query) non valida', 'invalid request': 'richiesta non valida', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'ticket non valido', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', 'Key bindings': 'Key bindings', 'Key bindings for ZenCoding Plugin': 'Key bindings for ZenCoding Plugin', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': 'file linguaggio "%(filename)s" creato/aggiornato', 'Language files (static strings) updated': 'Linguaggi (documenti con stringhe statiche) aggiornati', 'languages': 'linguaggi', 'Languages': 'Linguaggi', +'Last name': 'Last name', 'Last Revision': 'Last Revision', 'Last saved on:': 'Ultimo salvataggio:', 'Layout': 'Layout', @@ -333,8 +376,12 @@ 'Local Apps': 'Local Apps', 'locals': 'locals', 'Locals##debug': 'Locals##debug', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'Login': 'Accesso', 'login': 'accesso', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': "Accesso all'interfaccia amministrativa", 'Login/Register': 'Login/Register', @@ -356,10 +403,12 @@ 'Modules': 'Moduli', 'modules': 'moduli', 'Multi User Mode': 'Multi User Mode', +'Name': 'Name', 'new application "%s" created': 'creata la nuova applicazione "%s"', 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'New Application Wizard', 'New application wizard': 'New application wizard', +'New password': 'New password', 'new plugin installed': 'installato nuovo plugin', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': 'Nuovo elemento (record)', @@ -379,6 +428,8 @@ 'Not Authorized': 'Not Authorized', 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'online designer': 'online designer', 'Open new app in new window': 'Open new app in new window', @@ -388,6 +439,7 @@ 'Or Get from URL:': 'Or Get from URL:', 'or import from csv file': 'oppure importa da file CSV', 'or provide app url:': "oppure fornisci url dell'applicazione:", +'Origin': 'Origin', 'Original/Translation': 'Originale/Traduzione', 'Overview': 'Overview', 'Overwrite installed app': 'sovrascrivi applicazione installata', @@ -396,7 +448,12 @@ 'Pack custom': 'Pack custom', 'pack plugin': 'crea pacchetto del plugin', 'PAM authenticated user, cannot change password here': 'utente autenticato tramite PAM, impossibile modificare password qui', +'Password': 'Password', 'password changed': 'password modificata', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -404,6 +461,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Please', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" cancellato', 'Plugin "%s" in application': 'Plugin "%s" nell\'applicazione', @@ -420,6 +478,7 @@ 'previous 100 rows': '100 righe precedenti', 'Private files': 'Private files', 'private files': 'private files', +'Profile updated': 'Profile updated', 'Project Progress': 'Project Progress', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -436,21 +495,37 @@ 'Rapid Search': 'Rapid Search', 'Record': 'Record', 'record': 'record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'il record non esiste', 'record id': 'ID del record', 'Record id': 'Record id', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'register': 'registrazione', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', +'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'reload': 'reload', 'Reload routes': 'Reload routes', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'rimozione codice compilato', 'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', 'Replace': 'Replace', 'Replace All': 'Replace All', 'Repository (%s)': 'Repository (%s)', 'request': 'request', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', +'Reset Password key': 'Reset Password key', 'Resolve Conflict file': 'File di risoluzione conflitto', 'response': 'response', 'restart': 'restart', @@ -488,6 +563,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'shell', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'sito', 'Size of cache:': 'Size of cache:', @@ -543,7 +619,9 @@ 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'These files are served without processing, your images go here', 'these files are served without processing, your images go here': 'questi files vengono serviti così come sono, le immagini vanno qui', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', 'This is the %(filename)s template': 'Questo è il template %(filename)s', @@ -556,6 +634,7 @@ 'Ticket ID': 'Ticket ID', 'Ticket Missing': 'Ticket Missing', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', +'Timestamp': 'Timestamp', 'TM': 'TM', 'to previous version.': 'torna a versione precedente', 'To create a plugin, name a file/folder plugin_[name]': 'Per creare un plugin, chiamare un file o cartella plugin_[nome]', @@ -571,6 +650,7 @@ 'try something like': 'prova qualcosa come', 'Try the mobile interface': 'Try the mobile interface', 'try view': 'try view', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'Impossibile controllare presenza di aggiornamenti', @@ -586,6 +666,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': 'non riesco a decodificare questo file CSV', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'impossibile disinstallare "%s"', 'unable to upgrade because "%s"': 'impossibile aggiornare perché "%s"', 'uncheck all': 'smarca tutti', @@ -608,10 +689,25 @@ 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Per costruire richieste (query) più complesse si usano (...)&(...) come "e" (AND), (...)|(...) come "o" (OR), e ~(...) come negazione (NOT).', 'Use an url:': 'Use an url:', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'variables', +'Verify Password': 'Verify Password', 'Version': 'Versione', 'Version %s.%s.%s %s (%s)': 'Version %s.%s.%s %s (%s)', 'Version %s.%s.%s (%s) %s': 'Version %s.%s.%s (%s) %s', @@ -634,6 +730,7 @@ 'web2py Recent Tweets': 'Tweets recenti per web2py', 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py aggiornato; prego riavviarlo', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': 'Benvenuto %s', 'Welcome to web2py': 'Benvenuto su web2py', 'Working...': 'Working...', @@ -642,8 +739,13 @@ 'Yes': 'Yes', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/ja.py b/applications/admin/languages/ja.py old mode 100755 new mode 100644 index 551c7951..497132c9 --- a/applications/admin/languages/ja.py +++ b/applications/admin/languages/ja.py @@ -16,8 +16,23 @@ '(something like "it-it")': '(例: "it-it")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': '検索中: **%s** ファイル', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'Abort': '中断', 'About': 'About', 'About application': 'アプリケーションについて', @@ -46,6 +61,7 @@ 'application is compiled and cannot be designed': 'application is compiled and cannot be designed', 'Application name:': 'アプリケーション名:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'are not used', 'are not used yet': 'are not used yet', 'Are you sure you want to delete file "%s"?': 'Are you sure you want to delete file "%s"?', @@ -60,6 +76,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': '注意: 安全(HTTPS)な接続でログインするかlocalhostで実行されている必要があります。', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': '注意: テストはスレッドセーフではないので複数のテストを同時に実行しないでください。', 'ATTENTION: you cannot edit the running application!': '注意: 実行中のアプリケーションは編集できません!', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available databases and tables': '利用可能なデータベースとテーブル一覧', 'Available Databases and Tables': 'Available Databases and Tables', @@ -87,6 +104,7 @@ 'Change Admin Password': 'Change Admin Password', 'Change admin password': '管理者パスワード変更', 'change editor settings': 'change editor settings', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': '全てを選択', 'Check for upgrades': '更新チェック', @@ -97,8 +115,10 @@ 'Clear CACHE?': 'Clear CACHE?', 'Clear DISK': 'Clear DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': '列をクリックしてトレースバックを展開', 'Click row to view a ticket': 'Click row to view a ticket', +'Client IP': 'Client IP', 'code': 'コード', 'Code listing': 'Code listing', 'collapse/expand all': '全て開閉する', @@ -128,7 +148,7 @@ 'Current response': 'Current response', 'Current session': 'Current session', 'currently running': '現在実行中', -'currently saved or': '現在保存されているデータ または', +'currently saved or': '現在保存されているデータ\u3000または', 'data uploaded': 'data uploaded', 'Database': 'Database', 'Database %s select': 'Database %s select', @@ -154,6 +174,7 @@ 'Deploy to PythonAnywhere': 'Deploy to PythonAnywhere', 'Deployment form': 'Deployment form', 'Deployment Interface': 'Deployment Interface', +'Description': 'Description', 'Description:': 'Description:', 'design': 'デザイン', 'Detailed traceback description': '詳細なトレースバック内容', @@ -180,6 +201,7 @@ 'Download layouts from repository': 'Download layouts from repository', 'download plugins': 'プラグインのダウンロード', 'Download plugins from repository': 'Download plugins from repository', +'E-mail': 'E-mail', 'Edit': '編集', 'edit all': '全て編集', 'Edit application': 'アプリケーションを編集', @@ -192,6 +214,9 @@ 'Editing Plural Forms File': 'Editing Plural Forms File', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': '有効', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -228,8 +253,10 @@ 'filter': 'フィルタ', 'Find Next': 'Find Next', 'Find Previous': 'Find Previous', +'First name': 'First name', 'Form has errors': 'Form has errors', 'Frames': 'フレーム', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'doctestsのない関数は自動的にテストをパスします。', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -245,6 +272,10 @@ 'Goto': 'Goto', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Help': 'ヘルプ', 'here': 'here', 'Hide/Show Translated strings': 'Hide/Show Translated strings', @@ -261,12 +292,14 @@ 'Import/Export': 'Import/Export', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'インクルード', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'index': 'index', 'inspect attributes': '引数の検査', 'Install': 'インストール', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'アプリケーション一覧', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': 'internal error', @@ -275,21 +308,31 @@ 'Invalid action': 'Invalid action', 'Invalid application name': 'Invalid application name', 'invalid circular reference': 'invalid circular reference', +'Invalid email': 'Invalid email', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'invalid password', +'Invalid password': 'Invalid password', 'invalid password.': 'invalid password.', 'Invalid Query': 'Invalid Query', 'invalid request': 'invalid request', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'invalid ticket', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': 'language file "%(filename)s" created/updated', 'Language files (static strings) updated': 'Language files (static strings) updated', 'languages': '言語', 'Languages': '言語', +'Last name': 'Last name', 'Last Revision': 'Last Revision', 'Last saved on:': '最終保存日時:', 'License for': 'License for', @@ -303,7 +346,11 @@ 'Local Apps': 'Local Apps', 'locals': 'ローカル', 'Locals##debug': 'Locals##debug', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'Login': 'ログイン', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': '管理画面へログイン', 'Login/Register': 'Login/Register', @@ -324,10 +371,12 @@ 'Modules': 'モジュール', 'modules': 'モジュール', 'Multi User Mode': 'Multi User Mode', +'Name': 'Name', 'new application "%s" created': 'new application "%s" created', 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': '新規アプリケーション作成ウィザード', 'New application wizard': '新規アプリケーション作成ウィザード', +'New password': 'New password', 'new plugin installed': '新しいプラグインがインストールされました', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': 'New Record', @@ -346,6 +395,8 @@ 'Not Authorized': 'Not Authorized', 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'online designer': 'オンラインデザイナー', 'Open new app in new window': 'Open new app in new window', @@ -354,6 +405,7 @@ 'or alternatively': 'or alternatively', 'Or Get from URL:': 'Or Get from URL:', 'or import from csv file': 'or import from csv file', +'Origin': 'Origin', 'Original/Translation': 'Original/Translation', 'Overview': 'Overview', 'Overwrite installed app': 'アプリケーションを上書き', @@ -361,7 +413,12 @@ 'Pack compiled': 'コンパイルデータのパッケージ化', 'Pack custom': 'Pack custom', 'pack plugin': 'プラグインのパッケージ化', +'Password': 'Password', 'password changed': 'password changed', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -369,6 +426,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Please', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin "%(plugin)s" deleted': '"%(plugin)s"プラグインは削除されました', 'Plugin "%s" in application': '"%s"プラグイン', @@ -384,6 +442,7 @@ 'previous %s rows': 'previous %s rows', 'Private files': 'Private files', 'private files': 'private files', +'Profile updated': 'Profile updated', 'Project Progress': 'Project Progress', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -399,19 +458,35 @@ 'Ram Cleared': 'Ram Cleared', 'Rapid Search': 'Rapid Search', 'Record': 'Record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'record does not exist', 'Record id': 'Record id', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'register': 'register', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', +'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'ルーティング再読み込み', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'コンパイルデータの削除', 'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', 'Replace': 'Replace', 'Replace All': 'Replace All', 'Repository (%s)': 'Repository (%s)', 'request': 'リクエスト', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', +'Reset Password key': 'Reset Password key', 'Resolve Conflict file': 'Resolve Conflict file', 'response': 'レスポンス', 'restart': '最初からやり直し', @@ -447,6 +522,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'shell', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'サイト', 'Size of cache:': 'Size of cache:', @@ -495,7 +571,9 @@ 'There are no views': 'ビューがありません', 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'これらのファイルは直接参照されます, ここに画像が入ります', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', "This page can commit your changes to an openshift app repo and push them to your cloud instance. This assumes that you've already created the application instance using the web2py skeleton and have that repo somewhere on a filesystem that this web2py instance can access. This functionality requires GitPython installed and on the python path of the runtime that web2py is operating in.": "This page can commit your changes to an openshift app repo and push them to your cloud instance. This assumes that you've already created the application instance using the web2py skeleton and have that repo somewhere on a filesystem that this web2py instance can access. This functionality requires GitPython installed and on the python path of the runtime that web2py is operating in.", @@ -507,6 +585,7 @@ 'Ticket ID': 'チケットID', 'Ticket Missing': 'Ticket Missing', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', +'Timestamp': 'Timestamp', 'to previous version.': '前のバージョンへ戻す。', 'To create a plugin, name a file/folder plugin_[name]': 'ファイル名/フォルダ名 plugin_[名称]としてプラグインを作成してください', 'To emulate a breakpoint programatically, write:': 'To emulate a breakpoint programatically, write:', @@ -519,6 +598,7 @@ 'try something like': 'try something like', 'Try the mobile interface': 'Try the mobile interface', 'try view': 'try view', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'Unable to check for upgrades', @@ -533,6 +613,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': 'unable to parse csv file', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'unable to uninstall "%s"', 'unable to upgrade because "%s"': 'unable to upgrade because "%s"', 'uncheck all': '全ての選択を解除', @@ -552,10 +633,25 @@ 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.', 'user': 'ユーザー', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': '変数', +'Verify Password': 'Verify Password', 'Version': 'バージョン', 'Versioning': 'バージョン管理', 'Views': 'ビュー', @@ -573,14 +669,20 @@ 'web2py Recent Tweets': '最近のweb2pyTweets', 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py upgraded; please restart it', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Working...': 'Working...', 'WSGI reference name': 'WSGI reference name', 'YES': 'はい', 'Yes': 'Yes', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/my-mm.py b/applications/admin/languages/my-mm.py old mode 100755 new mode 100644 index cffed4c4..70f7e25e --- a/applications/admin/languages/my-mm.py +++ b/applications/admin/languages/my-mm.py @@ -15,7 +15,23 @@ '(something like "it-it")': '(something like "it-it")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Searching: **%s** %%{file}': 'Searching: **%s** %%{file}', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'Abort': 'Abort', 'About': 'အကြောင်း', 'About application': 'About application', @@ -47,6 +63,7 @@ 'application is compiled and cannot be designed': 'application is compiled and cannot be designed', 'Application name:': 'Application name:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'အသုံးမပြုပါ', 'are not used yet': 'အသုံးမပြုသေးပါ', 'Are you sure you want to delete file "%s"?': 'Are you sure you want to delete file "%s"?', @@ -61,6 +78,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.', 'ATTENTION: you cannot edit the running application!': 'ATTENTION: you cannot edit the running application!', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available Databases and Tables': 'အသုံးပြုနိုင်သော ဒေတာဘေစ့်များနှင့် ဇယားများ', 'back': 'back', @@ -88,6 +106,7 @@ 'Change Admin Password': 'Change Admin Password', 'Change admin password': 'Change admin password', 'change editor settings': 'change editor settings', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'check all', 'Check for upgrades': 'Check for upgrades', @@ -98,6 +117,7 @@ 'Clear CACHE?': 'CACHE ကို ရှင်းလင်းမည်မှာ ဟုတ်ပါသလား။', 'Clear DISK': 'DISK ကို ရှင်းလင်းမည်။', 'Clear RAM': 'RAM ကို ရှင်းလင်းမည်။', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Click row to expand traceback', 'Click row to view a ticket': 'Click row to view a ticket', 'Client IP': 'Client IP', @@ -206,6 +226,9 @@ 'Editor': 'Editor', 'Email Address': 'Email Address', 'Email and SMS': 'အီးမေးလ်နှင့် SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'ဖွင့်ရန်', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -249,6 +272,7 @@ 'Forms and Validators': 'Forms and Validators', 'Frames': 'Frames', 'Free Applications': 'အခမဲ့ Applications', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Functions with no doctests will result in [passed] tests.', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -263,7 +287,10 @@ 'Goto': 'Goto', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Groups': 'အဖွဲ့များ', 'Hello World': 'မင်္ဂလာပါ ကမ္ဘာကြီး။', 'Help': 'အကူအညီ', @@ -283,11 +310,13 @@ 'Import/Export': 'သွင်းယူရန်/ထုတ်ယူရန်', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'includes', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'inspect attributes': 'inspect attributes', 'Install': 'Install', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'ထည့်သွင်းပြီး application များ', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': 'internal error', @@ -299,15 +328,23 @@ 'invalid circular reference': 'invalid circular reference', 'Invalid email': 'အီးမေးလ် ဖြည့်သွင်းမှုမှားနေသည်', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'invalid password', +'Invalid password': 'Invalid password', 'invalid password.': 'invalid password.', 'Invalid Query': 'Invalid Query', 'invalid request': 'invalid request', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'invalid ticket', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Is Active': 'Is Active', 'Key': 'Key', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill process', 'Language': 'ဘာသာစကား', @@ -332,7 +369,11 @@ 'Local Apps': 'Local Apps', 'locals': 'locals', 'Locals##debug': 'Locals##debug', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'Login': 'ဝင်ရောက်အသုံးပြုရန်', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'Login to the Administrative Interface', 'Login/Register': 'Login/Register', @@ -363,6 +404,7 @@ 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'New Application Wizard', 'New application wizard': 'New application wizard', +'New password': 'New password', 'new plugin installed': 'new plugin installed', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': 'မှတ်တမ်း အသစ်', @@ -382,6 +424,7 @@ 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', 'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'Online examples': 'အွန်လိုင်း နမူနာများ', 'Open new app in new window': 'Open new app in new window', @@ -402,7 +445,10 @@ 'pack plugin': 'pack plugin', 'Password': 'စကားဝှက်', 'password changed': 'password changed', +'Password changed': 'Password changed', "Password fields don't match": 'စကားဝှက်များ ကိုက်ညီမှု မရှိပါ', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -427,6 +473,7 @@ 'previous %s rows': 'previous %s rows', 'Private files': 'Private files', 'private files': 'private files', +'Profile updated': 'Profile updated', 'Project Progress': 'Project Progress', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -445,14 +492,24 @@ 'Rapid Search': 'Rapid Search', 'Recipes': 'Recipes', 'Record': 'မှတ်တမ်း', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'မှတ်တမ်း မရှိပါ', 'Record ID': 'Record ID', 'Record id': 'Record id', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'register': 'register', 'Register': 'မှတ်ပုံတင်ရန်', 'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Reload routes', 'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'Remove compiled', @@ -502,6 +559,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'shell', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'Site', 'Size of cache:': 'Size of cache:', @@ -556,6 +614,7 @@ 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'These files are served without processing, your images go here', 'This App': 'ဒီ App', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", 'This email already has an account': 'ဒီအီးမေးလ်တွင် အကောင့် ရှိပြီး ဖြစ်ပါသည်', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', @@ -583,6 +642,7 @@ 'Try the mobile interface': 'Try the mobile interface', 'try view': 'try view', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'Unable to check for upgrades', @@ -596,6 +656,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': 'unable to parse csv file', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'unable to uninstall "%s"', 'unable to upgrade because "%s"': 'unable to upgrade because "%s"', 'uncheck all': 'uncheck all', @@ -614,8 +675,21 @@ 'upload plugin file:': 'upload plugin file:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.', 'User': 'အသုံးပြုသူ', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'User ID', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'အသုံးပြုသူများ', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'variables', @@ -639,6 +713,7 @@ 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py upgraded; please restart it', 'Welcome': 'ကြိုဆိုပါ၏', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py!': 'web2py မှ ကြိုဆိုပါသည်။', 'Which called the function %s located in the file %s': 'Which called the function %s located in the file %s', 'Working...': 'ဆောင်ရွက်နေပါသည် ။ ။ ။', @@ -649,11 +724,16 @@ 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', 'You can modify this application and adapt it to your needs': 'သင် ဒီ application ကို ပြုပြင်မွမ်းမံနိုင်ပါသည်။ ထို့အပြင် သင့်လိုအပ်ချက်များနှင့် ကိုက်ညီစေရန် ပြုလုပ်နိုင်ပါသည်။', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'You visited the url %s': 'သင် လည်ပတ်ခဲ့သော URL %s', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', 'စကားဝှက် အသစ် တောင်းဆိုရန်': 'စကားဝှက် အသစ် တောင်းဆိုရန်', 'မှတ်ပုံတင်ရန်': 'မှတ်ပုံတင်ရန်', 'ဝင်ရောက်အသုံးပြုရန်': 'ဝင်ရောက်အသုံးပြုရန်', diff --git a/applications/admin/languages/nl.py b/applications/admin/languages/nl.py old mode 100755 new mode 100644 index 9998ee62..e24b6d2e --- a/applications/admin/languages/nl.py +++ b/applications/admin/languages/nl.py @@ -18,7 +18,23 @@ '(something like "it-it")': '(zoiets als "it-it")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': '@markmin: zoeken: **%s** %%{file}', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'Abort': 'Afbreken', 'About': 'Over', 'about': 'over', @@ -50,6 +66,7 @@ 'application is compiled and cannot be designed': 'applicatie is gecompileerd en kan niet worden ontworpen', 'Application name:': 'Applicatienaam:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'worden niet gebruikt', 'are not used yet': 'worden nog niet gebruikt', 'Are you sure you want to delete file "%s"?': 'Weet je zeker dat je bestand "%s" wilt verwijderen?', @@ -64,6 +81,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'LET OP: Login heeft beveiligde (HTTPS) verbinding nodig of moet draaien op localhost.', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'LET OP: TESTEN IS NIET THREAD SAFE EN PROBEER NIET MEERDERE TESTEN TEGELIJK TE DOEN.', 'ATTENTION: you cannot edit the running application!': 'LET OP: je kan de draaiende applicatie niet bewerken!', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available databases and tables': 'Beschikbare databases en tabellen', 'Available Databases and Tables': 'Available Databases and Tables', @@ -92,6 +110,7 @@ 'Change Admin Password': 'Change Admin Password', 'Change admin password': 'Verander admin wachtwoord', 'change editor settings': 'change editor settings', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'vink alles aan', 'Check for upgrades': 'Controleer voor upgrades', @@ -102,8 +121,10 @@ 'Clear CACHE?': 'Leeg CACHE?', 'Clear DISK': 'Leeg DISK', 'Clear RAM': 'Leeg RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Klik rij om traceback uit te klappen', 'Click row to view a ticket': 'Klik rij om ticket te bekijken', +'Client IP': 'Client IP', 'code': 'code', 'Code listing': 'Code listing', 'collapse/expand all': 'klap in/klap alles uit', @@ -165,6 +186,7 @@ 'Deploy to PythonAnywhere': 'Deploy to PythonAnywhere', 'Deployment form': 'Deploymentformulier', 'Deployment Interface': 'Deployment Interface', +'Description': 'Description', 'Description:': 'Description:', 'design': 'design', 'Detailed traceback description': 'Gedetailleerde traceback beschrijving', @@ -191,6 +213,7 @@ 'Download layouts from repository': 'Download layouts from repository', 'download plugins': 'download plugins', 'Download plugins from repository': 'Download plugins from repository', +'E-mail': 'E-mail', 'Edit': 'Bewerk', 'edit all': 'bewerk alles', 'Edit application': 'Bewerk applicatie', @@ -204,6 +227,9 @@ 'Editing Plural Forms File': 'Meervoudsvormenbestand aan het bewerken', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Zet aan', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -243,8 +269,10 @@ 'filter': 'filter', 'Find Next': 'Find Next', 'Find Previous': 'Find Previous', +'First name': 'First name', 'Form has errors': 'Form has errors', 'Frames': 'Frames', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Functies zonder doctests zullen resulteren in [passed] tests.', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -261,6 +289,10 @@ 'Goto': 'Ga naar', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Help': 'Help', 'here': 'here', 'Hide/Show Translated strings': 'Verberg/Toon Vertaalde strings', @@ -277,6 +309,7 @@ 'Import/Export': 'Import/Export', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'Binnen ontwikkeling, gebruik de default Rocket webserver die op het moment ondersteund wordt door deze debugger.', 'includes': 'includes', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'index': 'index', 'insert new': 'insert new', @@ -285,6 +318,7 @@ 'Install': 'Install', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'Geïnstalleerde applicaties', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interactie op %s regel %s', 'Interactive console': 'Interactieve console', 'internal error': 'interne error', @@ -294,24 +328,34 @@ 'Invalid application name': 'Invalid application name', 'invalid circual reference': 'ongeldige cirkelreferentie', 'invalid circular reference': 'Ongeldige circulaire referentie', +'Invalid email': 'Invalid email', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'ongeldig wachtwoord', +'Invalid password': 'Invalid password', 'invalid password.': 'ongeldig wachtwoord.', 'Invalid Query': 'Ongeldige Query', 'invalid request': 'ongeldige request', 'Invalid request': 'Invalid request', 'invalid request ': 'ongeldige request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'ongeldige tabelnamen (auth_* tabellen zijn al gedefinieerd)', 'invalid ticket': 'ongeldige ticket', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', 'Key bindings': 'Key bindings', 'Key bindings for ZenCoding Plugin': 'Key bindings voor ZenCoding Plugin', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill proces', 'language file "%(filename)s" created/updated': 'taalbestand "%(filename)s" gemaakt/geupdate', 'Language files (static strings) updated': 'Taalbestanden (statische strings) geupdate', 'languages': 'talen', 'Languages': 'Talen', +'Last name': 'Last name', 'Last Revision': 'Last Revision', 'Last saved on:': 'Laatst opgeslagen op:', 'License for': 'Licentie voor', @@ -326,8 +370,12 @@ 'Local Apps': 'Local Apps', 'locals': 'locals', 'Locals##debug': 'Locals##debug', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'Login': 'Login', 'login': 'Login', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'Login op de Administratieve Interface', 'Login/Register': 'Login/Register', @@ -355,10 +403,12 @@ 'Must include at least %s lowercase': 'Moet ten minste bevatten %s kleine letter', 'Must include at least %s of the following : %s': 'Moet ten minste bevatten %s van het volgende : %s', 'Must include at least %s uppercase': 'Moet ten minste bevatten %s hoofdletter', +'Name': 'Name', 'new application "%s" created': 'nieuwe applicatie "%s" gemaakt', 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'Nieuwe Applicatie Wizard', 'New application wizard': 'Nieuwe applicatie wizard', +'New password': 'New password', 'new plugin installed': 'nieuwe plugin geïnstalleerd', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': 'Nieuw Record', @@ -380,6 +430,8 @@ 'Not Authorized': 'Geen Rechten', 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Notitie: Bij een Github error status code 128, zorg ervoor dat het systeem en het account dat je aan het deployen bent een correspondeerde ssh key geconfigureerd heeft in het openshift account. ', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": 'Om op productie deze debugger te gebruiken, moet je webserver configureren om een proces en meerdere threads te gebruiken.', 'online designer': 'online designer', 'Open new app in new window': 'Open new app in new window', @@ -388,6 +440,7 @@ 'or alternatively': 'or alternatively', 'Or Get from URL:': 'Or Get from URL:', 'or import from csv file': 'of importeer van csv-bestand', +'Origin': 'Origin', 'Original/Translation': 'Oorspronkelijk/Vertaling', 'Overview': 'Overview', 'Overwrite installed app': 'Overschrijf geïnstalleerde app', @@ -396,7 +449,12 @@ 'Pack custom': 'Pack custom', 'pack plugin': 'pack plugin', 'PAM authenticated user, cannot change password here': 'PAM geauthenticeerde gebruiker, kan wachtwoord hier niet wijzigen', +'Password': 'Password', 'password changed': 'wachtwoord gewijzigd', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Pad naar appcfg.py', 'Path to local openshift repo root.': 'Pad naar lokale openshift repo root.', @@ -405,6 +463,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Alstublieft', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin': 'plugin', 'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" gedetecteerd', @@ -423,6 +482,7 @@ 'Previous Edit Point': 'Vorige Bewerkpunt', 'Private files': 'Privébestanden', 'private files': 'privébestanden', +'Profile updated': 'Profile updated', 'Project Progress': 'Projectvoortgang', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -439,20 +499,36 @@ 'Rapid Search': 'Rapid Search', 'Record': 'Record', 'record': 'record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'record bestaat niet', 'record id': 'record id', 'Record id': 'Record id', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', 'refresh': 'ververs', 'register': 'register', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', +'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Herlaadt routes', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'Verwijder gecompileerde', 'Removed Breakpoint on %s at line %s': 'Verwijder Breakpoint op %s op regel %s', 'Replace': 'Replace', 'Replace All': 'Replace All', 'Repository (%s)': 'Repository (%s)', 'request': 'request', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'vereist python-git, maar niet geïnstalleerd', +'Reset Password key': 'Reset Password key', 'resolve': 'oplossen', 'Resolve Conflict file': 'Los Conflictbestand op', 'response': 'antwoord', @@ -495,6 +571,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Zet Breakpoint op %s op regel %s: %s', 'shell': 'shell', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'signup': 'signup', 'signup_requested': 'signup_requested', 'Singular Form': 'Enkelvoudsvorm', @@ -548,7 +625,9 @@ 'There are no views': 'Er zijn geen views', 'These files are not served, they are only available from within your app': 'Deze bestanden worden niet geserveerd, ze zijn alleen beschikbaar vanuit binnen je app.', 'These files are served without processing, your images go here': 'Deze bestanden worden geserveerd zonder verwerkingen, je afbeeldingen horen hier', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": 'Deze debugger werkt misschien niet goed, of je hebt geen threaded webserver, of je gebruikt multiple daemon processen.', +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'Dit is een experimentele feature en heeft meer tests nodig. Downgraden op eigen risico.', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'Dit is een experimentele feature en heeft meer tests nodig. Upgraden op eigen risico.', 'This is the %(filename)s template': 'Dit is de %(filename)s template', @@ -563,6 +642,7 @@ 'Ticket Missing': 'Ticket Missing', 'tickets': 'tickets', 'Time in Cache (h:m:s)': 'Tijd in Cache (u:m:s)', +'Timestamp': 'Timestamp', 'to previous version.': 'naar vorige versie.', 'To create a plugin, name a file/folder plugin_[name]': 'Om een plugin te maken, neem een bestand/directory plugin_[naam]', 'To emulate a breakpoint programatically, write:': 'Om een breakpoint programmatische te emuleren, schrijf:', @@ -575,6 +655,7 @@ 'try something like': 'probeer zoiets als', 'Try the mobile interface': 'Try the mobile interface', 'try view': 'try view', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger commando hier en druk op Return (Enter) om het uit te voeren.', 'Type python statement in here and hit Return (Enter) to execute it.': 'Type python statement hier en druk op Return (Enter) om het uit te voeren.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', @@ -592,6 +673,7 @@ 'unable to install application "%(appname)s"': 'onmogelijk om applicatie "%(appname)s" te installeren', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': 'onmogelijk om csv-bestand te parsen', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'onmogelijk om te deïnstalleren "%s"', 'unable to upgrade because "%s"': 'onmogelijk om te upgraden omdat "%s"', 'unauthorized': 'niet geautoriseerd ', @@ -615,11 +697,26 @@ 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Gebruik (...)&(...) voor AND, (...)|(...) voor OR, en ~(...) voor NOT om meer complexe queries te maken.', 'user': 'gebruiker', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Het gebruik van de shell kan database locken voor andere gebruikers van deze app.', 'value not allowed': 'waarde is niet toegestaan', 'variables': 'variabelen', +'Verify Password': 'Verify Password', 'Version': 'Versie', 'Version %s.%s.%s (%s) %s': 'Versie %s.%s.%s (%s) %s', 'Versioning': 'Versionering', @@ -639,6 +736,7 @@ 'web2py Recent Tweets': 'web2py Recente Tweets', 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py geupgrade; herstart alstublieft', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Working...': 'Working...', 'Wrap with Abbreviation': 'Wrap met Afkorting', 'WSGI reference name': 'WSGI reference name', @@ -647,9 +745,14 @@ 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'Je kan ook een breakpoint zetten of verwijderen in het bewerkscherm met de Toggle Breakpoint-knop', 'You can inspect variables using the console bellow': 'Je kan je variabelen inspecteren in de console hieronder', 'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'Je hebt nog een poging om in te loggen voor je buitengesloten wordt.', 'you must specify a name for the uploaded application': 'je moet een naam specificeren voor de geuploade applicatie', 'You need to set up and reach a': 'Je moet het volgende opzetten en bereiken:', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Je applicatie zal geblokkeerd zijn tot je een actie button aanklikt (volgende, step, ga door, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/pl.py b/applications/admin/languages/pl.py old mode 100755 new mode 100644 index 8f943f33..bbc9060f --- a/applications/admin/languages/pl.py +++ b/applications/admin/languages/pl.py @@ -16,7 +16,23 @@ '(something like "it-it")': '(coś podobnego do "it-it")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': 'Searching: **%s** files', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': 'Nowa wersja web2py jest dostępna', 'A new version of web2py is available: %s': 'Nowa wersja web2py jest dostępna: %s', 'Abort': 'Abort', @@ -50,6 +66,7 @@ 'application is compiled and cannot be designed': 'aplikacja jest skompilowana i nie może być projektowana', 'Application name:': 'Application name:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'are not used', 'are not used yet': 'are not used yet', 'Are you sure you want to delete file "%s"?': 'Czy na pewno chcesz usunąć plik "%s"?', @@ -66,6 +83,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'UWAGA: Wymagane jest bezpieczne (HTTPS) połączenie lub połączenie z lokalnego adresu.', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'UWAGA: TESTOWANIE NIE JEST BEZPIECZNE W ŚRODOWISKU WIELOWĄTKOWYM, TAK WIĘC NIE URUCHAMIAJ WIELU TESTÓW JEDNOCZEŚNIE.', 'ATTENTION: you cannot edit the running application!': 'UWAGA: nie można edytować uruchomionych aplikacji!', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available databases and tables': 'Dostępne bazy danych i tabele', 'Available Databases and Tables': 'Available Databases and Tables', @@ -94,6 +112,7 @@ 'Change Admin Password': 'Change Admin Password', 'Change admin password': 'change admin password', 'change editor settings': 'change editor settings', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'zaznacz wszystko', 'Check for upgrades': 'check for upgrades', @@ -106,9 +125,11 @@ 'Clear RAM': 'Clear RAM', 'click here for online examples': 'kliknij aby przejść do interaktywnych przykładów', 'click here for the administrative interface': 'kliknij aby przejść do panelu administracyjnego', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Click row to expand traceback', 'Click row to view a ticket': 'Click row to view a ticket', 'click to check for upgrades': 'kliknij aby sprawdzić aktualizacje', +'Client IP': 'Client IP', 'code': 'code', 'Code listing': 'Code listing', 'collapse/expand all': 'collapse/expand all', @@ -168,6 +189,7 @@ 'Deploy to PythonAnywhere': 'Deploy to PythonAnywhere', 'Deployment form': 'Deployment form', 'Deployment Interface': 'Deployment Interface', +'Description': 'Description', 'Description:': 'Description:', 'design': 'projektuj', 'DESIGN': 'PROJEKTUJ', @@ -196,6 +218,7 @@ 'Download layouts from repository': 'Download layouts from repository', 'download plugins': 'download plugins', 'Download plugins from repository': 'Download plugins from repository', +'E-mail': 'E-mail', 'EDIT': 'EDYTUJ', 'Edit': 'edytuj', 'edit all': 'edit all', @@ -211,6 +234,9 @@ 'Editing Plural Forms File': 'Editing Plural Forms File', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Enable', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -250,8 +276,10 @@ 'filter': 'filter', 'Find Next': 'Find Next', 'Find Previous': 'Find Previous', +'First name': 'First name', 'Form has errors': 'Form has errors', 'Frames': 'Frames', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Funkcje bez doctestów będą dołączone do [zaliczonych] testów.', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -266,6 +294,10 @@ 'Goto': 'Goto', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Hello World': 'Witaj Świecie', 'Help': 'pomoc', 'here': 'here', @@ -284,6 +316,7 @@ 'Import/Export': 'Importuj/eksportuj', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'zawiera', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'insert new': 'wstaw nowy rekord tabeli', 'insert new %s': 'wstaw nowy rekord do tabeli %s', @@ -291,6 +324,7 @@ 'Install': 'install', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'Zainstalowane aplikacje', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': 'wewnętrzny błąd', @@ -299,15 +333,24 @@ 'Invalid action': 'Błędna akcja', 'Invalid application name': 'Invalid application name', 'invalid circular reference': 'invalid circular reference', +'Invalid email': 'Invalid email', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'błędne hasło', +'Invalid password': 'Invalid password', 'invalid password.': 'invalid password.', 'Invalid Query': 'Błędne zapytanie', 'invalid request': 'błędne zapytanie', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'błędny bilet', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': 'plik tłumaczeń "%(filename)s" został utworzony/uaktualniony', @@ -315,6 +358,7 @@ 'languages': 'pliki tłumaczeń', 'Languages': 'Tłumaczenia', 'languages updated': 'pliki tłumaczeń zostały uaktualnione', +'Last name': 'Last name', 'Last Revision': 'Last Revision', 'Last saved on:': 'Ostatnio zapisany:', 'License for': 'Licencja dla', @@ -328,8 +372,12 @@ 'Local Apps': 'Local Apps', 'locals': 'locals', 'Locals##debug': 'Locals##debug', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'Login': 'Zaloguj', 'login': 'zaloguj', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'Logowanie do panelu administracyjnego', 'Login/Register': 'Login/Register', @@ -350,10 +398,12 @@ 'Modules': 'Moduły', 'modules': 'moduły', 'Multi User Mode': 'Multi User Mode', +'Name': 'Name', 'new application "%s" created': 'nowa aplikacja "%s" została utworzona', 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'New Application Wizard', 'New application wizard': 'New application wizard', +'New password': 'New password', 'new plugin installed': 'nowa wtyczka została zainstalowana', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': 'Nowy rekord', @@ -373,6 +423,8 @@ 'Not Authorized': 'Not Authorized', 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'Open new app in new window': 'Open new app in new window', 'OpenShift Deployment Interface': 'OpenShift Deployment Interface', @@ -382,6 +434,7 @@ 'or import from csv file': 'lub zaimportuj z pliku csv', 'or provide app url:': 'or provide app url:', 'or provide application url:': 'lub podaj url aplikacji:', +'Origin': 'Origin', 'Original/Translation': 'Oryginał/tłumaczenie', 'Overview': 'Overview', 'Overwrite installed app': 'overwrite installed app', @@ -390,7 +443,12 @@ 'Pack custom': 'Pack custom', 'pack plugin': 'spakuj wtyczkę', 'PAM authenticated user, cannot change password here': 'PAM authenticated user, cannot change password here', +'Password': 'Password', 'password changed': 'password changed', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -398,6 +456,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Please', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin "%(plugin)s" deleted': 'wtyczka "%(plugin)s" została usunięta', 'Plugin "%s" in application': 'Wtyczka "%s" w aplikacji', @@ -414,6 +473,7 @@ 'previous 100 rows': 'poprzednie 100 wierszy', 'Private files': 'Private files', 'private files': 'private files', +'Profile updated': 'Profile updated', 'Project Progress': 'Project Progress', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -430,20 +490,36 @@ 'Rapid Search': 'Rapid Search', 'Record': 'Record', 'record': 'rekord', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'rekord nie istnieje', 'record id': 'ID rekordu', 'Record id': 'Record id', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'register': 'register', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', +'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Reload routes', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'usuń skompilowane', 'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', 'Replace': 'Replace', 'Replace All': 'Replace All', 'Repository (%s)': 'Repository (%s)', 'request': 'request', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', +'Reset Password key': 'Reset Password key', 'Resolve Conflict file': 'Rozwiąż konflikt plików', 'response': 'response', 'restart': 'restart', @@ -482,6 +558,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'powłoka', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'strona główna', 'Size of cache:': 'Size of cache:', @@ -536,7 +613,9 @@ 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'These files are served without processing, your images go here', 'these files are served without processing, your images go here': 'pliki obsługiwane bez interpretacji, to jest miejsce na Twoje obrazy', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', 'This is the %(filename)s template': 'To jest szablon %(filename)s', @@ -549,6 +628,7 @@ 'Ticket ID': 'Ticket ID', 'Ticket Missing': 'Ticket Missing', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', +'Timestamp': 'Timestamp', 'TM': 'TM', 'to previous version.': 'do poprzedniej wersji.', 'To create a plugin, name a file/folder plugin_[name]': 'Aby utworzyć wtyczkę, nazwij plik/katalog plugin_[nazwa]', @@ -564,6 +644,7 @@ 'try something like': 'spróbój czegos takiego jak', 'Try the mobile interface': 'Try the mobile interface', 'try view': 'try view', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'Nie można sprawdzić aktualizacji', @@ -580,6 +661,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': 'nie można sparsować pliku csv', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'nie można odinstalować "%s"', 'unable to upgrade because "%s"': 'unable to upgrade because "%s"', 'uncheck all': 'odznacz wszystko', @@ -603,10 +685,25 @@ 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Użyj (...)&(...) jako AND, (...)|(...) jako OR oraz ~(...) jako NOT do tworzenia bardziej skomplikowanych zapytań.', 'Use an url:': 'Use an url:', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'variables', +'Verify Password': 'Verify Password', 'Version': 'Wersja', 'Versioning': 'Versioning', 'versioning': 'versioning', @@ -626,6 +723,7 @@ 'web2py Recent Tweets': 'najnowsze tweety web2py', 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py upgraded; please restart it', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py': 'Witaj w web2py', 'Working...': 'Working...', 'WSGI reference name': 'WSGI reference name', @@ -633,8 +731,13 @@ 'Yes': 'Yes', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/plural-en.py b/applications/admin/languages/plural-en.py old mode 100755 new mode 100644 diff --git a/applications/admin/languages/plural-ru.py b/applications/admin/languages/plural-ru.py old mode 100755 new mode 100644 diff --git a/applications/admin/languages/plural-uk.py b/applications/admin/languages/plural-uk.py old mode 100755 new mode 100644 diff --git a/applications/admin/languages/pt-br.py b/applications/admin/languages/pt-br.py old mode 100755 new mode 100644 index 79695514..3960a3ea --- a/applications/admin/languages/pt-br.py +++ b/applications/admin/languages/pt-br.py @@ -15,8 +15,23 @@ '(something like "it-it")': '(algo como "it-it")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': 'Buscando: **%s** arquivos', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': 'Está disponível uma nova versão do web2py', 'A new version of web2py is available: %s': 'Está disponível uma nova versão do web2py: %s', 'Abort': 'Abort', @@ -50,6 +65,7 @@ 'application is compiled and cannot be designed': 'A aplicação está compilada e não pode ser modificada', 'Application name:': 'Nome da aplicação:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'are not used', 'are not used yet': 'are not used yet', 'Are you sure you want to delete file "%s"?': 'Tem certeza que deseja apagar o arquivo "%s"?', @@ -66,6 +82,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATENÇÃO: o login requer uma conexão segura (HTTPS) ou executar de localhost.', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATENÇÃO OS TESTES NÃO SÃO THREAD SAFE, NÃO EFETUE MÚLTIPLOS TESTES AO MESMO TEMPO.', 'ATTENTION: you cannot edit the running application!': 'ATENÇÃO: Não pode modificar a aplicação em execução!', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available databases and tables': 'Bancos de dados e tabelas disponíveis', 'Available Databases and Tables': 'Available Databases and Tables', @@ -96,6 +113,7 @@ 'Change admin password': 'mudar senha de administrador', 'change editor settings': 'change editor settings', 'Change Password': 'Mudar Senha', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'marcar todos', 'Check for upgrades': 'Verificar se existem atualizações', @@ -108,6 +126,7 @@ 'Clear RAM': 'Clear RAM', 'click here for online examples': 'clique para ver exemplos online', 'click here for the administrative interface': 'Clique aqui para acessar a interface administrativa', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Clique na linha para expandir o traceback', 'Click row to view a ticket': 'Click row to view a ticket', 'click to check for upgrades': 'clique aqui para verificar se existem atualizações', @@ -221,6 +240,9 @@ 'Editing Plural Forms File': 'Editing Plural Forms File', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Enable', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -263,6 +285,7 @@ 'First name': 'Nome', 'Form has errors': 'Form has errors', 'Frames': 'Frames', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Funções sem doctests resultarão em testes [aceitos].', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -277,7 +300,10 @@ 'Goto': 'Goto', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'ID do Grupo', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Hello World': 'Olá Mundo', 'Help': 'Ajuda', 'here': 'here', @@ -296,6 +322,7 @@ 'Import/Export': 'Importar/Exportar', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'inclui', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'insert new': 'inserir novo', 'insert new %s': 'inserir novo %s', @@ -303,6 +330,7 @@ 'Install': 'instalar', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'Aplicações instaladas', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': 'erro interno', @@ -313,14 +341,22 @@ 'invalid circular reference': 'invalid circular reference', 'Invalid email': 'E-mail inválido', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'senha inválida', +'Invalid password': 'Invalid password', 'invalid password.': 'invalid password.', 'Invalid Query': 'Consulta inválida', 'invalid request': 'solicitação inválida', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'ticket inválido', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': 'arquivo de idioma "%(filename)s" criado/atualizado', @@ -342,8 +378,12 @@ 'Local Apps': 'Local Apps', 'locals': 'locals', 'Locals##debug': 'Locals##debug', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'login': 'início de sessão', 'Login': 'Entrar', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'Entrar na interface adminitrativa', 'Login/Register': 'Login/Register', @@ -371,6 +411,7 @@ 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'Assistente para novas aplicações ', 'New application wizard': 'Assistente para novas aplicações', +'New password': 'New password', 'new plugin installed': 'novo plugin instalado', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': 'Novo registro', @@ -390,6 +431,8 @@ 'Not Authorized': 'Not Authorized', 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'Open new app in new window': 'Open new app in new window', 'OpenShift Deployment Interface': 'OpenShift Deployment Interface', @@ -410,6 +453,10 @@ 'PAM authenticated user, cannot change password here': 'usuário autenticado por PAM não pode alterar a senha aqui', 'Password': 'Senha', 'password changed': 'senha alterada', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -417,6 +464,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Please', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" apagado', 'Plugin "%s" in application': 'Plugin "%s" na aplicação', @@ -433,6 +481,7 @@ 'previous 100 rows': '100 registros anteriores', 'Private files': 'Private files', 'private files': 'private files', +'Profile updated': 'Profile updated', 'Project Progress': 'Project Progress', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -449,23 +498,37 @@ 'Rapid Search': 'Rapid Search', 'Record': 'Record', 'record': 'registro', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'o registro não existe', 'record id': 'id do registro', 'Record ID': 'ID do Registro', 'Record id': 'Record id', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'register': 'register', 'Register': 'Registrar-se', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Chave de registro', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Recarregar routes', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'Eliminar compilados', 'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', 'Replace': 'Replace', 'Replace All': 'Replace All', 'Repository (%s)': 'Repository (%s)', 'request': 'request', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', +'Reset Password key': 'Reset Password key', 'Resolve Conflict file': 'Arquivo de resolução de conflito', 'response': 'response', 'restart': 'reiniciar', @@ -504,6 +567,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'Terminal', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'Site', 'Size of cache:': 'Size of cache:', @@ -559,7 +623,9 @@ 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'Estes arquivos são servidos sem processamento, suas imagens ficam aqui', 'these files are served without processing, your images go here': 'Estes arquivos são servidos sem processamento, suas imagens ficam aqui', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', 'This is the %(filename)s template': 'Este é o template %(filename)s', @@ -588,6 +654,7 @@ 'try something like': 'tente algo como', 'Try the mobile interface': 'Experimente a interface para smartphones e tablets', 'try view': 'try view', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'Não é possível checar as atualizações', @@ -604,6 +671,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': 'não é possível analisar o arquivo CSV', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'não é possível desinstalar "%s"', 'unable to upgrade because "%s"': 'não é possível atualizar porque "%s"', 'uncheck all': 'desmarcar todos', @@ -626,11 +694,25 @@ 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) para AND, (...)|(...) para OR, e ~(...) para NOT, para criar consultas mais complexas.', 'Use an url:': 'Use uma url:', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'ID do Usuário', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'variáveis', +'Verify Password': 'Verify Password', 'Version': 'Versão', 'Versioning': 'Versionamento', 'versioning': 'versionamento', @@ -650,6 +732,7 @@ 'web2py Recent Tweets': 'Tweets Recentes de @web2py', 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py atualizado; favor reiniciar', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py': 'Bem-vindo ao web2py', 'Working...': 'Working...', 'WSGI reference name': 'WSGI reference name', @@ -657,8 +740,13 @@ 'Yes': 'Yes', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/pt.py b/applications/admin/languages/pt.py old mode 100755 new mode 100644 index e27c6eaf..f88d4c37 --- a/applications/admin/languages/pt.py +++ b/applications/admin/languages/pt.py @@ -16,9 +16,23 @@ '(something like "it-it")': '(algo como "it-it")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', '@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': 'Searching: **%s** files', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': 'Está disponível uma nova versão do web2py', 'A new version of web2py is available: %s': 'Está disponível uma nova versão do web2py: %s', 'Abort': 'Abort', @@ -52,6 +66,7 @@ 'application is compiled and cannot be designed': 'A aplicação está compilada e não pode ser modificada', 'Application name:': 'Nome da aplicação:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'não usadas', 'are not used yet': 'ainda não usadas', 'Are you sure you want to delete file "%s"?': 'Tem certeza que deseja apagar o arquivo "%s"?', @@ -68,6 +83,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATENÇÃO o login requer uma conexão segura (HTTPS) ou executar de localhost.', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATENÇÃO OS TESTES NÃO THREAD SAFE, NÃO EFETUE MÚLTIPLOS TESTES AO MESMO TEMPO.', 'ATTENTION: you cannot edit the running application!': 'ATENÇÃO: Não pode modificar a aplicação em execução!', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocompletar Código Python', 'Available databases and tables': 'Bancos de dados e tabelas disponíveis', 'Available Databases and Tables': 'Available Databases and Tables', @@ -98,6 +114,7 @@ 'Change admin password': 'mudar senha de administrador', 'change editor settings': 'mudar definições do editor', 'Change Password': 'Trocar Senha', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'marcar todos', 'Check for upgrades': 'checar por atualizações', @@ -110,6 +127,7 @@ 'Clear RAM': 'Clear RAM', 'click here for online examples': 'clique para ver exemplos online', 'click here for the administrative interface': 'Clique aqui para acessar a interface administrativa', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Clique em uma coluna para expandir o log do erro', 'Click row to view a ticket': 'Click row to view a ticket', 'click to check for upgrades': 'clique aqui para checar por atualizações', @@ -223,6 +241,9 @@ 'Editing Plural Forms File': 'Editing Plural Forms File', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Enable', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -265,6 +286,7 @@ 'First name': 'Nome', 'Form has errors': 'Form has errors', 'Frames': 'Frames', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Funções sem doctests resultarão em testes [aceitos].', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -279,7 +301,10 @@ 'Goto': 'Goto', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'ID do Grupo', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Hello World': 'Olá Mundo', 'Help': 'ajuda', 'here': 'here', @@ -298,6 +323,7 @@ 'Import/Export': 'Importar/Exportar', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'inclui', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'insert new': 'inserir novo', 'insert new %s': 'inserir novo %s', @@ -305,6 +331,7 @@ 'Install': 'instalar', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'Aplicações instaladas', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': 'erro interno', @@ -315,14 +342,22 @@ 'invalid circular reference': 'invalid circular reference', 'Invalid email': 'E-mail inválido', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'senha inválida', +'Invalid password': 'Invalid password', 'invalid password.': 'invalid password.', 'Invalid Query': 'Consulta inválida', 'invalid request': 'solicitação inválida', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'ticket inválido', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Atalhos de teclado', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': 'arquivo de linguagem "%(filename)s" criado/atualizado', @@ -344,8 +379,12 @@ 'Local Apps': 'Local Apps', 'locals': 'locals', 'Locals##debug': 'Locals##debug', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'login': 'inicio de sessão', 'Login': 'Entrar', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'Entrar na interface adminitrativa', 'Login/Register': 'Login/Register', @@ -373,6 +412,7 @@ 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'New Application Wizard', 'New application wizard': 'Assistente para novas aplicações ', +'New password': 'New password', 'new plugin installed': 'novo plugin instalado', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': 'Novo registro', @@ -393,6 +433,8 @@ 'Not Authorized': 'Not Authorized', 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'online designer': 'online designer', 'Open new app in new window': 'Open new app in new window', @@ -414,6 +456,10 @@ 'PAM authenticated user, cannot change password here': 'usuario autenticado por PAM, não pode alterar a senha por aqui', 'Password': 'Senha', 'password changed': 'senha alterada', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -421,6 +467,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Please', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" eliminado', 'Plugin "%s" in application': 'Plugin "%s" na aplicação', @@ -437,6 +484,7 @@ 'previous 100 rows': '100 registros anteriores', 'Private files': 'Private files', 'private files': 'private files', +'Profile updated': 'Profile updated', 'Project Progress': 'Project Progress', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -454,23 +502,37 @@ 'Read': 'Read', 'Record': 'Record', 'record': 'registro', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'o registro não existe', 'record id': 'id do registro', 'Record ID': 'ID do Registro', 'Record id': 'Record id', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'register': 'register', 'Register': 'Registrar-se', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Chave de registro', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Reload routes', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'eliminar compilados', 'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', 'Replace': 'Substituir', 'Replace All': 'Substituir Tudo', 'Repository (%s)': 'Repository (%s)', 'request': 'request', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', +'Reset Password key': 'Reset Password key', 'Resolve Conflict file': 'Arquivo de resolução de conflito', 'response': 'response', 'restart': 'restart', @@ -509,6 +571,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'Terminal', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'site', 'Size of cache:': 'Size of cache:', @@ -564,7 +627,9 @@ 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'These files are served without processing, your images go here', 'these files are served without processing, your images go here': 'Estes arquivos são servidos sem processamento, suas imagens ficam aqui', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', 'This is the %(filename)s template': 'Este é o template %(filename)s', @@ -593,6 +658,7 @@ 'try something like': 'tente algo como', 'Try the mobile interface': 'Try the mobile interface', 'try view': 'try view', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'Não é possível checar as atualizações', @@ -609,6 +675,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': 'não é possível analisar o arquivo CSV', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'não é possível instalar "%s"', 'unable to upgrade because "%s"': 'não é possível atualizar porque "%s"', 'uncheck all': 'desmarcar todos', @@ -632,11 +699,25 @@ 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) para AND, (...)|(...) para OR, y ~(...) para NOT, para criar consultas mais complexas.', 'Use an url:': 'Use uma url:', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'ID do Usuario', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'variáveis', +'Verify Password': 'Verify Password', 'Version': 'Versão', 'Versioning': 'Versioning', 'versioning': 'versionamento', @@ -656,6 +737,7 @@ 'web2py Recent Tweets': 'Tweets Recentes de @web2py', 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py atualizado; favor reiniciar', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py': 'Bem-vindo ao web2py', 'Working...': 'Working...', 'WSGI reference name': 'WSGI reference name', @@ -663,8 +745,13 @@ 'Yes': 'Yes', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/ro.py b/applications/admin/languages/ro.py old mode 100755 new mode 100644 index bc8c5a07..d0566f61 --- a/applications/admin/languages/ro.py +++ b/applications/admin/languages/ro.py @@ -23,7 +23,23 @@ '>': '>', '>=': '>=', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': 'Căutare: **%s** fișiere', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': 'O nouă versiune de web2py este disponibilă', 'A new version of web2py is available: %s': 'O nouă versiune de web2py este disponibilă: %s', 'Abort': 'Anulează', @@ -64,6 +80,7 @@ 'application is compiled and cannot be designed': 'aplicația este compilată și nu poate fi editată', 'Application name:': 'Nume aplicație:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'are not used', 'are not used yet': 'are not used yet', 'Are you sure you want to delete file "%s"?': 'Sigur ștergeți fișierul "%s"?', @@ -80,6 +97,7 @@ 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATENȚIE: Nu puteți efectua mai multe teste o dată deoarece lansarea în execuție a mai multor subpocese nu este sigură.', 'ATTENTION: you cannot edit the running application!': 'ATENȚIE: nu puteți edita o aplicație în curs de execuție!', 'Authentication': 'Autentificare', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available databases and tables': 'Baze de date și tabele disponibile', 'Available Databases and Tables': 'Available Databases and Tables', @@ -124,6 +142,7 @@ 'Clear CACHE?': 'Clear CACHE?', 'Clear DISK': 'Clear DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Clic pe linie pentru a extinde mesajul de trasabilitate', 'Click row to view a ticket': 'Click row to view a ticket', 'click to check for upgrades': 'Clic pentru a verifica dacă există upgrade-uri', @@ -250,6 +269,9 @@ 'Editor': 'Editor', 'Email Address': 'Email Address', 'Email and SMS': 'E-mail și SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Enable', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -301,6 +323,7 @@ 'Forms and Validators': 'Formulare și validatori', 'Frames': 'Frames', 'Free Applications': 'Aplicații gratuite', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Funcțiile fără doctests vor genera teste [trecute].', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -318,6 +341,7 @@ 'graph model': 'graph model', 'Graph Model': 'Graph Model', 'Group %(group_id)s created': 'Grup %(group_id)s creat', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'ID grup', 'Group uniquely assigned to user %(id)s': 'Grup asociat în mod unic utilizatorului %(id)s', 'Groups': 'Grupuri', @@ -342,6 +366,7 @@ 'Import/Export': 'Import/Export', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'include', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'Index': 'Index', 'index': 'index', @@ -351,6 +376,7 @@ 'Install': 'Instalează', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'Aplicații instalate', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': 'eroare internă', @@ -362,17 +388,24 @@ 'invalid circular reference': 'invalid circular reference', 'Invalid email': 'E-mail invalid', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'parolă invalidă', 'Invalid password': 'Parolă invalidă', 'invalid password.': 'invalid password.', 'Invalid Query': 'Interogare invalidă', 'invalid request': 'cerere invalidă', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'tichet invalid', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', 'Key bindings': 'Combinație taste', 'Key bindings for ZenCoding Plugin': 'Key bindings for ZenCoding Plugin', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': 'fișier de limbă "%(filename)s" creat/actualizat', @@ -399,10 +432,12 @@ 'locals': 'localizare', 'Locals##debug': 'Locals##debug', 'located in the file': 'prezentă în fișierul', +'Log In': 'Log In', 'Logged in': 'Logat', 'Logged out': 'Delogat', 'login': 'autentificare', 'Login': 'Autentificare', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'Logare interfață de administrare', 'Login/Register': 'Login/Register', @@ -486,7 +521,10 @@ 'pack plugin': 'pack plugin', 'Password': 'Parola', 'password changed': 'password changed', +'Password changed': 'Password changed', "Password fields don't match": 'Câmpurile de parolă nu se potrivesc', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -514,6 +552,7 @@ 'Private files': 'Private files', 'private files': 'private files', 'Profile': 'Profil', +'Profile updated': 'Profile updated', 'Project Progress': 'Progres proiect', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -534,15 +573,24 @@ 'Recipes': 'Rețete', 'Record': 'Record', 'record': 'înregistrare', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'înregistrare inexistentă', 'record id': 'id înregistrare', 'Record ID': 'ID înregistrare', 'Record id': 'Record id', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'register': 'înregistrare', 'Register': 'Înregistrare', 'Registration identifier': 'Identificator de autentificare', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Cheie înregistrare', +'Registration needs verification': 'Registration needs verification', 'Registration successful': 'Autentificare reușită', 'Reload routes': 'Reîncarcare rute', 'Remember me (for 30 days)': 'Ține-mă minte (timp de 30 de zile)', @@ -598,6 +646,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'line de commandă', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'site': 'site', 'Site': 'Site', @@ -661,7 +710,9 @@ 'These files are served without processing, your images go here': 'Aceste fișiere sunt servite fără procesare, fișierele imagine se pun aici', 'these files are served without processing, your images go here': 'aceste fișiere sunt servite fără procesare, imaginea se plasează acolo', 'This App': 'Această aplicație', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is a copy of the scaffolding application': 'Aceasta este o copie a aplicației schelet', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', @@ -693,6 +744,7 @@ 'Try the mobile interface': 'Try the mobile interface', 'try view': 'try view', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'Imposibil de verificat dacă există actualizări', @@ -708,6 +760,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': 'imposibil de analizat fișierul csv', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'imposibil de dezinstalat "%s"', 'unable to upgrade because "%s"': 'unable to upgrade because "%s"', 'uncheck all': 'decoșează tot', @@ -730,14 +783,21 @@ 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Folosiți (...)&(...) pentru ȘI, (...)|(...) pentru SAU, și ~(...) pentru NEGARE, pentru a crea interogări complexe.', 'user': 'utilizator', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': 'Utilizator %(id)s autentificat', 'User %(id)s Logged-out': 'Utilizator %(id)s delogat', 'User %(id)s Password changed': 'Parola utilizatorului %(id)s a fost schimbată', 'User %(id)s Password reset': 'Resetare parola utilizator %(id)s', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', 'User %(id)s Profile updated': 'Profil utilizator %(id)s actualizat', 'User %(id)s Registered': 'Utilizator %(id)s înregistrat', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'ID utilizator', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'value already in database or empty': 'Valoare existentă în baza de date sau vidă', @@ -767,6 +827,7 @@ 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py upgraded; please restart it', 'Welcome': 'Bine ați venit', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': 'Bine ați venit %s', 'Welcome to web2py': 'Bun venit la web2py', 'Welcome to web2py!': 'Bun venit la web2py!', @@ -780,9 +841,14 @@ 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', 'You can modify this application and adapt it to your needs': 'Puteți modifica și adapta aplicația nevoilor dvs.', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'You visited the url': 'Ați vizitat adresa', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/ru.py b/applications/admin/languages/ru.py old mode 100755 new mode 100644 index 88f9d703..0d8ba603 --- a/applications/admin/languages/ru.py +++ b/applications/admin/languages/ru.py @@ -16,7 +16,23 @@ '(something like "it-it")': '(наподобие "it-it")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': 'Найдено: **%s** %%{файл}', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': 'Доступна новая версия web2py', 'A new version of web2py is available: %s': 'Доступна новая версия web2py: %s', 'Abort': 'Отмена', @@ -51,6 +67,7 @@ 'application is compiled and cannot be designed': 'Приложение скомпилировано и дизайн не может быть изменен', 'Application name:': 'Название приложения:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'are not used', 'are not used yet': 'are not used yet', 'Are you sure you want to delete file "%s"?': 'Вы действительно хотите удалить файл "%s"?', @@ -69,6 +86,7 @@ 'ATTENTION: This is an experimental feature and it needs more testing.': 'ВНИМАНИЕ: Это экспериментальная возможность и требует тестирования.', 'ATTENTION: you cannot edit the running application!': 'ВНИМАНИЕ: Вы не можете редактировать работающее приложение!', 'Authentication': 'Аутентификация', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available databases and tables': 'Доступные базы данных и таблицы', 'Available Databases and Tables': 'Available Databases and Tables', @@ -101,6 +119,7 @@ 'change editor settings': 'change editor settings', 'Change Password': 'Изменить пароль', 'change password': 'изменить пароль', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'проверить все', 'Check for upgrades': 'проверить обновления', @@ -113,6 +132,7 @@ 'Clear RAM': 'Clear RAM', 'click here for online examples': 'нажмите здесь для онлайн примеров', 'click here for the administrative interface': 'нажмите здесь для интерфейса администратора', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Click row to expand traceback', 'Click row to view a ticket': 'Click row to view a ticket', 'click to check for upgrades': 'нажмите для проверки обновления', @@ -229,6 +249,9 @@ 'Editing Plural Forms File': 'Editing Plural Forms File', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Enable', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -273,6 +296,7 @@ 'First name': 'Имя', 'Form has errors': 'Form has errors', 'Frames': 'Frames', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Функции без doctest будут давать [прошел] в тестах.', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -289,7 +313,10 @@ 'Goto': 'Goto', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'ID группы', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Hello World': 'Привет, Мир', 'Help': 'Помощь', 'here': 'here', @@ -310,6 +337,7 @@ 'Import/Export': 'Импорт/Экспорт', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'включает', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'Index': 'Индекс', 'index': 'index', @@ -319,6 +347,7 @@ 'Install': 'Установить', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'Установленные приложения', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': 'внутренняя ошибка', @@ -329,16 +358,24 @@ 'invalid circular reference': 'invalid circular reference', 'Invalid email': 'Неверный email', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'неверный пароль', +'Invalid password': 'Invalid password', 'invalid password.': 'invalid password.', 'Invalid Query': 'Неверный запрос', 'invalid request': 'неверный запрос', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'неверный тикет', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', 'Key bindings': 'Комбинации клавиш', 'Key bindings for ZenConding Plugin': 'Комбинации клавиш для плагина ZenConding', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': 'Языковой файл "%(filename)s" создан/обновлен', @@ -362,8 +399,12 @@ 'locals': 'locals', 'Locals##debug': 'Locals##debug', 'located in the file': 'расположенный в файле', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'login': 'логин', 'Login': 'Логин', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'Вход в интерфейс администратора', 'Login/Register': 'Login/Register', @@ -394,6 +435,7 @@ 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'New Application Wizard', 'New application wizard': 'Мастер нового приложения', +'New password': 'New password', 'new plugin installed': 'new plugin installed', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': 'Новая запись', @@ -414,6 +456,8 @@ 'Not Authorized': 'Not Authorized', 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'Open new app in new window': 'Open new app in new window', 'OpenShift Deployment Interface': 'OpenShift Deployment Interface', @@ -433,6 +477,10 @@ 'pack plugin': 'Упаковать плагин', 'Password': 'Пароль', 'password changed': 'password changed', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -440,6 +488,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Please', +'please input your password again': 'please input your password again', 'please wait!': 'подождите, пожалуйста!', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" deleted', @@ -458,6 +507,7 @@ 'Previous Edit Point': 'Предыдущее место правки', 'Private files': 'Private files', 'private files': 'private files', +'Profile updated': 'Profile updated', 'Project Progress': 'Project Progress', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -474,21 +524,34 @@ 'Rapid Search': 'Rapid Search', 'Record': 'Record', 'record': 'запись', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'запись не существует', 'record id': 'id записи', 'Record ID': 'ID записи', 'Record id': 'Record id', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'Register': 'Зарегистрироваться', 'register': 'зарегистрироваться', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Ключ регистрации', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Reload routes', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'Удалить скомпилированное', 'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', 'Replace': 'Replace', 'Replace All': 'Replace All', 'Repository (%s)': 'Repository (%s)', 'request': 'request', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', 'Reset Password key': 'Сброс пароля', @@ -531,6 +594,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'shell', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'сайт', 'Size of cache:': 'Size of cache:', @@ -593,7 +657,9 @@ 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'Эти файлы обслуживаются без обработки, ваши изображения попадут сюда', 'these files are served without processing, your images go here': 'Эти файлы обслуживаются без обработки, ваши изображения попадут сюда', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is a copy of the scaffolding application': 'Это копия сгенерированного приложения', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', @@ -623,6 +689,7 @@ 'try something like': 'попробовать что-либо вида', 'Try the mobile interface': 'Try the mobile interface', 'try view': 'try view', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'Невозможно проверить обновления', @@ -638,6 +705,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': 'невозможно разобрать файл csv', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'невозможно удалить "%s"', 'unable to upgrade because "%s"': 'unable to upgrade because "%s"', 'uncheck all': 'снять выбор всего', @@ -662,11 +730,25 @@ 'Use an url:': 'Используйте url:', 'user': 'пользователь', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'ID пользователя', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'переменные', +'Verify Password': 'Verify Password', 'Version': 'Версия', 'Versioning': 'Versioning', 'versioning': 'версии', @@ -687,6 +769,7 @@ 'web2py Recent Tweets': 'последние твиты по web2py', 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py upgraded; please restart it', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': 'Добро пожаловать, %s', 'Welcome to web2py': 'Добро пожаловать в web2py', 'Which called the function': 'Который вызвал функцию', @@ -700,9 +783,14 @@ 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', 'You can modify this application and adapt it to your needs': 'Вы можете изменить это приложение и подогнать под свои нужды', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'You visited the url': 'Вы посетили URL', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/sl.py b/applications/admin/languages/sl.py old mode 100755 new mode 100644 index c6767b97..02655b3d --- a/applications/admin/languages/sl.py +++ b/applications/admin/languages/sl.py @@ -16,7 +16,23 @@ '(something like "it-it")': '(nekaj kot "sl-SI" ali samo "sl")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': 'Iskanje: **%s** datoteke', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': 'Nova različica web2py je na voljo', 'A new version of web2py is available: %s': 'Nova različica web2py je na voljo: %s', 'Abort': 'Preklic', @@ -51,6 +67,7 @@ 'application is compiled and cannot be designed': 'aplikacija je prevedena in je ne morete popravljati', 'Application name:': 'Ime aplikacije:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'are not used', 'are not used yet': 'are not used yet', 'Are you sure you want to delete file "%s"?': 'Ali res želite pobrisati datoteko "%s"?', @@ -69,6 +86,7 @@ 'ATTENTION: This is an experimental feature and it needs more testing.': 'POZOR: To je preizkusni fazi in potrebuje več testiranja.', 'ATTENTION: you cannot edit the running application!': 'POZOR: Ne morete urejati aplikacije, ki že teče!', 'Authentication': 'Avtentikacija', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available databases and tables': 'Podatkovne baze in tabele', 'Available Databases and Tables': 'Available Databases and Tables', @@ -101,6 +119,7 @@ 'change editor settings': 'change editor settings', 'Change Password': 'Spremeni geslo', 'change password': 'spremeni geslo', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'označi vse', 'Check for upgrades': 'Preveri za posodobitve', @@ -113,6 +132,7 @@ 'Clear RAM': 'Clear RAM', 'click here for online examples': 'kliknite za spletne primere', 'click here for the administrative interface': 'kliknite za skrbniški vmesnik', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Kliknite vrstico da razširite sledenje', 'Click row to view a ticket': 'Kliknite vrstico za ogled listka', 'click to check for upgrades': 'kliknite za preverjanje nadgradenj', @@ -229,6 +249,9 @@ 'Editing Plural Forms File': 'Editing Plural Forms File', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Enable', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -273,6 +296,7 @@ 'First name': 'Ime', 'Form has errors': 'Form has errors', 'Frames': 'Okvirji', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Funkcije brez doctest bodo opravile teste brez preverjanja.', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -289,7 +313,10 @@ 'Goto': 'Goto', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'ID skupine', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Hello World': 'Pozdravljen, Svet!', 'Help': 'Pomoč', 'here': 'here', @@ -310,6 +337,7 @@ 'Import/Export': 'Uvoz/Izvoz', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'vključuje', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'Index': 'Indeks', 'index': 'indeks', @@ -319,6 +347,7 @@ 'Install': 'Namesti', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'Nameščene aplikacije', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': 'notranja napaka', @@ -329,16 +358,24 @@ 'invalid circular reference': 'invalid circular reference', 'Invalid email': 'Napačen e-naslov', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'napačno geslo', +'Invalid password': 'Invalid password', 'invalid password.': 'invalid password.', 'Invalid Query': 'Napačno povpraševanje', 'invalid request': 'napačna zahteva', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'napačen kartonček', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', 'Key bindings': 'Povezave tipk', 'Key bindings for ZenConding Plugin': 'Povezave tipk za ZenConding vtičnik', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': 'jezikovna datoteka "%(filename)s" ustvarjena/posodobljena', @@ -362,8 +399,12 @@ 'locals': 'locals', 'Locals##debug': 'Locals##debug', 'located in the file': 'se nahaja v datoteki', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'login': 'prijava', 'Login': 'Prijava', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'Prijava v skrbniški vmesnik', 'Login/Register': 'Login/Register', @@ -394,6 +435,7 @@ 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'Čarovnik za novo aplikacijo', 'New application wizard': 'Čarovnik za novo aplikacijo', +'New password': 'New password', 'new plugin installed': 'new plugin installed', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': 'Nov zapis', @@ -414,6 +456,8 @@ 'Not Authorized': 'Not Authorized', 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'Open new app in new window': 'Open new app in new window', 'OpenShift Deployment Interface': 'OpenShift Deployment Interface', @@ -433,6 +477,10 @@ 'pack plugin': 'zapakiraj vtičnik', 'Password': 'Geslo', 'password changed': 'password changed', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -440,6 +488,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Please', +'please input your password again': 'please input your password again', 'please wait!': 'Prosim počakajte!', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" deleted', @@ -458,6 +507,7 @@ 'Previous Edit Point': 'Prejšnja točka urejanja', 'Private files': 'Private files', 'private files': 'private files', +'Profile updated': 'Profile updated', 'Project Progress': 'Project Progress', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -474,21 +524,34 @@ 'Rapid Search': 'Rapid Search', 'Record': 'Record', 'record': 'zapis', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'zapis ne obstaja', 'record id': 'id zapisa', 'Record ID': 'ID zapisa', 'Record id': 'Record id', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'Register': 'Registracija', 'register': 'registracija', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Registracijski ključ', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Ponovno naloži preusmeritve', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'Odstrani prevedeno', 'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', 'Replace': 'Replace', 'Replace All': 'Replace All', 'Repository (%s)': 'Repository (%s)', 'request': 'zahteva', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', 'Reset Password key': 'Ponastavi ključ gesla', @@ -530,6 +593,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'lupina', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'Spletišče', 'Size of cache:': 'Size of cache:', @@ -592,7 +656,9 @@ 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'Te datoteke so poslane brez obdelave. Vaše slike shranite tu.', 'these files are served without processing, your images go here': 'te datoteke so poslane brez posredovanja in obdelave, svoje slike shranite tu', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is a copy of the scaffolding application': 'To je kopija okvirne aplikacije', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', @@ -622,6 +688,7 @@ 'try something like': 'poskusite na primer', 'Try the mobile interface': 'Try the mobile interface', 'try view': 'try view', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'Ne morem preveriti posodobitev', @@ -637,6 +704,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': 'ne morem obdelati csv datoteke', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'ne morem odstraniti "%s"', 'unable to upgrade because "%s"': 'unable to upgrade because "%s"', 'uncheck all': 'odznači vse', @@ -661,11 +729,25 @@ 'Use an url:': 'Uporabite URL:', 'user': 'uporabnik', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'ID uporabnika', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'spremenljivke', +'Verify Password': 'Verify Password', 'Version': 'Različica', 'Versioning': 'Versioning', 'versioning': 'različice', @@ -686,6 +768,7 @@ 'web2py Recent Tweets': 'zadnji tviti na web2py', 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py upgraded; please restart it', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': 'Dobrodošli, %s', 'Welcome to web2py': 'Dobrodošli v web2py', 'Which called the function': 'Ki je klical funkcijo', @@ -699,9 +782,14 @@ 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', 'You can modify this application and adapt it to your needs': 'Lahko spremenite to aplikacijo in jo prilagodite vašim potrebam', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'You visited the url': 'Obiskali ste URL', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/sr-cr.py b/applications/admin/languages/sr-cr.py old mode 100755 new mode 100644 index 882819b4..0eab6f13 --- a/applications/admin/languages/sr-cr.py +++ b/applications/admin/languages/sr-cr.py @@ -16,7 +16,23 @@ '(something like "it-it")': '(на примјер "it-it")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', '@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(датотека **gluon/contrib/plural_rules/%s.py** није пронађена)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Searching: **%s** %%{file}': 'Searching: **%s** %%{file}', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'Abort': 'Одустани', 'About': 'Информације', 'About application': 'О апликацији', @@ -45,6 +61,7 @@ 'application is compiled and cannot be designed': 'апликација је компајлирана и не може се даље уређивати', 'Application name:': 'Назив апликације:', 'Application updated via git pull': 'Апликација ажурирана преко git pull', +'Apply changes': 'Apply changes', 'are not used': 'није кориштено', 'are not used yet': 'није још кориштено', 'Are you sure you want to delete file "%s"?': 'Да ли сте сигурни да желите избрисати датотеку "%s"?', @@ -59,6 +76,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.', 'ATTENTION: you cannot edit the running application!': 'ПАЖЊА: не можете уређивати покренуту апликацију!', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available Databases and Tables': 'Доступне базе података и табеле', 'back': 'назад', @@ -85,6 +103,7 @@ 'Change Admin Password': 'Промијени лозинку администратора', 'Change admin password': 'Промијени лозинку администратора', 'change editor settings': 'change editor settings', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'check all', 'Check for upgrades': 'Провјери могућност надоградње', @@ -95,8 +114,10 @@ 'Clear CACHE?': 'Обриши CACHE?', 'Clear DISK': 'Обриши DISK', 'Clear RAM': 'Обриши RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Click row to expand traceback', 'Click row to view a ticket': 'Click row to view a ticket', +'Client IP': 'Client IP', 'code': 'код', 'Code listing': 'Приказ кода', 'collapse/expand all': 'сакрити/приказати све', @@ -153,6 +174,7 @@ 'Deploy to PythonAnywhere': 'Постави на PythonAnywhere', 'Deployment form': 'Deployment form', 'Deployment Interface': 'Deployment Interface', +'Description': 'Description', 'Description:': 'Description:', 'design': 'design', 'Detailed traceback description': 'Detailed traceback description', @@ -179,6 +201,7 @@ 'Download layouts from repository': 'Download layouts from repository', 'download plugins': 'преузми помоћне модуле', 'Download plugins from repository': 'Преузми помоћне модуле из репозиторијум', +'E-mail': 'E-mail', 'Edit': 'Уреди', 'edit all': 'уреди све', 'Edit application': 'Уреди апликацију', @@ -192,6 +215,9 @@ 'Editing Plural Forms File': 'Editing Plural Forms File', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Enable', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -229,8 +255,10 @@ 'filter': 'филтер', 'Find Next': 'Пронађи сљедећи', 'Find Previous': 'Пронађи претходни', +'First name': 'First name', 'Form has errors': 'Form has errors', 'Frames': 'Frames', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Functions with no doctests will result in [passed] tests.', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -247,6 +275,10 @@ 'Goto': 'Goto', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Help': 'Помоћ', 'here': 'here', 'Hide/Show Translated strings': 'Сакрити/Приказати преведене ријечи', @@ -263,11 +295,13 @@ 'Import/Export': 'инсталационо окружење', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'укључује', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'inspect attributes': 'inspect attributes', 'Install': 'Инсталирај', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'Инсталиране апликације', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': 'унутрашња грешка', @@ -276,23 +310,33 @@ 'Invalid action': 'Invalid action', 'Invalid application name': 'Invalid application name', 'invalid circular reference': 'invalid circular reference', +'Invalid email': 'Invalid email', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'Неважећа лозинка', +'Invalid password': 'Invalid password', 'invalid password.': 'неважећа лозинка.', 'Invalid Query': 'Погрешан упит', 'invalid request': 'invalid request', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'погрешан тикет', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', 'Key bindings': 'Пречице', 'Key bindings for ZenCoding Plugin': 'Пречице за ZenCoding Plugin', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Пречице на тастатури', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': 'језичка датотека "%(filename)s" је креирана/ажурирана', 'Language files (static strings) updated': 'Језичке датотеке су ажуриране', 'languages': 'језици', 'Languages': 'Језици', +'Last name': 'Last name', 'Last Revision': 'Last Revision', 'Last saved on:': 'Посљедња измјена:', 'License for': 'Лиценца за', @@ -306,7 +350,11 @@ 'Local Apps': 'Локалне апликације', 'locals': 'locals', 'Locals##debug': 'Locals##debug', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'Login': 'Пријава', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'Пријава за административно окружење', 'Login/Register': 'Пријава/Регистрација', @@ -329,10 +377,12 @@ 'Modules': 'Модули', 'modules': 'модули', 'Multi User Mode': 'Вишекориснички режим рада', +'Name': 'Name', 'new application "%s" created': 'нова апликација "%s" је креирана', 'new application "%s" imported': 'нова апликација "%s" је увежена', 'New Application Wizard': 'Чаробњак за нове апликације', 'New application wizard': 'Чаробњак за нове апликације', +'New password': 'New password', 'new plugin installed': 'нови помоћни модул је инсталиран', 'New plugin installed: %s': 'Инсталиран нови помоћни модул: %s', 'New Record': 'Нови запис', @@ -353,6 +403,8 @@ 'Not Authorized': 'Немате овлашћење', 'Not supported': 'Није подржано', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'online designer': 'онлајн дизајнер', 'Open new app in new window': 'Отвори нову апликацију у новом прозору', @@ -361,6 +413,7 @@ 'or alternatively': 'или алтернативно', 'Or Get from URL:': 'Or Get from URL:', 'or import from csv file': 'или увези помоћу csv датотеке', +'Origin': 'Origin', 'Original/Translation': 'Оргинал/Превод', 'Overview': 'Преглед', 'Overwrite installed app': 'Замјени већ постојећу апликацију', @@ -368,7 +421,12 @@ 'Pack compiled': 'Запакуј компајлирано', 'Pack custom': 'Прилагођено паковање', 'pack plugin': 'запакуј помоћни модул', +'Password': 'Password', 'password changed': 'лозинка је промијењена', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Претходне корекције', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -376,6 +434,7 @@ 'Permission': 'Дозвола', 'Permissions': 'Дозволе', 'Please': 'Молим', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin "%(plugin)s" deleted': 'помоћни модул "%(plugin)s" је избрисан', 'Plugin "%s" in application': 'Помоћни модул "%s" у апликацији', @@ -392,6 +451,7 @@ 'Previous Edit Point': 'Previous Edit Point', 'Private files': 'Приватне датотеке', 'private files': 'приватне датотеке', +'Profile updated': 'Profile updated', 'Project Progress': 'Напредак пројекта', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -407,19 +467,35 @@ 'Ram Cleared': 'Ram Cleared', 'Rapid Search': 'Rapid Search', 'Record': 'Record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'record does not exist', 'Record id': 'Record id', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'register': 'register', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', +'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Обнови преусмјерења', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'Уклони компајлирано', 'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', 'Replace': 'Замијени', 'Replace All': 'Замијени све', 'Repository (%s)': 'Репозиторијум (%s)', 'request': 'request', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', +'Reset Password key': 'Reset Password key', 'Resolve Conflict file': 'Resolve Conflict file', 'response': 'response', 'restart': 'restart', @@ -456,6 +532,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'shell', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'Сајт', 'Size of cache:': 'Size of cache:', @@ -504,7 +581,9 @@ 'There are no views': 'Нема преводиоца', 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'These files are served without processing, your images go here', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', "This page can commit your changes to an openshift app repo and push them to your cloud instance. This assumes that you've already created the application instance using the web2py skeleton and have that repo somewhere on a filesystem that this web2py instance can access. This functionality requires GitPython installed and on the python path of the runtime that web2py is operating in.": "This page can commit your changes to an openshift app repo and push them to your cloud instance. This assumes that you've already created the application instance using the web2py skeleton and have that repo somewhere on a filesystem that this web2py instance can access. This functionality requires GitPython installed and on the python path of the runtime that web2py is operating in.", @@ -516,6 +595,7 @@ 'Ticket ID': 'Тикет ID', 'Ticket Missing': 'Недостаје тикет', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', +'Timestamp': 'Timestamp', 'to previous version.': 'на претходну верзију.', 'To create a plugin, name a file/folder plugin_[name]': 'To create a plugin, name a file/folder plugin_[name]', 'To emulate a breakpoint programatically, write:': 'To emulate a breakpoint programatically, write:', @@ -528,6 +608,7 @@ 'try something like': 'на примјер', 'Try the mobile interface': 'Пробај мобилнo окружење', 'try view': 'пробај приказ', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'Не могу да провјерим могућност надоградње', @@ -541,6 +622,7 @@ 'Unable to download the list of plugins': 'Не могу да преузмем списак помоћних модула', 'unable to install plugin "%s"': 'не могу да инсталирам помоћни модул "%s"', 'unable to parse csv file': 'не могу да расчланим csv датотеку', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'не могу да деинсталирам "%s"', 'unable to upgrade because "%s"': 'не могу да ажуримам због "%s"', 'uncheck all': 'uncheck all', @@ -559,10 +641,25 @@ 'upload plugin file:': 'преузми датотеку помоћног модула:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.', 'User': 'Корисник', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', 'Username': 'Корисничко име', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Корисници', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'variables', +'Verify Password': 'Verify Password', 'Version': 'Верзија', 'Version %s.%s.%s (%s) %s': 'Верзија %s.%s.%s (%s) %s', 'Versioning': 'Креирање верзија', @@ -581,6 +678,7 @@ 'web2py Recent Tweets': 'web2py Recent Tweets', 'web2py upgrade': 'web2py надоградња', 'web2py upgraded; please restart it': 'web2py је ажуриран; молим да рестартујете', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Working...': 'Извршавам...', 'Wrap with Abbreviation': 'Wrap with Abbreviation', 'WSGI reference name': 'WSGI reference name', @@ -588,8 +686,13 @@ 'Yes': 'Да', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/sr-lt.py b/applications/admin/languages/sr-lt.py old mode 100755 new mode 100644 index a807b8cc..4acf2c2f --- a/applications/admin/languages/sr-lt.py +++ b/applications/admin/languages/sr-lt.py @@ -16,8 +16,23 @@ '(something like "it-it")': '(na primjer "it-it")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', '@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(datoteka **gluon/contrib/plural_rules/%s.py** nije pronađena)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Searching: **%s** %%{file}': 'Searching: **%s** %%{file}', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'Abort': 'Odustani', 'About': 'Informacije', 'About application': 'O aplikaciji', @@ -46,6 +61,7 @@ 'application is compiled and cannot be designed': 'aplikacija je kompajlirana i ne može se dalje uređivati', 'Application name:': 'Naziv aplikacije:', 'Application updated via git pull': 'Aplikacija ažurirana preko git pull', +'Apply changes': 'Apply changes', 'are not used': 'nije korišteno', 'are not used yet': 'nije još korišteno', 'Are you sure you want to delete file "%s"?': 'Da li ste sigurni da želite izbrisati datoteku "%s"?', @@ -60,6 +76,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.', 'ATTENTION: you cannot edit the running application!': 'ATTENTION: ne možete uređivati pokrenutu aplikaciju!', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available Databases and Tables': 'Dostupne baze podataka i tabele', 'back': 'nazad', @@ -86,6 +103,7 @@ 'Change admin password': 'Promijeni lozinku administratora', 'Change Admin Password': 'Promijeni lozinku administratora', 'change editor settings': 'change editor settings', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'check all', 'Check for upgrades': 'Provjeri mogućnost nadogradnje', @@ -96,8 +114,10 @@ 'Clear CACHE?': 'Obriši CACHE?', 'Clear DISK': 'Obriši DISK', 'Clear RAM': 'Obriši RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Click row to expand traceback', 'Click row to view a ticket': 'Click row to view a ticket', +'Client IP': 'Client IP', 'code': 'kod', 'Code listing': 'Prikaz koda', 'collapse/expand all': 'sakriti/prikazati sve', @@ -154,6 +174,7 @@ 'Deploy to PythonAnywhere': 'Postavi na PythonAnywhere', 'Deployment form': 'Deployment form', 'Deployment Interface': 'Deployment Interface', +'Description': 'Description', 'Description:': 'Description:', 'design': 'design', 'Detailed traceback description': 'Detailed traceback description', @@ -180,6 +201,7 @@ 'Download layouts from repository': 'Download layouts from repository', 'download plugins': 'preuzmi pomoćne module', 'Download plugins from repository': 'Preuzmi pomoćne module iz repozitorijum', +'E-mail': 'E-mail', 'Edit': 'Uredi', 'edit all': 'uredi sve', 'Edit application': 'Uredi aplikaciju', @@ -193,6 +215,9 @@ 'Editing Plural Forms File': 'Editing Plural Forms File', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Enable', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -230,8 +255,10 @@ 'filter': 'filter', 'Find Next': 'Pronađi sljedeći', 'Find Previous': 'Pronađi prethodni', +'First name': 'First name', 'Form has errors': 'Form has errors', 'Frames': 'Frames', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Functions with no doctests will result in [passed] tests.', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -248,6 +275,10 @@ 'Goto': 'Goto', 'Graph Model': 'Graph Model', 'graph model': 'graph model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Help': 'Pomoć', 'here': 'here', 'Hide/Show Translated strings': 'Sakriti/Prikazati prevedene riječi', @@ -264,11 +295,13 @@ 'Import/Export': 'Uvoz/Izvoz', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'uključuje', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'inspect attributes': 'inspect attributes', 'Install': 'Instaliraj', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'Instalirane aplikacije', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': 'unutrašnja greška', @@ -277,23 +310,33 @@ 'Invalid action': 'Invalid action', 'Invalid application name': 'Invalid application name', 'invalid circular reference': 'invalid circular reference', +'Invalid email': 'Invalid email', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'Nevažeća lozinka', +'Invalid password': 'Invalid password', 'invalid password.': 'nevažeća lozinka.', 'Invalid Query': 'Pogrešan upit', 'invalid request': 'invalid request', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'pogrešan tiket', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', 'Key bindings': 'Prečice', 'Key bindings for ZenCoding Plugin': 'Prečice za ZenCoding Plugin', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Prečice na tastaturi', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': 'jezička datoteka "%(filename)s" je kreirana/ažurirana', 'Language files (static strings) updated': 'Jezičke datoteke su ažurirane', 'Languages': 'Jezici', 'languages': 'jezici', +'Last name': 'Last name', 'Last Revision': 'Last Revision', 'Last saved on:': 'Posljednja izmjena:', 'License for': 'Licenca za', @@ -307,7 +350,11 @@ 'Local Apps': 'Lokalne aplikacije', 'locals': 'locals', 'Locals##debug': 'Locals##debug', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'Login': 'Prijava', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'Prijava za administrativno okruženje', 'Login/Register': 'Prijava/Registracija', @@ -330,10 +377,12 @@ 'modules': 'moduli', 'Modules': 'Moduli', 'Multi User Mode': 'Višekorisnički režim rada', +'Name': 'Name', 'new application "%s" created': 'nova aplikacija "%s" je kreirana', 'new application "%s" imported': 'nova aplikacija "%s" je uvežena', 'New application wizard': 'Čarobnjak za nove aplikacije', 'New Application Wizard': 'Čarobnjak za nove aplikacije', +'New password': 'New password', 'new plugin installed': 'novi pomoćni modul je instaliran', 'New plugin installed: %s': 'Instaliran novi pomoćni modul: %s', 'New Record': 'Novi zapis', @@ -354,6 +403,8 @@ 'Not Authorized': 'Nemate ovlašćenje', 'Not supported': 'Nije podržano', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'online designer': 'onlajn dizajner', 'Open new app in new window': 'Otvori novu aplikaciju u novom prozoru', @@ -362,6 +413,7 @@ 'or alternatively': 'ili alternativno', 'Or Get from URL:': 'Or Get from URL:', 'or import from csv file': 'ili uvezi pomoću csv datoteke', +'Origin': 'Origin', 'Original/Translation': 'Original/Prevod', 'Overview': 'Pregled', 'Overwrite installed app': 'Zamjeni već postojeću aplikaciju', @@ -369,7 +421,12 @@ 'Pack compiled': 'Zapakuj kompajlirano', 'Pack custom': 'Prilagođeno pakovanje', 'pack plugin': 'zapakuj pomoćni modul', +'Password': 'Password', 'password changed': 'lozinka je promijenjena', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Prethodne korekcije', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -377,6 +434,7 @@ 'Permission': 'Dozvola', 'Permissions': 'Dozvole', 'Please': 'Molim', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin "%(plugin)s" deleted': 'Pomoćni modul "%(plugin)s" je izbrisan', 'Plugin "%s" in application': 'Pomoćni modul "%s" u aplikaciji', @@ -393,6 +451,7 @@ 'Previous Edit Point': 'Previous Edit Point', 'private files': 'privatne datoteke', 'Private files': 'Privatne datoteke', +'Profile updated': 'Profile updated', 'Project Progress': 'Napredak projekta', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -408,19 +467,35 @@ 'Ram Cleared': 'Ram Cleared', 'Rapid Search': 'Rapid Search', 'Record': 'Record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'record does not exist', 'Record id': 'Record id', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'register': 'register', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', +'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Obnovi preusmjerenja', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'Remove compiled', 'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', 'Replace': 'Zamijeni', 'Replace All': 'Zamijeni sve', 'Repository (%s)': 'Repozitorijum (%s)', 'request': 'request', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', +'Reset Password key': 'Reset Password key', 'Resolve Conflict file': 'Resolve Conflict file', 'response': 'response', 'restart': 'restart', @@ -457,6 +532,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'shell', 'Showing %s to %s of %s %s found': 'Prikazujem %s do %s od %s %s pronađenih', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'Početna', 'Size of cache:': 'Size of cache:', @@ -505,7 +581,9 @@ 'There are no views': 'Nema stranica prikaza', 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'These files are served without processing, your images go here', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', "This page can commit your changes to an openshift app repo and push them to your cloud instance. This assumes that you've already created the application instance using the web2py skeleton and have that repo somewhere on a filesystem that this web2py instance can access. This functionality requires GitPython installed and on the python path of the runtime that web2py is operating in.": "This page can commit your changes to an openshift app repo and push them to your cloud instance. This assumes that you've already created the application instance using the web2py skeleton and have that repo somewhere on a filesystem that this web2py instance can access. This functionality requires GitPython installed and on the python path of the runtime that web2py is operating in.", @@ -517,6 +595,7 @@ 'Ticket ID': 'Tiket ID', 'Ticket Missing': 'Nedostaje tiket', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', +'Timestamp': 'Timestamp', 'to previous version.': 'na prethodnu verziju.', 'To create a plugin, name a file/folder plugin_[name]': 'To create a plugin, name a file/folder plugin_[name]', 'To emulate a breakpoint programatically, write:': 'To emulate a breakpoint programatically, write:', @@ -529,6 +608,7 @@ 'try something like': 'na primjer', 'Try the mobile interface': 'Probaj mobilno okruženje', 'try view': 'probaj prikaz', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'Ne mogu da provjerim mogućnost nadogradnje', @@ -542,6 +622,7 @@ 'Unable to download the list of plugins': 'Ne mogu da preuzmem spisak pomoćnih modula', 'unable to install plugin "%s"': 'ne mogu da instaliram pomoćni modul "%s"', 'unable to parse csv file': 'ne mogu da rasčlanim csv datoteku', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'ne mogu da deinstaliram "%s"', 'unable to upgrade because "%s"': 'ne mogu da ažurimam zbog "%s"', 'uncheck all': 'uncheck all', @@ -560,10 +641,25 @@ 'upload plugin file:': 'preuzmi datoteku pomoćnog modula:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.', 'User': 'Korisnik', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', 'Username': 'Korisničko ime', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Korisnici', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'promenljive', +'Verify Password': 'Verify Password', 'Version': 'Verzija', 'Version %s.%s.%s (%s) %s': 'Verzija %s.%s.%s (%s) %s', 'Versioning': 'Kreiranje verzija', @@ -582,6 +678,7 @@ 'web2py Recent Tweets': 'web2py Recent Tweets', 'web2py upgrade': 'web2py nadogradnja', 'web2py upgraded; please restart it': 'web2py je ažuriran; molim da restartujete', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Working...': 'Izvršavam...', 'Wrap with Abbreviation': 'Wrap with Abbreviation', 'WSGI reference name': 'WSGI referentni naziv', @@ -589,8 +686,13 @@ 'Yes': 'Da', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/tr.py b/applications/admin/languages/tr.py old mode 100755 new mode 100644 index 50e52d8e..128bb3d5 --- a/applications/admin/languages/tr.py +++ b/applications/admin/languages/tr.py @@ -17,9 +17,23 @@ '(version %s)': '(version %s)', '1: Setting Parameters': '1: Parametrelerin Yapılandırılması', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': '@markmin\x01Bir hata oluştu, lütfen sayfayı [[reload %s]]', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', "@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "'%s' uygulaması için[[NEWLINE]]Mercurial Sürüm Kontrol Sistemi Arayüzü", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': '@markmin\x01Aranıyor: **%s** %%{file}', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available: %s': "web2py'nin yeni sürümü mevcut: %s ", 'A new version of web2py is available: Version 1.68.2 (2009-10-21 09:59:29)\n': "web2py'nin yeni sürümü mevcut: Sürüm 1.68.2 (2009-10-21 09:59:29)\r\n", 'Abort': 'Abort', @@ -53,6 +67,7 @@ 'application is compiled and cannot be designed': 'uygulama derlenmiş ve tasarlanamaz', 'Application name:': 'Uygulama adı:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'kullanılamıyor', 'are not used yet': 'şimdilik kullanılamıyor', 'Are you sure you want to delete file "%s"?': '«%s» dosyasını silmek istediğinize emin misiniz?', @@ -68,6 +83,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'UYARI: Giriş günceli bağlantı (HTTPS) gerektirmekte veya yerel makinada çalışılmalıdır.', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'UYARI: ÇOKLU TEST GÜVENLİ DEĞİL. BİRDEN ÇOK TESTİ AYNI ANDA YAPMAYIN.', 'ATTENTION: you cannot edit the running application!': 'UYARI: çalışan uygulamayı düzenleyemezsiniz!', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Python Kodlarını Otomatik Tamamla', 'Available databases and tables': 'Kullanılabilir veritabanları ve tablolar', 'Available Databases and Tables': 'Available Databases and Tables', @@ -96,6 +112,7 @@ 'Change Admin Password': 'Change Admin Password', 'Change admin password': 'admin parolasını değiştir', 'change editor settings': 'düzenleyici ayarlarını değiştir', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'tümünü kontrol et', 'Check for upgrades': 'Güncellemeleri kontrol et', @@ -106,9 +123,11 @@ 'Clear CACHE?': 'Clear CACHE?', 'Clear DISK': 'Clear DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Takibi genişletmek için satır üzerine tıkla', 'Click row to view a ticket': 'Click row to view a ticket', 'click to check for upgrades': 'güncellemeleri denetlemek için tıklayın', +'Client IP': 'Client IP', 'code': 'kod', 'Code listing': 'Code listing', 'collapse/expand all': 'sıkıştır/tümünü aç', @@ -168,6 +187,7 @@ 'Deploy to PythonAnywhere': 'Deploy to PythonAnywhere', 'Deployment form': 'Yayınlama formu', 'Deployment Interface': 'Deployment Interface', +'Description': 'Description', 'Description:': 'Description:', 'design': 'tadarla', 'Detailed traceback description': 'Ayrıntılı nedenin bulma tanımı', @@ -194,6 +214,7 @@ 'Download layouts from repository': 'Download layouts from repository', 'download plugins': 'eklentileri indir', 'Download plugins from repository': 'Download plugins from repository', +'E-mail': 'E-mail', 'EDIT': 'DÜZENLE', 'Edit': 'Düzenle', 'edit all': 'tümünü düzenle', @@ -209,6 +230,9 @@ 'Editing Plural Forms File': 'Editing Plural Forms File', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Etkinleştir', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -251,8 +275,10 @@ 'filter': 'filtre', 'Find Next': 'Sonrakini Bul', 'Find Previous': 'Öncekini Bul', +'First name': 'First name', 'Form has errors': 'Form has errors', 'Frames': 'Çerçeveler', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': '[passed] testlerdeki işlevlerde doctest yok', 'GAE Email': 'GAE E-postası', 'GAE Output': 'GAE Output', @@ -267,6 +293,10 @@ 'Goto': 'Git', 'graph model': 'grafik modeli', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Help': 'Yardım', 'here': 'here', 'Hide/Show Translated strings': 'Çevrilmiş cümleleri Gizle/Görüntüle', @@ -284,6 +314,7 @@ 'Import/Export': 'İçe/Dışa Aktarıcı', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': 'içerir', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'index': 'indeks', 'insert new': 'yeni ekle', @@ -292,6 +323,7 @@ 'Install': 'Kurucu', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'Kurulu uygulamalar', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': 'dahili hata', @@ -300,24 +332,34 @@ 'Invalid action': 'Geçersiz eylem', 'Invalid application name': 'Invalid application name', 'invalid circular reference': 'invalid circular reference', +'Invalid email': 'Invalid email', 'invalid expression': 'geçersiz ifade', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'Parola geçersiz', +'Invalid password': 'Invalid password', 'invalid password.': 'geçersiz parola.', 'Invalid Query': 'Geçersiz Sorgu', 'invalid request': 'geçersiz istek', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': 'geçersiz bilet', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', 'Key bindings': 'Anahtarlar', 'Key bindings for ZenCoding Plugin': 'ZenCoding Eklentisi için anahtarlar', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': '"%(filename)s" dil dosyası/dosyaları güncellendi', 'Language files (static strings) updated': 'Dil dosyası (statik cümleler) güncellendi', 'languages': 'diller', 'Languages': 'Diller', +'Last name': 'Last name', 'Last Revision': 'Last Revision', 'Last saved on:': 'Son kaydedilme:', 'License for': 'için lisanslanmış', @@ -331,8 +373,12 @@ 'Local Apps': 'Local Apps', 'locals': 'yereller', 'Locals##debug': 'Yereller', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'Login': 'Giriş', 'login': 'giriş', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'Yönetsel Arayüze Giriş\t', 'Login/Register': 'Login/Register', @@ -353,10 +399,12 @@ 'Modules': 'Modüller', 'modules': 'modüller', 'Multi User Mode': 'Multi User Mode', +'Name': 'Name', 'new application "%s" created': 'yeni uygulama "%s" oluşturuldu', 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'Yeni Uygulama Sihirbazı', 'New application wizard': 'Yeni uygulama sihirbazı', +'New password': 'New password', 'new plugin installed': 'yeni eklenti kuruldu', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': 'Yeni Kayıt', @@ -377,6 +425,8 @@ 'Not Authorized': 'Not Authorized', 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Not: Eğer github hata kodu 128 durumunu bildiren bir ileti alırsanız, OpenShift hesabınızı ait ssh anahtarının doğru yapılandırıldığından emin olun.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'online designer': 'çevirimiçi tasarlayıcı', 'Open new app in new window': 'Yeni pencerede yeni uygualama aç', @@ -387,6 +437,7 @@ 'or import from csv file': 'veya CSV dsoyasından içerin', 'or provide app url:': "veya uygulama URL'si verin:", 'or provide application url:': "veya uygulama URL'si verin:", +'Origin': 'Origin', 'Original/Translation': 'Orjinal / Çeviri', 'Overview': 'Overview', 'Overwrite installed app': 'Kurulu uygulama üzerine yaz', @@ -395,7 +446,12 @@ 'Pack custom': 'Tercihli paketle', 'pack plugin': 'eklentiyi paketle', 'PAM authenticated user, cannot change password here': 'PAM onaylı kullanıcı, parola buradan değiştirilemiyor', +'Password': 'Password', 'password changed': 'parola değiştirilidi', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'appcfg.py dosyasının patikası', 'Path to local openshift repo root.': 'Yerel openshift repo kökünün patikası.', @@ -403,6 +459,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Lütfen', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin "%(plugin)s" deleted': '"%(plugin)s" eklentisi silindi', 'Plugin "%s" in application': '"%s" uygulamasında eklenti', @@ -419,6 +476,7 @@ 'previous 100 rows': 'önceki 100 satır', 'Private files': 'Özel dosyalar', 'private files': 'özel dosyalar', +'Profile updated': 'Profile updated', 'Project Progress': 'Proje İlerlemesi', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -435,20 +493,36 @@ 'Rapid Search': 'Hızlı Arama', 'Record': 'Record', 'record': 'kayıt', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'kayıt bulunamıyor', 'record id': 'kayıt id', 'Record id': 'Record id', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', 'refresh': 'yeniden yükle', 'register': 'register', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', +'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Yönelendirmeyi yeniden yükle', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'Derlemeyi kaldır', 'Removed Breakpoint on %s at line %s': ' %s üzerindeki kesme noktası %s satırında değiştrildi.', 'Replace': 'Değiştir', 'Replace All': 'Tümünü değiştir', 'Repository (%s)': 'Repository (%s)', 'request': 'istek', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'python-git gerekyior, fakat kurulu değil', +'Reset Password key': 'Reset Password key', 'Resolve Conflict file': 'Dosyadaki çakışmayı çöz', 'response': 'tepki', 'restart': 'yeniden başla', @@ -487,6 +561,7 @@ 'Set Breakpoint on %s at line %s: %s': "%s'nin %s satırındaki kesme noktasını: %s yap", 'shell': 'kabuk', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': 'Site', 'Size of cache:': 'Size of cache:', @@ -541,7 +616,9 @@ 'These files are not served, they are only available from within your app': 'Bu dosyalar servis yapılmıyor, bunlar sadece uygulamanız içerisinden erişilebilir.', 'These files are served without processing, your images go here': 'Bu dosyalarda işlem yapılmadan kaydedildi, resimler buraya gidiyor:', 'these files are served without processing, your images go here': 'bu dosyalarda işlem yapılmadan kaydedildi, resimler buraya gidiyor:', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', 'This is the %(filename)s template': 'Bu şablonlar %(filename)s: ', @@ -554,6 +631,7 @@ 'Ticket ID': "Bile ID'si", 'Ticket Missing': 'Ticket Missing', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', +'Timestamp': 'Timestamp', 'TM': 'TM', 'to previous version.': 'önceki sürüme.', 'To create a plugin, name a file/folder plugin_[name]': 'Eklenti oluşturmak için dosyayı dosya/klasör plugin_[isim] şeklinde isimlendir. ', @@ -569,6 +647,7 @@ 'try something like': 'gibi birşey dene', 'Try the mobile interface': 'Mobil arayüzü dene', 'try view': 'görünümü dene', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'Güncellemeler denetlenemiyor', @@ -585,6 +664,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': "impossible d'analyser les fichiers CSV", +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'kladıramıyor çünkü "%s"', 'unable to upgrade because "%s"': 'güncelleyemiyor çünkü "%s"', 'uncheck all': 'tümünü kladır', @@ -610,10 +690,25 @@ 'Use an url:': "Url'yi kullan:", 'user': 'kullanıcı', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'değişkenler', +'Verify Password': 'Verify Password', 'Version': 'Sürüm', 'Versioning': 'Sürümleme', 'versioning': 'sürümleme', @@ -633,14 +728,20 @@ 'web2py Recent Tweets': 'web2py Son Twitler', 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py güncellendi, lütfen yeniden başlatın', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Working...': 'Working...', 'WSGI reference name': 'WSGI referans ismi', 'YES': 'EVET', 'Yes': 'Yes', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': '"kesme noktaları" düğmesini kullanarak düzenleme penceresine geçebilir ve kesme nokatalarını hem ekeleyebilir hemde kaldırabilirsiniz', 'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'Gelinceye kadar kurmalısınız', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/uk.py b/applications/admin/languages/uk.py old mode 100755 new mode 100644 index e1462848..ae6fdf76 --- a/applications/admin/languages/uk.py +++ b/applications/admin/languages/uk.py @@ -18,10 +18,23 @@ '(something like "it-it")': '(щось схоже на "uk-ua")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', '@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(не існує файлу **gluon/contrib/plural_rules/%s.py**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'Сталась помилка, будь-ласка [[переватажте %s]] сторінку', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', "@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Інтерфейс системи контролю версій Mercurial[[NEWLINE]]для додатку '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': 'Знайдено: **%s** %%{файл}', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'Abort': 'Припинити', 'About': 'Про', 'about': 'про', @@ -53,6 +66,7 @@ 'application is compiled and cannot be designed': 'додаток скомпільований. налаштування змінювати не можна', 'Application name:': 'Назва додатку:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'не використовуються', 'are not used yet': 'поки що не використовуються', 'Are you sure you want to delete file "%s"?': 'Ви впевнені, що хочете вилучити файл "%s"?', @@ -67,6 +81,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': "УВАГА: Вхід потребує надійного (HTTPS) з'єднання або запуску на локальному комп'ютері.", 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ОБЕРЕЖНО: ТЕСТУВАННЯ НЕ Є ПОТОКО-БЕЗПЕЧНИМ, ТОЖ НЕ ЗАПУСКАЙТЕ ДЕКІЛЬКА ТЕСТІВ ОДНОЧАСНО.', 'ATTENTION: you cannot edit the running application!': 'УВАГА: Ви не можете редагувати додаток, який зараз виконуєте!', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Автозавершення коду на Python', 'Available databases and tables': 'Доступні бази даних та таблиці', 'Available Databases and Tables': 'Available Databases and Tables', @@ -95,6 +110,7 @@ 'Change Admin Password': 'Change Admin Password', 'Change admin password': 'Змінити пароль адміністратора', 'change editor settings': 'змінити налаштування редактора', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': 'відмітити всі', 'Check for upgrades': 'Перевірити оновлення', @@ -105,8 +121,10 @@ 'Clear CACHE?': 'Очистити ВЕСЬ кеш?', 'Clear DISK': 'Очистити ДИСКОВИЙ КЕШ', 'Clear RAM': "Очистити КЕШ В ПАМ'ЯТІ", +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': '"Клацніть" мишкою по рядку, щоб розгорнути стек викликів (traceback)', 'Click row to view a ticket': 'Для перегляду позначки (ticket) "клацніть" мишкою по рядку', +'Client IP': 'Client IP', 'code': 'код', 'Code listing': 'Лістинг', 'collapse/expand all': 'згорнути/розгорнути все', @@ -168,6 +186,7 @@ 'Deploy to PythonAnywhere': 'Deploy to PythonAnywhere', 'Deployment form': 'Форма розгортання (deployment form)', 'Deployment Interface': 'Deployment Interface', +'Description': 'Description', 'Description:': 'Description:', 'design': 'налаштування', 'Detailed traceback description': 'Детальний опис стеку викликів (traceback)', @@ -194,6 +213,7 @@ 'Download layouts from repository': 'Download layouts from repository', 'download plugins': 'завантажити втулки', 'Download plugins from repository': 'Download plugins from repository', +'E-mail': 'E-mail', 'Edit': 'Редагувати', 'edit all': 'редагувати всі', 'Edit application': 'Налаштування додатку', @@ -207,6 +227,9 @@ 'Editing Plural Forms File': 'Редагується файл форм множини', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Увімкнути', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -247,8 +270,10 @@ 'filter': 'фільтр', 'Find Next': 'Шукати наступний', 'Find Previous': 'Шукати попередній', +'First name': 'First name', 'Form has errors': 'Form has errors', 'Frames': 'Стек викликів', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Функції, в яких відсутні док-тести відносяться до функцій, які успішно пройшли тести.', 'GAE Email': 'Ел.пошта GAE', 'GAE Output': 'Відповідь GAE', @@ -265,6 +290,10 @@ 'Goto': 'Перейти до', 'graph model': 'графова модель', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Help': 'Допомога', 'here': 'here', 'Hide/Show Translated strings': 'Сховати/показати ВЖЕ ПЕРЕКЛАДЕНІ рядки', @@ -281,6 +310,7 @@ 'Import/Export': 'Імпорт/Експорт', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'Під час розробки , використовуйте вбудований веб-сервер Rocket, він найкраще налаштований на спільну роботу з інтерактивним ладначем.', 'includes': 'включає', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'index': 'індекс', 'insert new': 'вставити новий', @@ -289,6 +319,7 @@ 'Install': 'Встановлення', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': 'Встановлені додатки (applications)', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Виконується %s рядок %s', 'Interactive console': 'Інтерактивна консоль', 'internal error': 'внутрішня помилка', @@ -298,24 +329,34 @@ 'Invalid application name': 'Invalid application name', 'invalid circual reference': 'помилкове циклічне посилання', 'invalid circular reference': 'помилкове циклічне посилання', +'Invalid email': 'Invalid email', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'неправильний пароль', +'Invalid password': 'Invalid password', 'invalid password.': 'неправильний пароль.', 'Invalid Query': 'Помилковий запит', 'invalid request': 'хибний запит', 'Invalid request': 'Invalid request', 'invalid request ': 'Хибний запит', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'хибна назва таблиці (таблиці auth_* вже оголошено)', 'invalid ticket': 'недійсна позначка про помилку (ticket)', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Ключ', 'Key bindings': 'Клавіатурні скорочення:', 'Key bindings for ZenCoding Plugin': 'Клавіатурні скорочення для втулки ZenCoding plugin', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'вбити процес', 'language file "%(filename)s" created/updated': 'Файл перекладу "%(filename)s" створено/оновлено', 'Language files (static strings) updated': 'Файли перекладів (із статичних рядків в першоджерелах) оновлено', 'languages': 'переклади', 'Languages': 'Переклади', +'Last name': 'Last name', 'Last Revision': 'Last Revision', 'Last saved on:': 'Востаннє збережено:', 'License for': 'Ліцензія додатку', @@ -330,8 +371,12 @@ 'Local Apps': 'Local Apps', 'locals': 'локальні', 'Locals##debug': 'Локальні змінні', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'Login': 'Вхід', 'login': 'вхід', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': 'Вхід в адміністративний інтерфейс', 'Login/Register': 'Login/Register', @@ -359,10 +404,12 @@ 'Must include at least %s lowercase': 'Повинен включати щонайменше %s малих букв', 'Must include at least %s of the following : %s': 'Має включати не менше %s таких символів : %s', 'Must include at least %s uppercase': 'Повинен включати щонайменше %s великих букв', +'Name': 'Name', 'new application "%s" created': 'новий додаток "%s" створено', 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'Майстер створення нового додатку', 'New application wizard': 'Майстер створення нового додатку', +'New password': 'New password', 'new plugin installed': 'нова втулка (plugin) встановлена', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': 'Новий запис', @@ -384,6 +431,8 @@ 'Not Authorized': 'Не дозволено', 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Примітка: Якщо ви отримали повідомлення про помилку з кодом стану github рівним 128, переконайтесь, що система та обліковий запис, з якого відбувається розгортання використовують відповідний ключ ssh, налаштований в обліковому записі openshift.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": 'У промисловій експлуатації, ви повинні налаштувати ваш веб-сервер на використання одного процесу та багатьох потоків, якщо бажаєте скористатись цим ладначем.', 'online designer': 'дизайнер БД', 'Open new app in new window': 'Open new app in new window', @@ -392,6 +441,7 @@ 'or alternatively': 'або альтернативно', 'Or Get from URL:': 'Або Отримати з мережі (ч/з URL):', 'or import from csv file': 'або імпортувати через csv-файл', +'Origin': 'Origin', 'Original/Translation': 'Оригінал/переклад', 'Overview': 'Overview', 'Overwrite installed app': 'Перезаписати встановлений додаток', @@ -400,7 +450,12 @@ 'Pack custom': 'Пакувати вибране', 'pack plugin': 'запакувати втулку', 'PAM authenticated user, cannot change password here': 'Ввімкнена система ідентифікації користувачів PAM. Для зміни паролю скористайтесь командами вашої ОС ', +'Password': 'Password', 'password changed': 'пароль змінено', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Шлях до appcfg.py', 'Path to local openshift repo root.': 'Шлях до локального корня репозитарія openshift.', @@ -409,6 +464,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Будь-ласка', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin': 'втулка', 'plugin "%(plugin)s" deleted': 'втулку "%(plugin)s" вилучено', @@ -427,6 +483,7 @@ 'Previous Edit Point': 'Попереднє місце редагування', 'Private files': 'Приватні файли', 'private files': 'приватні файли', +'Profile updated': 'Profile updated', 'Project Progress': 'Поступ проекту', 'Pull': 'Втягнути', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -443,21 +500,37 @@ 'Rapid Search': 'Миттєвий пошук', 'Record': 'Record', 'record': 'запис', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'запису не існує', 'record id': 'Ід.запису', 'Record id': 'Record id', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', 'refresh': 'оновіть', 'register': 'register', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', +'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'reload': 'перевантажити', 'Reload routes': 'Перезавантажити маршрути', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': 'Вилуч.компл', 'Removed Breakpoint on %s at line %s': 'Вилучено точку зупинки у %s в рядку %s', 'Replace': 'Замінити', 'Replace All': 'Замінити все', 'Repository (%s)': 'Repository (%s)', 'request': 'запит', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'Для розгортання необхідний пакет python-git, але він не встановлений', +'Reset Password key': 'Reset Password key', 'resolve': "розв'язати", 'Resolve Conflict file': "Файл розв'язування конфлікту", 'response': 'відповідь', @@ -500,6 +573,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Додано точку зупинки в %s на рядок %s: %s', 'shell': 'консоль', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'signup': 'підписатись', 'signup_requested': 'необхідна_реєстрація', 'Singular Form': 'Форма однини', @@ -553,7 +627,9 @@ 'There are no views': 'Відображень нема', 'These files are not served, they are only available from within your app': 'Ці файли ніяк не обробляються, вони доступні тільки в межах вашого додатку', 'These files are served without processing, your images go here': 'Ці файли обслуговуються "як є", без обробки, ваші графічні файли та інші супутні файли даних можуть знаходитись тут', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": 'Цей ладнач може працювати некоректно, якщо ви використовуєте веб-сервер без підтримки потоків або використовуєте декілька сервісних процесів.', +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'Це експериментальна властивість, яка вимагає подальшого тестування. Якщо ви вирішили повернутись на попередню версію, ви це робити на ваш власний розсуд.', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'Це експериментальна властивість, яка вимагає подальшого тестування. Якщо ви вирішили розпочати оновлення, ви це робите на ваш власний розсуд', 'This is the %(filename)s template': 'Це шаблон %(filename)s', @@ -568,6 +644,7 @@ 'Ticket Missing': 'Позначка (ticket) відсутня', 'tickets': 'позначки (tickets)', 'Time in Cache (h:m:s)': 'Час в кеші (г:хв:сек)', +'Timestamp': 'Timestamp', 'to previous version.': 'до попередньої версії.', 'To create a plugin, name a file/folder plugin_[name]': 'Для створення втулки, назвіть файл/каталог plugin_[name]', 'To emulate a breakpoint programatically, write:': 'Для встановлення точки зупинки програмним чином напишіть:', @@ -580,6 +657,7 @@ 'try something like': 'спробуйте щось схоже на', 'Try the mobile interface': 'Спробуйте мобільний інтерфейс', 'try view': 'дивитись результат', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'наберіть тут будь-які команди ладнача PDB і натисніть клавішу [Return] ([Enter]), щоб запустити їх на виконання.', 'Type python statement in here and hit Return (Enter) to execute it.': 'Наберіть тут будь-які вирази Python і натисніть клавішу [Return] ([Enter]), щоб запустити їх на виконання.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', @@ -597,6 +675,7 @@ 'unable to install application "%(appname)s"': 'не вдається встановити додаток "%(appname)s"', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': 'не вдається розібрати csv-файл', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'не вдається вилучити "%s"', 'unable to upgrade because "%s"': 'не вдається оновити, тому що "%s"', 'unauthorized': 'неавторизовано', @@ -620,11 +699,26 @@ 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Для створення складних запитів використовуйте (...)&(...) замість AND, (...)|(...) замість OR, та ~(...) замість NOT.', 'user': 'користувач', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Використання оболонки може заблокувати базу даних від сумісного використання іншими користувачами цього додатку.', 'value not allowed': 'недопустиме значення', 'variables': 'змінні', +'Verify Password': 'Verify Password', 'Version': 'Версія', 'Version %s.%s.%s (%s) %s': 'Версія %s.%s.%s (%s) %s', 'Versioning': 'Контроль версій', @@ -644,6 +738,7 @@ 'web2py Recent Tweets': 'Останні твіти web2py', 'web2py upgrade': 'оновлення web2py', 'web2py upgraded; please restart it': 'web2py оновлено; будь-ласка перезапустіть його', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Working...': 'Working...', 'Wrap with Abbreviation': 'Загорнути з абревіатурою', 'WSGI reference name': "ім'я посилання WSGI", @@ -652,9 +747,14 @@ 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'Ви також можете встановлювати/вилучати точки зупинок під час редагування першоджерел (sources), використовуючи кнопку "+/- точку зупинки"', 'You can inspect variables using the console bellow': 'Ви можете досліджувати змінні, використовуючи інтерактивну консоль', 'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'У вас є ще одна спроба перед тим, як вхід буде заблоковано', 'you must specify a name for the uploaded application': "ви повинні вказати ім'я додатка, перед ти, як завантажити його", 'You need to set up and reach a': 'Треба встановити та досягнути', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Ваш додаток буде заблоковано, поки ви не клацнете по одній з кнопок керування ("наступний", "крок", "продовжити", та ін.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/zh-tw.py b/applications/admin/languages/zh-tw.py old mode 100755 new mode 100644 index b8d564fc..1b15382f --- a/applications/admin/languages/zh-tw.py +++ b/applications/admin/languages/zh-tw.py @@ -15,7 +15,23 @@ '(something like "it-it")': '(格式類似 "zh-tw")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': 'Searching: **%s** files', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': '新版的 web2py 已發行', 'A new version of web2py is available: %s': '新版的 web2py 已發行: %s', 'Abort': 'Abort', @@ -50,6 +66,7 @@ 'application is compiled and cannot be designed': '應用程式已經編譯無法重新設計', 'Application name:': 'Application name:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'are not used', 'are not used yet': 'are not used yet', 'Are you sure you want to delete file "%s"?': '確定要刪除檔案"%s"?', @@ -67,6 +84,7 @@ 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': '注意: 因為在測試模式不保證多執行緒安全性,也就是說不可以同時執行多個測試案例', 'ATTENTION: you cannot edit the running application!': '注意:不可編輯正在執行的應用程式!', 'Authentication': '驗證', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available databases and tables': '可提供的資料庫和資料表', 'Available Databases and Tables': 'Available Databases and Tables', @@ -96,6 +114,7 @@ 'Change admin password': 'change admin password', 'change editor settings': 'change editor settings', 'Change Password': '變更密碼', +'Change password': 'Change password', 'change_password': '變更密碼', 'Changelog': 'Changelog', 'check all': '全選', @@ -110,6 +129,7 @@ 'Clear RAM': 'Clear RAM', 'click here for online examples': '點此處進入線上範例', 'click here for the administrative interface': '點此處進入管理介面', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Click row to expand traceback', 'Click row to view a ticket': 'Click row to view a ticket', 'click to check for upgrades': '點擊打勾以便升級', @@ -226,6 +246,9 @@ 'Editing Plural Forms File': 'Editing Plural Forms File', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Enable', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -266,6 +289,7 @@ 'First name': '名', 'Form has errors': 'Form has errors', 'Frames': 'Frames', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': '沒有 doctests 的函式會顯示 [passed].', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -280,7 +304,10 @@ 'Goto': 'Goto', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': '群組編號', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Hello World': '嗨! 世界', 'Help': '說明檔', 'here': 'here', @@ -299,6 +326,7 @@ 'Import/Export': '匯入/匯出', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': '包含', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'Index': '索引', 'index': '索引', @@ -308,6 +336,7 @@ 'Install': '安裝', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': '已安裝應用程式', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': '內部錯誤', @@ -318,14 +347,22 @@ 'invalid circular reference': 'invalid circular reference', 'Invalid email': '不合法的電子郵件', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': '密碼錯誤', +'Invalid password': 'Invalid password', 'invalid password.': 'invalid password.', 'Invalid Query': '不合法的查詢', 'invalid request': '不合法的網路要求(request)', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': '不合法的問題單號', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': '語言檔"%(filename)s"已創建或更新', @@ -347,8 +384,12 @@ 'Local Apps': 'Local Apps', 'locals': 'locals', 'Locals##debug': 'Locals##debug', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'login': '登入', 'Login': '登入', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': '登入到管理員介面', 'Login/Register': 'Login/Register', @@ -376,6 +417,7 @@ 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'New Application Wizard', 'New application wizard': 'New application wizard', +'New password': 'New password', 'new plugin installed': '已安裝新插件', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': '新紀錄', @@ -395,6 +437,8 @@ 'Not Authorized': 'Not Authorized', 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'Open new app in new window': 'Open new app in new window', 'OpenShift Deployment Interface': 'OpenShift Deployment Interface', @@ -414,7 +458,10 @@ 'PAM authenticated user, cannot change password here': 'PAM 授權使用者, 無法在此變更密碼', 'Password': '密碼', 'password changed': '密碼已變更', +'Password changed': 'Password changed', "Password fields don't match": '密碼欄不匹配', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -423,6 +470,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Please', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin': '插件', 'plugin "%(plugin)s" deleted': '已刪除插件"%(plugin)s"', @@ -440,6 +488,7 @@ 'previous 100 rows': '往前 100 筆', 'Private files': 'Private files', 'private files': 'private files', +'Profile updated': 'Profile updated', 'Project Progress': 'Project Progress', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -456,14 +505,25 @@ 'Rapid Search': 'Rapid Search', 'Record': 'Record', 'record': '紀錄', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': '紀錄不存在', 'record id': '紀錄編號', 'Record ID': '紀錄編號', 'Record id': 'Record id', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'register': '註冊', 'Register': '註冊', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': '註冊金鑰', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Reload routes', 'Remember me (for 30 days)': '記住帳號(30 天)', 'Remove compiled': '編譯檔案已移除', @@ -472,6 +532,7 @@ 'Replace All': 'Replace All', 'Repository (%s)': 'Repository (%s)', 'request': 'request', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', 'Reset Password key': '重設密碼', @@ -514,6 +575,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': '命令列操作介面', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': '網站', 'Size of cache:': 'Size of cache:', @@ -570,7 +632,9 @@ 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'These files are served without processing, your images go here', 'these files are served without processing, your images go here': '這些檔案保留未經處理,你的影像檔在此', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', 'This is the %(filename)s template': '這是%(filename)s檔案的樣板(template)', @@ -600,6 +664,7 @@ 'try something like': '嘗試如', 'Try the mobile interface': 'Try the mobile interface', 'try view': 'try view', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': '無法做升級檢查', @@ -616,6 +681,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': '無法解析逗號分隔檔(csv)', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': '無法移除安裝 "%s"', 'unable to upgrade because "%s"': '無法升級因為 "%s"', 'uncheck all': '全不選', @@ -639,10 +705,21 @@ 'upload plugin file:': '上傳插件檔:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': '使用下列方式來組合更複雜的條件式, (...)&(...) 代表同時存在的條件, (...)|(...) 代表擇一的條件, ~(...)則代表反向條件.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': '使用者 %(id)s 已登入', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', 'User %(id)s Registered': '使用者 %(id)s 已註冊', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': '使用者編號', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'variables', @@ -667,6 +744,7 @@ 'web2py Recent Tweets': 'web2py 最近的 Tweets', 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': '已升級 web2py ; 請重新啟動', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': '歡迎 %s', 'Welcome to web2py': '歡迎使用 web2py', 'Working...': 'Working...', @@ -675,8 +753,13 @@ 'Yes': 'Yes', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/languages/zh.py b/applications/admin/languages/zh.py old mode 100755 new mode 100644 index 7215a477..70952c2c --- a/applications/admin/languages/zh.py +++ b/applications/admin/languages/zh.py @@ -19,7 +19,23 @@ '(something like "it-it")': '(就酱 "it-it")', '(version %s)': '(version %s)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +"@markmin\x01Mercurial Version Control System Interface[[NEWLINE]]for application '%s'": "Mercurial Version Control System Interface[[NEWLINE]]for application '%s'", +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.': 'Please [[refresh %s]] this page to see if a breakpoint was hit and debug interaction is required.', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': 'Searching: **%s** files', +'@markmin\x01You need to set up and reach a [[breakpoint %s]] to use the debugger!': 'You need to set up and reach a [[breakpoint %s]] to use the debugger!', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': '新版本web2py已经可用', 'A new version of web2py is available: %s': '新版本web2py已经可用: %s', 'Abort': 'Abort', @@ -53,6 +69,7 @@ 'application is compiled and cannot be designed': '应用已编译完无法设计', 'Application name:': 'Application name:', 'Application updated via git pull': 'Application updated via git pull', +'Apply changes': 'Apply changes', 'are not used': 'are not used', 'are not used yet': 'are not used yet', 'Are you sure you want to delete file "%s"?': '你真想删除文件"%s"?', @@ -69,6 +86,7 @@ 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': '', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': '', 'ATTENTION: you cannot edit the running application!': '注意: 不能修改正在运行中的应用!', +'Authentication code': 'Authentication code', 'Autocomplete Python Code': 'Autocomplete Python Code', 'Available databases and tables': '可用数据库/表', 'Available Databases and Tables': 'Available Databases and Tables', @@ -90,7 +108,7 @@ 'can be a git repo': 'can be a git repo', 'Cancel': 'Cancel', 'Cannot be empty': '不能不填', -'Cannot compile: there are errors in your app.        Debug it, correct errors and try again.': '无法编译: 应用中有错误,请修订后再试.', +'Cannot compile: there are errors in your app. \xa0 \xa0 \xa0 \xa0Debug it, correct errors and try again.': '无法编译: 应用中有错误,请修订后再试.', 'Cannot compile: there are errors in your app:': 'Cannot compile: there are errors in your app:', 'cannot create file': '无法创建文件', 'cannot upload file "%(filename)s"': '无法上传文件 "%(filename)s"', @@ -98,6 +116,7 @@ 'Change admin password': 'change admin password', 'change editor settings': 'change editor settings', 'Change Password': '修改密码', +'Change password': 'Change password', 'Changelog': 'Changelog', 'check all': '检查所有', 'Check for upgrades': 'check for upgrades', @@ -110,6 +129,7 @@ 'Clear RAM': 'Clear RAM', 'click here for online examples': '猛击此处,查看在线实例', 'click here for the administrative interface': '猛击此处,进入管理界面', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Click row to expand traceback', 'Click row to view a ticket': 'Click row to view a ticket', 'click to check for upgrades': '猛击查看是否有升级版本', @@ -220,6 +240,9 @@ 'Editing Plural Forms File': 'Editing Plural Forms File', 'Editor': 'Editor', 'Email Address': 'Email Address', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Enable', 'Enable Close-Tag': 'Enable Close-Tag', 'Enable Code Folding': 'Enable Code Folding', @@ -262,6 +285,7 @@ 'First name': '姓', 'Form has errors': 'Form has errors', 'Frames': 'Frames', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': '', 'GAE Email': 'GAE Email', 'GAE Output': 'GAE Output', @@ -277,7 +301,10 @@ 'Goto': 'Goto', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': '组ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Hello World': '道可道,非常道;名可名,非常名', 'Help': '帮助', 'here': 'here', @@ -296,6 +323,7 @@ 'Import/Export': '导入/导出', 'In development, use the default Rocket webserver that is currently supported by this debugger.': 'In development, use the default Rocket webserver that is currently supported by this debugger.', 'includes': '包含', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Indent with tabs': 'Indent with tabs', 'insert new': '新插入', 'insert new %s': '新插入 %s', @@ -303,6 +331,7 @@ 'Install': 'install', 'Installation of %(plugin)s for %(app)s': 'Installation of %(plugin)s for %(app)s', 'Installed applications': '已安装的应用', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interaction at %s line %s', 'Interactive console': 'Interactive console', 'internal error': '内部错误', @@ -313,14 +342,22 @@ 'invalid circular reference': 'invalid circular reference', 'Invalid email': '无效的email', 'Invalid git repository specified.': 'Invalid git repository specified.', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': '无效密码', +'Invalid password': 'Invalid password', 'invalid password.': 'invalid password.', 'Invalid Query': '无效查询', 'invalid request': '无效请求', 'Invalid request': 'Invalid request', +'Invalid reset password': 'Invalid reset password', 'invalid table names (auth_* tables already defined)': 'invalid table names (auth_* tables already defined)', 'invalid ticket': '无效传票', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'Keyboard shortcuts': 'Keyboard shortcuts', 'kill process': 'kill process', 'language file "%(filename)s" created/updated': '语言文件 "%(filename)s"被创建/更新', @@ -342,8 +379,12 @@ 'Local Apps': 'Local Apps', 'locals': 'locals', 'Locals##debug': 'Locals##debug', +'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'login': '登录', 'Login': '登录', +'Login disabled by administrator': 'Login disabled by administrator', 'Login successful': 'Login successful', 'Login to the Administrative Interface': '登录到管理员界面', 'Login/Register': 'Login/Register', @@ -370,6 +411,7 @@ 'new application "%s" imported': 'new application "%s" imported', 'New Application Wizard': 'New Application Wizard', 'New application wizard': 'New application wizard', +'New password': 'New password', 'new plugin installed': 'new plugin installed', 'New plugin installed: %s': 'New plugin installed: %s', 'New Record': '新记录', @@ -389,6 +431,8 @@ 'Not Authorized': 'Not Authorized', 'Not supported': 'Not supported', 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.': 'Note: If you receive an error with github status code of 128, ensure the system and account you are deploying from has a cooresponding ssh key configured in the openshift account.', +'Object or table name': 'Object or table name', +'Old password': 'Old password', "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.": "On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.", 'Open new app in new window': 'Open new app in new window', 'OpenShift Deployment Interface': 'OpenShift Deployment Interface', @@ -409,6 +453,10 @@ 'PAM authenticated user, cannot change password here': 'PAM authenticated user, cannot change password here', 'Password': '密码', 'password changed': 'password changed', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Past revisions': 'Past revisions', 'Path to appcfg.py': 'Path to appcfg.py', 'Path to local openshift repo root.': 'Path to local openshift repo root.', @@ -416,6 +464,7 @@ 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Please', +'please input your password again': 'please input your password again', 'Please wait, giving pythonanywhere a moment...': 'Please wait, giving pythonanywhere a moment...', 'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" deleted', 'Plugin "%s" in application': 'Plugin "%s" in application', @@ -432,6 +481,7 @@ 'previous 100 rows': '前100行', 'Private files': 'Private files', 'private files': 'private files', +'Profile updated': 'Profile updated', 'Project Progress': 'Project Progress', 'Pull': 'Pull', 'Pull failed, certain files could not be checked out. Check logs for details.': 'Pull failed, certain files could not be checked out. Check logs for details.', @@ -448,23 +498,37 @@ 'Rapid Search': 'Rapid Search', 'Record': 'Record', 'record': 'record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': '记录并不存在', 'record id': '记录ID', 'Record ID': '记录ID', 'Record id': 'Record id', +'Record Updated': 'Record Updated', 'refresh': 'refresh', 'register': 'register', 'Register': '注册', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': '注册密匙', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Reload routes', +'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Remove compiled': '已移除', 'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', 'Replace': 'Replace', 'Replace All': 'Replace All', 'Repository (%s)': 'Repository (%s)', 'request': 'request', +'Request reset password': 'Request reset password', 'requires distutils, but not installed': 'requires distutils, but not installed', 'requires python-git, but not installed': 'requires python-git, but not installed', +'Reset Password key': 'Reset Password key', 'Resolve Conflict file': '解决冲突文件', 'response': 'response', 'restart': 'restart', @@ -503,6 +567,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': '', 'Showing %s to %s of %s %s found': 'Showing %s to %s of %s %s found', +'Sign Up': 'Sign Up', 'Singular Form': 'Singular Form', 'Site': '总站', 'Size of cache:': 'Size of cache:', @@ -558,7 +623,9 @@ 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'These files are served without processing, your images go here', 'these files are served without processing, your images go here': '', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.": "This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.", +'This email already has an account': 'This email already has an account', 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to downgrade you do it at your own risk', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', 'This is the %(filename)s template': '这是 %(filename)s 模板', @@ -577,7 +644,7 @@ 'To create a plugin, name a file/folder plugin_[name]': 'To create a plugin, name a file/folder plugin_[name]', 'To emulate a breakpoint programatically, write:': 'To emulate a breakpoint programatically, write:', 'to use the debugger!': 'to use the debugger!', -'to  previous version.': '退回前一版本', +'to \xa0previous version.': '退回前一版本', 'toggle breakpoint': 'toggle breakpoint', 'Toggle comment': 'Toggle comment', 'Toggle Fullscreen': 'Toggle Fullscreen', @@ -588,6 +655,7 @@ 'try something like': '尝试', 'Try the mobile interface': 'Try the mobile interface', 'try view': 'try view', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'Type PDB debugger command in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': '无法检查是否需要升级', @@ -604,6 +672,7 @@ 'Unable to download the list of plugins': 'Unable to download the list of plugins', 'unable to install plugin "%s"': 'unable to install plugin "%s"', 'unable to parse csv file': '无法生成 cvs', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': '无法卸载 "%s"', 'unable to upgrade because "%s"': 'unable to upgrade because "%s"', 'uncheck all': '反选全部', @@ -625,14 +694,28 @@ 'upload file:': '提交文件:', 'upload plugin file:': 'upload plugin file:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.', -'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...)  for NOT to build more complex queries.': '', +'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) \xa0for NOT to build more complex queries.': '', 'Use an url:': 'Use an url:', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': '用户ID', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Using the shell may lock the database to other users of this app.': 'Using the shell may lock the database to other users of this app.', 'variables': 'variables', +'Verify Password': 'Verify Password', 'Version': '版本', 'Versioning': 'Versioning', 'versioning': '版本', @@ -652,6 +735,7 @@ 'web2py Recent Tweets': 'twitter上的web2py进展实播', 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py upgraded; please restart it', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py': '欢迎使用web2py', 'Working...': 'Working...', 'WSGI reference name': 'WSGI reference name', @@ -659,8 +743,13 @@ 'Yes': 'Yes', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button', 'You can inspect variables using the console below': 'You can inspect variables using the console below', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You have one more login attempt before you are locked out': 'You have one more login attempt before you are locked out', 'You need to set up and reach a': 'You need to set up and reach a', 'You only need these if you have already registered': 'You only need these if you have already registered', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/admin/models/0_imports.py b/applications/admin/models/0_imports.py index b911c0e7..6b550b83 100644 --- a/applications/admin/models/0_imports.py +++ b/applications/admin/models/0_imports.py @@ -25,6 +25,5 @@ from gluon.utils import md5_hash from gluon.fileutils import listdir, cleanpath, up from gluon.fileutils import tar, tar_compiled, untar, fix_newlines from gluon.languages import findT, update_all_languages -from gluon.myregex import * from gluon.restricted import * from gluon.compileapp import compile_application, remove_compiled_application diff --git a/applications/admin/settings.cfg b/applications/admin/settings.cfg index 4127b27c..12505206 100644 --- a/applications/admin/settings.cfg +++ b/applications/admin/settings.cfg @@ -1,9 +1,12 @@ -[DEFAULT] - [editor] -theme = web2py -editor = default +theme = twilight +editor = sublime closetag = true +tabwidth = 4 +highlightline = true +linenumbers = true +codefolding = false +indentwithtabs = false [editor_sessions] welcome = welcome/models/db.py,welcome/controllers/default.py,welcome/views/default/index.html diff --git a/applications/admin/static/css/jqueryMultiSelect.css b/applications/admin/static/css/jqueryMultiSelect.css old mode 100755 new mode 100644 diff --git a/applications/admin/static/js/jqueryMultiSelect.js b/applications/admin/static/js/jqueryMultiSelect.js old mode 100755 new mode 100644 diff --git a/applications/admin/static/js/web2py_bootstrap.js b/applications/admin/static/js/web2py_bootstrap.js index 7206cb1b..abf6d1c7 100644 --- a/applications/admin/static/js/web2py_bootstrap.js +++ b/applications/admin/static/js/web2py_bootstrap.js @@ -29,5 +29,14 @@ jQuery(function(){ } hoverMenu(); // first page load jQuery(window).resize(hoverMenu); // on resize event - jQuery('ul.nav li.dropdown a').click(function(){window.location=jQuery(this).attr('href');}); + jQuery('ul.nav li.dropdown a').click(function(){ + if(jQuery(this).attr("target")){ + window.open( + jQuery(this).attr('href'), + jQuery(this).attr('target') // <- This is what makes it open in a new window. + ); + } else { + window.location=jQuery(this).attr('href'); + } + }); }); diff --git a/applications/admin/static/plugin_jqmobile/images/ajax-loader.png b/applications/admin/static/plugin_jqmobile/images/ajax-loader.png old mode 100755 new mode 100644 diff --git a/applications/admin/static/plugin_jqmobile/images/icons-18-black.png b/applications/admin/static/plugin_jqmobile/images/icons-18-black.png old mode 100755 new mode 100644 diff --git a/applications/admin/static/plugin_jqmobile/images/icons-18-white.png b/applications/admin/static/plugin_jqmobile/images/icons-18-white.png old mode 100755 new mode 100644 diff --git a/applications/admin/static/plugin_jqmobile/images/icons-36-black.png b/applications/admin/static/plugin_jqmobile/images/icons-36-black.png old mode 100755 new mode 100644 diff --git a/applications/admin/static/plugin_jqmobile/images/icons-36-white.png b/applications/admin/static/plugin_jqmobile/images/icons-36-white.png old mode 100755 new mode 100644 diff --git a/applications/admin/static/plugin_multiselect/jquery.multi-select.js b/applications/admin/static/plugin_multiselect/jquery.multi-select.js old mode 100755 new mode 100644 diff --git a/applications/admin/static/plugin_multiselect/multi-select.css b/applications/admin/static/plugin_multiselect/multi-select.css old mode 100755 new mode 100644 diff --git a/applications/admin/static/plugin_multiselect/switch.png b/applications/admin/static/plugin_multiselect/switch.png old mode 100755 new mode 100644 diff --git a/applications/admin/views/debug/interact.html b/applications/admin/views/debug/interact.html index c771bfb7..29cd285c 100644 --- a/applications/admin/views/debug/interact.html +++ b/applications/admin/views/debug/interact.html @@ -63,12 +63,12 @@

{{=T("No Interaction yet")}}

{{pass}} diff --git a/applications/admin/views/default.mobile/plugin.html b/applications/admin/views/default.mobile/plugin.html index d42ec974..c1d36892 100644 --- a/applications/admin/views/default.mobile/plugin.html +++ b/applications/admin/views/default.mobile/plugin.html @@ -38,19 +38,19 @@ def file_upload_form(location): INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY()), INPUT(_type="hidden",_name="location",_value=location), INPUT(_type="hidden",_name="sender",_value=URL('design/'+app)), - INPUT(_type="submit",_value=T("submit")),_action=URL('upload_file')) + INPUT(_type="submit",_value=T("Submit")),_action=URL('upload_file')) return form def file_create_form(location): form=FORM(T("create file with filename:")," ", INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY()), INPUT(_type="hidden",_name="location",_value=location), INPUT(_type="hidden",_name="sender",_value=URL('design/'+app)), - INPUT(_type="submit",_value=T("submit")),_action=URL('create_file')) + INPUT(_type="submit",_value=T("Submit")),_action=URL('create_file')) return form def upload_plugin_form(app): form=FORM(T("upload plugin file:")," ", INPUT(_type="file",_name="pluginfile"), - INPUT(_type="submit",_value=T("submit"))) + INPUT(_type="submit",_value=T("Submit"))) return form def deletefile(arglist): return A_delete(SPAN(T('Delete')), _href=URL('delete',args=arglist,vars=dict(sender=request.function+'/'+app))) diff --git a/applications/admin/views/default/editor_settings.html b/applications/admin/views/default/editor_settings.html index d0485231..c1f54235 100644 --- a/applications/admin/views/default/editor_settings.html +++ b/applications/admin/views/default/editor_settings.html @@ -6,7 +6,7 @@ return files }} {{themes = [f[:-4] for f in listfiles('admin', "static/codemirror/theme", regexp='.*\.css$' )]}} -{{editors = ['default', 'vim', 'emacs']}} +{{editors = ['default', 'vim', 'emacs', 'sublime']}}
@@ -27,7 +27,7 @@
-
{{=SELECT(range(1,9, 1), value=editor_settings['tabwidth'], _name="tabwidth" )}}
+
{{=SELECT(list(range(1,9, 1)), value=editor_settings['tabwidth'], _name="tabwidth" )}}
diff --git a/applications/admin/views/default/plugin.html b/applications/admin/views/default/plugin.html index a0c5a2d4..8ce7da62 100644 --- a/applications/admin/views/default/plugin.html +++ b/applications/admin/views/default/plugin.html @@ -24,7 +24,7 @@ def file_upload_form(location): INPUT(_type="hidden",_name="location",_value=location), INPUT(_type="hidden",_name="token",_value=session.token), INPUT(_type="hidden",_name="sender",_value=URL('design/'+app)), - INPUT(_type="submit",_value=T("submit")),_action=URL('upload_file')) + INPUT(_type="submit",_value=T("Submit")),_action=URL('upload_file')) return form def file_create_form(location): form=FORM(T("create file with filename:")," ", @@ -32,7 +32,7 @@ def file_create_form(location): INPUT(_type="hidden",_name="location",_value=location), INPUT(_type="hidden",_name="token",_value=session.token), INPUT(_type="hidden",_name="sender",_value=URL('design/'+app)), - INPUT(_type="submit",_value=T("submit")),_action=URL('create_file')) + INPUT(_type="submit",_value=T("Submit")),_action=URL('create_file')) return form def deletefile(arglist): return A(TAG[''](IMG(_src=URL('static', 'images/delete_icon.png')), @@ -85,7 +85,7 @@ def deletefile(arglist): {{=peekfile('models',m)}} - {{if len(defines[m]):}}{{=T("defines tables")}} {{pass}}{{=XML(', '.join([B(table).xml() for table in defines[m]]))}} + {{if len(defines[m]):}}{{=T("defines tables")}} {{pass}}{{=XML(b', '.join([B(table).xml() for table in defines[m]]))}} {{pass}} @@ -118,7 +118,7 @@ for c in controllers: controller_functions+=[c[:-3]+'/%s.html'%x for x in functi {{=peekfile('controllers',c)}} - {{if functions[c]:}}{{=T("exposes")}} {{pass}}{{=XML(', '.join([A(f,_href=URL(a=app,c=c[:-3],f=f)).xml() for f in functions[c]]))}} + {{if functions[c]:}}{{=T("exposes")}} {{pass}}{{=XML(b', '.join([A(f,_href=URL(a=app,c=c[:-3],f=f)).xml() for f in functions[c]]))}} {{pass}} @@ -145,7 +145,7 @@ for c in controllers: controller_functions+=[c[:-3]+'/%s.html'%x for x in functi {{if c in extend:}}{{=T("extends")}} {{=extend[c]}} {{pass}} - {{if include[c]:}}{{=T("includes")}} {{pass}}{{=XML(', '.join([B(f).xml() for f in include[c]]))}} + {{if include[c]:}}{{=T("includes")}} {{pass}}{{=XML(b', '.join([B(f).xml() for f in include[c]]))}} {{pass}} diff --git a/applications/examples/controllers/appadmin.py b/applications/examples/controllers/appadmin.py index da050a5b..3a076002 100644 --- a/applications/examples/controllers/appadmin.py +++ b/applications/examples/controllers/appadmin.py @@ -30,7 +30,7 @@ except: if request.is_https: session.secure() -elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1") and \ +elif (remote_addr not in hosts) and (remote_addr != '127.0.0.1') and \ (request.function != 'manage'): raise HTTP(200, T('appadmin is disabled because insecure channel')) @@ -46,7 +46,7 @@ if request.function == 'manage': auth.table_permission()]) manager_role = manager_action.get('role', None) if manager_action else None if not (gluon.fileutils.check_credentials(request) or auth.has_membership(manager_role)): - raise HTTP(403, "Not authorized") + raise HTTP(403, 'Not authorized') menu = False elif (request.application == 'admin' and not session.authorized) or \ (request.application != 'admin' and not gluon.fileutils.check_credentials(request)): @@ -182,12 +182,12 @@ def select(): db = get_database(request) dbname = request.args[0] try: - is_imap = db._uri.startswith("imap://") + is_imap = db._uri.startswith('imap://') except (KeyError, AttributeError, TypeError): is_imap = False - regex = re.compile('(?P
\w+)\.(?P\w+)=(?P\d+)') + regex = re.compile(r'(?P
\w+)\.(?P\w+)=(?P\d+)') if len(request.args) > 1 and hasattr(db[request.args[1]], '_primarykey'): - regex = re.compile('(?P
\w+)\.(?P\w+)=(?P.+)') + regex = re.compile(r'(?P
\w+)\.(?P\w+)=(?P.+)') if request.vars.query: match = regex.match(request.vars.query) if match: @@ -224,20 +224,20 @@ def select(): session.last_orderby = orderby session.last_query = request.vars.query form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px', - _name='query', _value=request.vars.query or '', _class="form-control", + _name='query', _value=request.vars.query or '', _class='form-control', requires=IS_NOT_EMPTY( - error_message=T("Cannot be empty")))), TR(T('Update:'), + error_message=T('Cannot be empty')))), TR(T('Update:'), INPUT(_name='update_check', _type='checkbox', value=False), INPUT(_style='width:400px', _name='update_fields', _value=request.vars.update_fields - or '', _class="form-control")), TR(T('Delete:'), INPUT(_name='delete_check', + or '', _class='form-control')), TR(T('Delete:'), INPUT(_name='delete_check', _class='delete', _type='checkbox', value=False), ''), - TR('', '', INPUT(_type='submit', _value=T('submit'), _class="btn btn-primary"))), + TR('', '', INPUT(_type='submit', _value=T('Submit'), _class='btn btn-primary'))), _action=URL(r=request, args=request.args)) tb = None if form.accepts(request.vars, formname=None): - regex = re.compile(request.args[0] + '\.(?P
\w+)\..+') + regex = re.compile(request.args[0] + r'\.(?P
\w+)\..+') match = regex.match(form.vars.query.strip()) if match: table = match.group('table') @@ -254,8 +254,8 @@ def select(): if is_imap: fields = [db[table][name] for name in - ("id", "uid", "created", "to", - "sender", "subject")] + ('id', 'uid', 'created', 'to', + 'sender', 'subject')] if orderby: rows = db(query, ignore_common_filters=True).select( *fields, limitby=(start, stop), @@ -271,10 +271,10 @@ def select(): # begin handle upload csv csv_table = table or request.vars.table if csv_table: - formcsv = FORM(str(T('or import from csv file')) + " ", + formcsv = FORM(str(T('or import from csv file')) + ' ', INPUT(_type='file', _name='csvfile'), INPUT(_type='hidden', _value=csv_table, _name='table'), - INPUT(_type='submit', _value=T('import'), _class="btn btn-primary")) + INPUT(_type='submit', _value=T('import'), _class='btn btn-primary')) else: formcsv = None if formcsv and formcsv.process().accepted: @@ -356,26 +356,26 @@ def state(): def ccache(): if is_gae: form = FORM( - P(TAG.BUTTON(T("Clear CACHE?"), _type="submit", _name="yes", _value="yes"))) + P(TAG.BUTTON(T('Clear CACHE?'), _type='submit', _name='yes', _value='yes'))) else: cache.ram.initialize() cache.disk.initialize() form = FORM( P(TAG.BUTTON( - T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")), + T('Clear CACHE?'), _type='submit', _name='yes', _value='yes')), P(TAG.BUTTON( - T("Clear RAM"), _type="submit", _name="ram", _value="ram")), + T('Clear RAM'), _type='submit', _name='ram', _value='ram')), P(TAG.BUTTON( - T("Clear DISK"), _type="submit", _name="disk", _value="disk")), + T('Clear DISK'), _type='submit', _name='disk', _value='disk')), ) if form.accepts(request.vars, session): - session.flash = "" + session.flash = '' if is_gae: if request.vars.yes: cache.ram.clear() - session.flash += T("Cache Cleared") + session.flash += T('Cache Cleared') else: clear_ram = False clear_disk = False @@ -387,10 +387,10 @@ def ccache(): clear_disk = True if clear_ram: cache.ram.clear() - session.flash += T("Ram Cleared") + session.flash += T('Ram Cleared') if clear_disk: cache.disk.clear() - session.flash += T("Disk Cleared") + session.flash += T('Disk Cleared') redirect(URL(r=request)) try: @@ -436,7 +436,7 @@ def ccache(): gae_stats['ratio'] = ((gae_stats['hits'] * 100) / (gae_stats['hits'] + gae_stats['misses'])) except ZeroDivisionError: - gae_stats['ratio'] = T("?") + gae_stats['ratio'] = T('?') gae_stats['oldest'] = GetInHMS(time.time() - gae_stats['oldest_item_age']) total.update(gae_stats) else: @@ -502,7 +502,7 @@ def ccache(): TR(TD(B(T('Key'))), TD(B(T('Time in Cache (h:m:s)')))), *[TR(TD(k[0]), TD('%02d:%02d:%02d' % k[1])) for k in keys], **dict(_class='cache-keys', - _style="border-collapse: separate; border-spacing: .5em;")) + _style='border-collapse: separate; border-spacing: .5em;')) if not is_gae: ram['keys'] = key_table(ram['keys']) @@ -536,26 +536,26 @@ def table_template(table): # This is horribe HTML but the only one graphiz understands rows = [] cellpadding = 4 - color = "#000000" - bgcolor = "#FFFFFF" - face = "Helvetica" - face_bold = "Helvetica Bold" + color = '#000000' + bgcolor = '#FFFFFF' + face = 'Helvetica' + face_bold = 'Helvetica Bold' border = 0 rows.append(TR(TD(FONT(table, _face=face_bold, _color=bgcolor), _colspan=3, _cellpadding=cellpadding, - _align="center", _bgcolor=color))) + _align='center', _bgcolor=color))) for row in db[table]: rows.append(TR(TD(FONT(row.name, _color=color, _face=face_bold), - _align="left", _cellpadding=cellpadding, + _align='left', _cellpadding=cellpadding, _border=border), TD(FONT(row.type, _color=color, _face=face), - _align="left", _cellpadding=cellpadding, + _align='left', _cellpadding=cellpadding, _border=border), TD(FONT(types(row), _color=color, _face=face), - _align="center", _cellpadding=cellpadding, + _align='center', _cellpadding=cellpadding, _border=border))) - return "< %s >" % TABLE(*rows, **dict(_bgcolor=bgcolor, _border=1, + return '< %s >' % TABLE(*rows, **dict(_bgcolor=bgcolor, _border=1, _cellborder=0, _cellspacing=0) ).xml() @@ -632,15 +632,15 @@ def hooks(): if len(functions): method_hooks.append({'name': op, 'functions':functions}) if len(method_hooks): - tables.append({'name': "%s.%s" % (db_str, t), 'slug': IS_SLUG()("%s.%s" % (db_str,t))[0], 'method_hooks':method_hooks}) + tables.append({'name': '%s.%s' % (db_str, t), 'slug': IS_SLUG()('%s.%s' % (db_str,t))[0], 'method_hooks':method_hooks}) # Render ul_main = UL(_class='nav nav-list') for t in tables: ul_main.append(A(t['name'], _onclick="collapse('a_%s')" % t['slug'])) - ul_t = UL(_class='nav nav-list', _id="a_%s" % t['slug'], _style='display:none') + ul_t = UL(_class='nav nav-list', _id='a_%s' % t['slug'], _style='display:none') for op in t['method_hooks']: ul_t.append(LI(op['name'])) - ul_t.append(UL([LI(A(f['funcname'], _class="editor_filelink", _href=f['url']if 'url' in f else None, **{'_data-lineno':f['lineno']-1})) for f in op['functions']])) + ul_t.append(UL([LI(A(f['funcname'], _class='editor_filelink', _href=f['url']if 'url' in f else None, **{'_data-lineno':f['lineno']-1})) for f in op['functions']])) ul_main.append(ul_t) return ul_main @@ -650,11 +650,11 @@ def hooks(): # ########################################################### def d3_graph_model(): - """ See https://www.facebook.com/web2py/posts/145613995589010 from Bruno Rocha + ''' See https://www.facebook.com/web2py/posts/145613995589010 from Bruno Rocha and also the app_admin bg_graph_model function - Create a list of table dicts, called "nodes" - """ + Create a list of table dicts, called 'nodes' + ''' nodes = [] links = [] @@ -670,10 +670,10 @@ def d3_graph_model(): elif f_type == 'string': disp = field.length elif f_type == 'id': - disp = "PK" + disp = 'PK' elif f_type.startswith('reference') or \ f_type.startswith('list:reference'): - disp = "FK" + disp = 'FK' else: disp = ' ' fields.append(dict(name=field.name, type=field.type, disp=disp)) @@ -685,7 +685,7 @@ def d3_graph_model(): links.append(dict(source=tablename, target = referenced_table)) - nodes.append(dict(name=tablename, type="table", fields = fields)) + nodes.append(dict(name=tablename, type='table', fields = fields)) # d3 v4 allows individual modules to be specified. The complete d3 library is included below. response.files.append(URL('admin','static','js/d3.min.js')) diff --git a/applications/examples/views/default/download.html b/applications/examples/views/default/download.html index c1ecdec5..d8df3871 100644 --- a/applications/examples/views/default/download.html +++ b/applications/examples/views/default/download.html @@ -62,16 +62,16 @@

- The source code version works on all supported platforms, including Linux, but it requires Python 2.6, or 2.7 (recommended). - It runs on Windows and most Unix systems, including Linux and BSD. + The source code version works on Windows and most Unix systems, including Linux, BSD and Mac . It requires Python 2.6 (no more supported), Python 2.7 (stable) or Python 3.5+ (recommended for new projects) already installed on your system. + There are also binary packages for Windows and Mac OS X. They include the Python 2.7 interpreter so you do not need to have it pre-installed.

Instructions

-

After download, unzip it and click on web2py.exe (windows) or web2py.app (osx). - To run from source, type:

-{{=CODE("python2.7 web2py.py", language=None, counter='>', _class='boxCode')}} +

With the binary packages, after download, just unzip it and then click on web2py.exe (windows) or web2py.app (osx). + If you prefer to run it from source with your own Python interpreter alreay installed, type:

+{{=CODE("python web2py.py", language=None, counter='>', _class='boxCode')}}

or for more info type:

-{{=CODE("python2.7 web2py.py -h", language=None, counter='>', _class='boxCode')}} +{{=CODE("python web2py.py -h", language=None, counter='>', _class='boxCode')}}

Caveats

diff --git a/applications/examples/views/default/index.html b/applications/examples/views/default/index.html index 19708c79..735ee4cc 100644 --- a/applications/examples/views/default/index.html +++ b/applications/examples/views/default/index.html @@ -4,7 +4,7 @@

Web Framework

-

Free open source full-stack framework for rapid development of fast, scalable, secure and portable database-driven web-based applications. Written and programmable in Python.

+

Free open source full-stack framework for rapid development of fast, scalable, secure and portable database-driven web-based applications. Written and programmable in Python (version 3 and 2.7).

diff --git a/applications/welcome/controllers/appadmin.py b/applications/welcome/controllers/appadmin.py index da050a5b..3a076002 100644 --- a/applications/welcome/controllers/appadmin.py +++ b/applications/welcome/controllers/appadmin.py @@ -30,7 +30,7 @@ except: if request.is_https: session.secure() -elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1") and \ +elif (remote_addr not in hosts) and (remote_addr != '127.0.0.1') and \ (request.function != 'manage'): raise HTTP(200, T('appadmin is disabled because insecure channel')) @@ -46,7 +46,7 @@ if request.function == 'manage': auth.table_permission()]) manager_role = manager_action.get('role', None) if manager_action else None if not (gluon.fileutils.check_credentials(request) or auth.has_membership(manager_role)): - raise HTTP(403, "Not authorized") + raise HTTP(403, 'Not authorized') menu = False elif (request.application == 'admin' and not session.authorized) or \ (request.application != 'admin' and not gluon.fileutils.check_credentials(request)): @@ -182,12 +182,12 @@ def select(): db = get_database(request) dbname = request.args[0] try: - is_imap = db._uri.startswith("imap://") + is_imap = db._uri.startswith('imap://') except (KeyError, AttributeError, TypeError): is_imap = False - regex = re.compile('(?P
@@ -18,7 +18,7 @@ - +
\w+)\.(?P\w+)=(?P\d+)') + regex = re.compile(r'(?P
\w+)\.(?P\w+)=(?P\d+)') if len(request.args) > 1 and hasattr(db[request.args[1]], '_primarykey'): - regex = re.compile('(?P
\w+)\.(?P\w+)=(?P.+)') + regex = re.compile(r'(?P
\w+)\.(?P\w+)=(?P.+)') if request.vars.query: match = regex.match(request.vars.query) if match: @@ -224,20 +224,20 @@ def select(): session.last_orderby = orderby session.last_query = request.vars.query form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px', - _name='query', _value=request.vars.query or '', _class="form-control", + _name='query', _value=request.vars.query or '', _class='form-control', requires=IS_NOT_EMPTY( - error_message=T("Cannot be empty")))), TR(T('Update:'), + error_message=T('Cannot be empty')))), TR(T('Update:'), INPUT(_name='update_check', _type='checkbox', value=False), INPUT(_style='width:400px', _name='update_fields', _value=request.vars.update_fields - or '', _class="form-control")), TR(T('Delete:'), INPUT(_name='delete_check', + or '', _class='form-control')), TR(T('Delete:'), INPUT(_name='delete_check', _class='delete', _type='checkbox', value=False), ''), - TR('', '', INPUT(_type='submit', _value=T('submit'), _class="btn btn-primary"))), + TR('', '', INPUT(_type='submit', _value=T('Submit'), _class='btn btn-primary'))), _action=URL(r=request, args=request.args)) tb = None if form.accepts(request.vars, formname=None): - regex = re.compile(request.args[0] + '\.(?P
\w+)\..+') + regex = re.compile(request.args[0] + r'\.(?P
\w+)\..+') match = regex.match(form.vars.query.strip()) if match: table = match.group('table') @@ -254,8 +254,8 @@ def select(): if is_imap: fields = [db[table][name] for name in - ("id", "uid", "created", "to", - "sender", "subject")] + ('id', 'uid', 'created', 'to', + 'sender', 'subject')] if orderby: rows = db(query, ignore_common_filters=True).select( *fields, limitby=(start, stop), @@ -271,10 +271,10 @@ def select(): # begin handle upload csv csv_table = table or request.vars.table if csv_table: - formcsv = FORM(str(T('or import from csv file')) + " ", + formcsv = FORM(str(T('or import from csv file')) + ' ', INPUT(_type='file', _name='csvfile'), INPUT(_type='hidden', _value=csv_table, _name='table'), - INPUT(_type='submit', _value=T('import'), _class="btn btn-primary")) + INPUT(_type='submit', _value=T('import'), _class='btn btn-primary')) else: formcsv = None if formcsv and formcsv.process().accepted: @@ -356,26 +356,26 @@ def state(): def ccache(): if is_gae: form = FORM( - P(TAG.BUTTON(T("Clear CACHE?"), _type="submit", _name="yes", _value="yes"))) + P(TAG.BUTTON(T('Clear CACHE?'), _type='submit', _name='yes', _value='yes'))) else: cache.ram.initialize() cache.disk.initialize() form = FORM( P(TAG.BUTTON( - T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")), + T('Clear CACHE?'), _type='submit', _name='yes', _value='yes')), P(TAG.BUTTON( - T("Clear RAM"), _type="submit", _name="ram", _value="ram")), + T('Clear RAM'), _type='submit', _name='ram', _value='ram')), P(TAG.BUTTON( - T("Clear DISK"), _type="submit", _name="disk", _value="disk")), + T('Clear DISK'), _type='submit', _name='disk', _value='disk')), ) if form.accepts(request.vars, session): - session.flash = "" + session.flash = '' if is_gae: if request.vars.yes: cache.ram.clear() - session.flash += T("Cache Cleared") + session.flash += T('Cache Cleared') else: clear_ram = False clear_disk = False @@ -387,10 +387,10 @@ def ccache(): clear_disk = True if clear_ram: cache.ram.clear() - session.flash += T("Ram Cleared") + session.flash += T('Ram Cleared') if clear_disk: cache.disk.clear() - session.flash += T("Disk Cleared") + session.flash += T('Disk Cleared') redirect(URL(r=request)) try: @@ -436,7 +436,7 @@ def ccache(): gae_stats['ratio'] = ((gae_stats['hits'] * 100) / (gae_stats['hits'] + gae_stats['misses'])) except ZeroDivisionError: - gae_stats['ratio'] = T("?") + gae_stats['ratio'] = T('?') gae_stats['oldest'] = GetInHMS(time.time() - gae_stats['oldest_item_age']) total.update(gae_stats) else: @@ -502,7 +502,7 @@ def ccache(): TR(TD(B(T('Key'))), TD(B(T('Time in Cache (h:m:s)')))), *[TR(TD(k[0]), TD('%02d:%02d:%02d' % k[1])) for k in keys], **dict(_class='cache-keys', - _style="border-collapse: separate; border-spacing: .5em;")) + _style='border-collapse: separate; border-spacing: .5em;')) if not is_gae: ram['keys'] = key_table(ram['keys']) @@ -536,26 +536,26 @@ def table_template(table): # This is horribe HTML but the only one graphiz understands rows = [] cellpadding = 4 - color = "#000000" - bgcolor = "#FFFFFF" - face = "Helvetica" - face_bold = "Helvetica Bold" + color = '#000000' + bgcolor = '#FFFFFF' + face = 'Helvetica' + face_bold = 'Helvetica Bold' border = 0 rows.append(TR(TD(FONT(table, _face=face_bold, _color=bgcolor), _colspan=3, _cellpadding=cellpadding, - _align="center", _bgcolor=color))) + _align='center', _bgcolor=color))) for row in db[table]: rows.append(TR(TD(FONT(row.name, _color=color, _face=face_bold), - _align="left", _cellpadding=cellpadding, + _align='left', _cellpadding=cellpadding, _border=border), TD(FONT(row.type, _color=color, _face=face), - _align="left", _cellpadding=cellpadding, + _align='left', _cellpadding=cellpadding, _border=border), TD(FONT(types(row), _color=color, _face=face), - _align="center", _cellpadding=cellpadding, + _align='center', _cellpadding=cellpadding, _border=border))) - return "< %s >" % TABLE(*rows, **dict(_bgcolor=bgcolor, _border=1, + return '< %s >' % TABLE(*rows, **dict(_bgcolor=bgcolor, _border=1, _cellborder=0, _cellspacing=0) ).xml() @@ -632,15 +632,15 @@ def hooks(): if len(functions): method_hooks.append({'name': op, 'functions':functions}) if len(method_hooks): - tables.append({'name': "%s.%s" % (db_str, t), 'slug': IS_SLUG()("%s.%s" % (db_str,t))[0], 'method_hooks':method_hooks}) + tables.append({'name': '%s.%s' % (db_str, t), 'slug': IS_SLUG()('%s.%s' % (db_str,t))[0], 'method_hooks':method_hooks}) # Render ul_main = UL(_class='nav nav-list') for t in tables: ul_main.append(A(t['name'], _onclick="collapse('a_%s')" % t['slug'])) - ul_t = UL(_class='nav nav-list', _id="a_%s" % t['slug'], _style='display:none') + ul_t = UL(_class='nav nav-list', _id='a_%s' % t['slug'], _style='display:none') for op in t['method_hooks']: ul_t.append(LI(op['name'])) - ul_t.append(UL([LI(A(f['funcname'], _class="editor_filelink", _href=f['url']if 'url' in f else None, **{'_data-lineno':f['lineno']-1})) for f in op['functions']])) + ul_t.append(UL([LI(A(f['funcname'], _class='editor_filelink', _href=f['url']if 'url' in f else None, **{'_data-lineno':f['lineno']-1})) for f in op['functions']])) ul_main.append(ul_t) return ul_main @@ -650,11 +650,11 @@ def hooks(): # ########################################################### def d3_graph_model(): - """ See https://www.facebook.com/web2py/posts/145613995589010 from Bruno Rocha + ''' See https://www.facebook.com/web2py/posts/145613995589010 from Bruno Rocha and also the app_admin bg_graph_model function - Create a list of table dicts, called "nodes" - """ + Create a list of table dicts, called 'nodes' + ''' nodes = [] links = [] @@ -670,10 +670,10 @@ def d3_graph_model(): elif f_type == 'string': disp = field.length elif f_type == 'id': - disp = "PK" + disp = 'PK' elif f_type.startswith('reference') or \ f_type.startswith('list:reference'): - disp = "FK" + disp = 'FK' else: disp = ' ' fields.append(dict(name=field.name, type=field.type, disp=disp)) @@ -685,7 +685,7 @@ def d3_graph_model(): links.append(dict(source=tablename, target = referenced_table)) - nodes.append(dict(name=tablename, type="table", fields = fields)) + nodes.append(dict(name=tablename, type='table', fields = fields)) # d3 v4 allows individual modules to be specified. The complete d3 library is included below. response.files.append(URL('admin','static','js/d3.min.js')) diff --git a/applications/welcome/cron/crontab.example b/applications/welcome/cron/crontab.example deleted file mode 100644 index 6ab4ea8c..00000000 --- a/applications/welcome/cron/crontab.example +++ /dev/null @@ -1 +0,0 @@ -#crontab \ No newline at end of file diff --git a/applications/welcome/languages/ar.py b/applications/welcome/languages/ar.py old mode 100755 new mode 100644 index dce4610f..9b48312a --- a/applications/welcome/languages/ar.py +++ b/applications/welcome/languages/ar.py @@ -18,16 +18,31 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '@markmin\x01**Hello World**': '**مرحباً بالعالم**', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'About': 'نبذة', 'Access Control': 'متحكمات الوصول', 'admin': 'admin', 'Administrative Interface': 'واجهة التحكم', 'Ajax Recipes': 'وصفات أجاكس', 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'API Example': 'API Example', 'appadmin is disabled because insecure channel': 'appadmin is disabled because insecure channel', +'Apply changes': 'Apply changes', 'Are you sure you want to delete this object?': 'هل أنت متأكد بحذف هذا الكائن ؟', +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'Available Databases and Tables', 'Buy this book': 'أشتري هذا الكتاب', "Buy web2py's book": "Buy web2py's book", @@ -37,10 +52,13 @@ 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Cache Keys': 'Cache Keys', 'Cannot be empty': 'لا يمكن بأن يكون خالي', +'Change Password': 'Change Password', +'Change password': 'Change password', 'Check to delete': 'أختر للحذف', 'Clear CACHE?': 'Clear CACHE?', 'Clear DISK': 'Clear DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Client IP': 'IP المستخدم', 'Community': 'المجتمع', 'Components and Plugins': 'العناصر والإضافات', @@ -76,6 +94,9 @@ 'E-mail': 'البريد الإلكتروني', 'Edit current record': 'Edit current record', 'Email and SMS': 'البريد الإلكتروني والرسالة النصية', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'enter an integer between %(min)g and %(max)g': 'أدخل عدد صحيح بين %(min)g و %(man)g', 'enter date and time as %(format)s': 'أدخل التاريخ والوقت كالنمط %(format)', 'Errors': 'الأخطاء', @@ -84,8 +105,11 @@ 'First name': 'الأسم الأول', 'Forms and Validators': 'الإستمارات والمدققات', 'Free Applications': 'تطبيقات مجانية', +'Function disabled': 'Function disabled', 'Graph Model': 'Graph Model', +'Grid Example': 'Grid Example', 'Group %(group_id)s created': 'المجموعة %(group_id)s قد أنشئت', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'هوية المجموعة', 'Group uniquely assigned to user %(id)s': 'المجموعة مخصصة للمستخدم %(id)s', 'Groups': 'مجموعات', @@ -98,13 +122,23 @@ 'How did you get here?': 'كيف أستطعت الوصول إلى هنا ؟', 'import': 'import', 'Import/Export': 'Import/Export', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': 'Internal State', 'Introduction': 'مقدمة', 'Invalid email': 'بريد إلكتروني غير صالح', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', +'Invalid password': 'Invalid password', 'Invalid Query': 'Invalid Query', 'invalid request': 'invalid request', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Is Active': 'نشط', 'Key': 'Key', +'Key verified': 'Key verified', 'Last name': 'أسم العائلة', 'Layout': 'النسق', 'Layout Plugins': 'نسّق الإضافات', @@ -114,9 +148,11 @@ 'Logged in': 'تم تسجيل الدخول', 'Logged out': 'تم تسجيل الخروج', 'Login': 'تسجيل الدخول', +'Login disabled by administrator': 'Login disabled by administrator', 'Logout': 'تسجيل الخروج', 'Lost Password': 'فقدت كلمة المرور', 'Lost password?': 'هل فقدت كلمة المرور ؟', +'Lost your password?': 'Lost your password?', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', 'Manage Cache': 'Manage Cache', @@ -126,12 +162,14 @@ 'Modified On': 'عُدلت في', 'My Sites': 'موقعي', 'Name': 'الأسم', +'New password': 'New password', 'New Record': 'New Record', 'new record inserted': 'new record inserted', 'next %s rows': 'next %s rows', 'No databases in this application': 'No databases in this application', 'Number of entries: **%s**': 'Number of entries: **%s**', 'Object or table name': 'أسم الكائن أو الجدول', +'Old password': 'Old password', 'Online book': 'Online book', 'Online examples': 'أمثلة على الأنترنت', 'or import from csv file': 'or import from csv file', @@ -140,7 +178,10 @@ 'Other Recipes': 'وصفات أخرى', 'Overview': 'نظرة عامة', 'Password': 'كلمة المرور', +'Password changed': 'Password changed', "Password fields don't match": 'حقول كلمة المرور لا تتطابق', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Permission': 'Permission', 'Permissions': 'Permissions', 'please input your password again': 'الرجاء إعادة إدخال كلمة المرور', @@ -149,6 +190,7 @@ 'Preface': 'المدخل', 'previous %s rows': 'previous %s rows', 'Profile': 'الملف الشخصي', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'pygraphviz library not found', 'Python': 'بايثون', 'Query:': 'Query:', @@ -159,14 +201,24 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'وصفات', 'Record': 'Record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'record does not exist', 'Record ID': 'هوية السجل ', 'Record id': 'Record id', +'Record Updated': 'Record Updated', 'Register': 'التسجيل', 'Registration identifier': 'مُعرف التسجيل', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'رمز التسجيل', +'Registration needs verification': 'Registration needs verification', 'Registration successful': 'تم التسجيل بنجاح', 'Remember me (for 30 days)': 'تذكرني ( إلى 30 يوم)', +'Request reset password': 'Request reset password', 'Reset Password key': 'إعادة ظبط مفتاح كلمة المرور', 'Role': 'دور', 'Roles': 'Roles', @@ -176,11 +228,13 @@ 'Semantic': 'دليل لفظي', 'Services': 'خدمات', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'Size of cache:': 'Size of cache:', 'state': 'state', 'Statistics': 'Statistics', 'Stylesheet': 'أسلوب النمط', 'submit': 'submit', +'Submit': 'Submit', 'Support': 'الدعم', 'Table': 'Table', 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.', @@ -188,28 +242,50 @@ 'The output of the file is a dictionary that was rendered by the view %s': 'نتاج هذا الملف هو قاموس قًدم بواسطة العارض %s', 'The Views': 'المشاهدات', 'This App': 'هذا التطبيق', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', 'Timestamp': 'البصمة الزمنية', 'Traceback': 'Traceback', 'Twitter': 'تويتر', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'unable to parse csv file': 'unable to parse csv file', +'Unable to send email': 'Unable to send email', 'Update:': 'Update:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': 'المستخدم %(id)s قد سجل دخوله', 'User %(id)s Logged-out': 'المستخدم %(id)s قد سجل خروجه', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', 'User %(id)s Registered': 'المستخدم %(id)s مسجل', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'هوية المستخدم', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'value already in database or empty': 'القيمة موجودة مسبقاً أو فارغة', 'Verify Password': 'تأكيد كلمة المرور', 'Videos': 'الفيديوهات', 'View': 'العرض', 'Welcome': 'مرحباً', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py!': 'مرحباً بكم في ويب2 باي !', 'Which called the function %s located in the file %s': 'الدالة المسماة %s موجودة في ملف %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Working...', 'You are successfully running web2py': 'أستطعت تثبيت web2py بنجاح !', 'You can modify this application and adapt it to your needs': 'تستطيع تعديل هذا التطبيق لما يناسب إحتياجك', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': ' ملقد زرت الرابط %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/ca.py b/applications/welcome/languages/ca.py old mode 100755 new mode 100644 index ef6ec70f..aa4b6fc5 --- a/applications/welcome/languages/ca.py +++ b/applications/welcome/languages/ca.py @@ -16,9 +16,19 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'Hi ha hagut un error, si us plau [[recarregui %s]] la pàgina', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', '@markmin\x01Number of entries: **%s**': "Nombre d'entrades: **%s**", +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': 'Hi ha una nova versió de wep2py disponible', 'A new version of web2py is available: %s': 'Hi ha una nova versió de wep2py disponible: %s', 'About': 'Sobre', @@ -44,6 +54,7 @@ 'And': 'I', 'and rename it (required):': 'i renombri-la (requerit):', 'and rename it:': " i renombri'l:", +'API Example': 'API Example', 'appadmin': 'appadmin', 'appadmin is disabled because insecure channel': 'admin inhabilitat, el canal no és segur', 'application "%s" uninstalled': 'aplicació "%s" desinstal·lada', @@ -60,6 +71,7 @@ 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATENCION: NO EJECUTE VARIAS PRUEBAS SIMULTANEAMENTE, NO SON THREAD SAFE.', 'ATTENTION: you cannot edit the running application!': 'ATENCIO: no pot modificar la aplicació que està ejecutant-se!', 'Authentication': 'Autenticació', +'Authentication code': 'Authentication code', 'Authentication failed at client DB!': '¡La autenticació ha fallat en la BDD client!', 'Authentication failed at main DB!': '¡La autenticació ha fallat en la BDD principal!', 'Available Databases and Tables': 'Bases de dades i taules disponibles', @@ -170,6 +182,8 @@ 'Email': 'Email', 'Email and SMS': 'Correu electrònic i SMS', 'Email sent': 'Correu electrònic enviat', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'End of impersonation': 'Fi de suplantació', 'enter a number between %(min)g and %(max)g': 'introdueixi un número entre %(min)g i %(max)g', 'Enter a valid email address': 'Entri una adreça email vàlida', @@ -205,9 +219,12 @@ 'Forgot username?': 'Ha oblidat el nom de usuari?', 'Forms and Validators': 'Formularis i validadors', 'Free Applications': 'Aplicacions Lliures', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Funcions sense doctests equivalen a pruebas [aceptades].', 'Graph Model': 'Graph Model', +'Grid Example': 'Grid Example', 'Group %(group_id)s created': 'Grupo %(group_id)s creat', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'ID de Grup', 'Group uniquely assigned to user %(id)s': 'Grup assignat únicament al usuari %(id)s', 'Groups': 'Grups', @@ -227,6 +244,7 @@ 'Import/Export': 'Importar/Exportar', 'in': 'a', 'includes': 'inclou', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Index': 'Índex', 'insert new': 'inserti nou', 'insert new %s': 'inserti nou %s', @@ -238,14 +256,20 @@ 'Invalid action': 'Acció invàlida', 'Invalid email': 'Correo electrónico invàlid', 'invalid expression': 'expressió invàlida', +'Invalid key': 'Invalid key', 'Invalid login': 'Inici de sessió invàlida', 'invalid password': 'contrasenya invàlida', +'Invalid password': 'Invalid password', 'Invalid Query': 'Consulta invàlida', 'invalid request': 'sol·licitud invàlida', 'Invalid reset password': 'Reinici de contrasenya invàlid', 'invalid ticket': 'tiquet invàlid', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Is Active': 'Està Actiu', 'Key': 'Clau', +'Key verified': 'Key verified', 'language file "%(filename)s" created/updated': 'fitxer de llenguatge "%(filename)s" creat/actualitzat', 'Language files (static strings) updated': 'Fitxers de llenguatge (cadenes estàtiques) actualitzats', 'languages': 'llenguatges', @@ -272,6 +296,7 @@ 'Lost Password': 'Contrasenya perdida', 'Lost password?': 'Ha oblidat la contrasenya?', 'lost password?': '¿ha oblidat la contrasenya?', +'Lost your password?': 'Lost your password?', 'Main Menu': 'Menú principal', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', @@ -322,6 +347,7 @@ 'Password changed': 'Contrasenya cambiada', "Password fields don't match": 'Els camps de contrasenya no coincideixen', 'Password reset': 'Reinici de contrasenya', +'Password retrieve': 'Password retrieve', 'Peeking at file': 'Visualitzant fitxer', 'Permission': 'Permís', 'Permissions': 'Permisos', @@ -348,15 +374,22 @@ 'Recipes': 'Receptes', 'Record': 'Registre', 'Record %(id)s created': 'Registre %(id)s creat', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', 'Record Created': 'Registre Creat', +'Record Deleted': 'Record Deleted', 'record does not exist': 'el registre no existe', 'Record ID': 'ID de Registre', 'Record id': 'Id de registre', +'Record Updated': 'Record Updated', 'Ref APB': 'Ref APB', 'register': "registri's", 'Register': "Registri's", 'Registration identifier': 'Identificador de Registre', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Clau de registre', +'Registration needs verification': 'Registration needs verification', 'Registration successful': 'Registre amb èxit', 'reload': 'recarregar', 'Remember me (for 30 days)': "Recordi'm (durant 30 dies)", @@ -382,6 +415,7 @@ 'session expired': 'sessió expirada', 'shell': 'terminal', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'site': 'lloc', 'Size of cache:': 'Mida de la Caché:', 'Slug': 'Slug', @@ -420,6 +454,7 @@ 'There are no views': 'No hi ha vistes', 'these files are served without processing, your images go here': 'aquests fitxers són servits sense processar, les seves imatges van aquí', 'This App': 'Aquesta Aplicació', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', 'This email already has an account': 'Aquest correu electrònic ja té un compte', 'This is a copy of the scaffolding application': 'Aquesta és una còpia de la aplicació de bastiment', 'This is the %(filename)s template': 'Aquesta és la plantilla %(filename)s', @@ -443,12 +478,14 @@ 'TSV (Spreadsheets)': 'TSV (Fulls de càlcul)', 'TSV (Spreadsheets, hidden cols)': 'TSV (Fulls de càlcul, columnes amagades)', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Unable to check for upgrades': 'No és possible verificar la existencia de actualitzacions', 'unable to create application "%s"': 'no és possible crear la aplicació "%s"', 'unable to delete file "%(filename)s"': 'no és possible eliminar el fitxer "%(filename)s"', 'Unable to download': 'No és possible la descàrrega', 'Unable to download app': 'No és possible descarregar la aplicació', 'unable to parse csv file': 'no és possible analitzar el fitxer CSV', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'no és possible instalar "%s"', 'uncheck all': 'desmarcar tots', 'uninstall': 'desinstalar', @@ -466,9 +503,12 @@ 'User %(id)s Logged-out': 'El usuari %(id)s finalitzà la sessió', 'User %(id)s Password changed': 'Contrasenya del usuari %(id)s canviada', 'User %(id)s Password reset': 'Contrasenya del usuari %(id)s reiniciada', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', 'User %(id)s Profile updated': 'Actualitzat el perfil del usuari %(id)s', 'User %(id)s Registered': 'Usuari %(id)s Registrat', 'User %(id)s Username retrieved': 'Se ha recuperat el nom de usuari del usuari %(id)s', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User %(username)s Logged-in': 'El usuari %(username)s inicià la sessió', "User '%(username)s' Logged-in": "El usuari '%(username)s' inicià la sessió", "User '%(username)s' Logged-out": "El usuari '%(username)s' finalitzà la sessió", @@ -476,6 +516,7 @@ 'User ID': 'ID de Usuari', 'User Logged-out': 'El usuari finalitzà la sessió', 'Username': 'Nom de usuari', +'Username already taken': 'Username already taken', 'Username retrieve': 'Recuperar nom de usuari', 'Users': 'Usuaris', 'Value already in database or empty': 'El valor ya existeix en la base de dades o està buit', @@ -496,10 +537,12 @@ 'web2py is up to date': 'web2py està actualitzat', 'web2py Recent Tweets': 'Tweets Recents de web2py', 'Welcome': 'Benvingut', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': 'Benvingut %s', 'Welcome to web2py': 'Benvingut a web2py', 'Welcome to web2py!': '¡Benvingut a web2py!', 'Which called the function %s located in the file %s': 'La qual va cridar la funció %s localitzada en el fitxer %s', +'Wiki Example': 'Wiki Example', 'Wiki Page': 'Wiki Page', 'Working...': 'Treballant ...', 'XML': 'XML', @@ -507,6 +550,10 @@ 'YES': 'SÍ', 'You are successfully running web2py': 'Vostè està executant web2py amb èxit', 'You can modify this application and adapt it to your needs': 'Vostè pot modificar aquesta aplicació i adaptar-la a les seves necessitats', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'Vostè va visitar la url %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', 'Your username is: %(username)s': 'El seu nom de usuari és: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/cs.py b/applications/welcome/languages/cs.py old mode 100755 new mode 100644 index 75add19f..02af85df --- a/applications/welcome/languages/cs.py +++ b/applications/welcome/languages/cs.py @@ -19,9 +19,21 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', '@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(soubor **gluon/contrib/plural_rules/%s.py** nenalezen)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '@markmin\x01Searching: **%s** %%{file}': 'Hledání: **%s** %%{soubor}', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'About': 'O programu', 'About application': 'O aplikaci', 'Access Control': 'Řízení přístupu', @@ -38,12 +50,14 @@ 'An error occured, please %s the page': 'An error occured, please %s the page', 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', 'and rename it:': 'a přejmenovat na:', +'API Example': 'API Example', 'appadmin': 'appadmin', 'appadmin is disabled because insecure channel': 'appadmin je zakázaná bez zabezpečeného spojení', 'Application': 'Application', 'application "%s" uninstalled': 'application "%s" odinstalována', 'application compiled': 'aplikace zkompilována', 'Application name:': 'Název aplikace:', +'Apply changes': 'Apply changes', 'are not used': 'nepoužita', 'are not used yet': 'ještě nepoužita', 'Are you sure you want to delete this object?': 'Opravdu chcete odstranit tento objekt?', @@ -53,6 +67,7 @@ 'at line %s': 'at line %s', 'ATTENTION:': 'ATTENTION:', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.', +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'Dostupné databáze a tabulky', 'back': 'zpět', 'Back to wizard': 'Back to wizard', @@ -75,6 +90,7 @@ 'Change Admin Password': 'Změnit heslo pro správu', 'Change admin password': 'Změnit heslo pro správu aplikací', 'Change password': 'Změna hesla', +'Change Password': 'Change Password', 'check all': 'vše označit', 'Check for upgrades': 'Zkusit aktualizovat', 'Check to delete': 'Označit ke smazání', @@ -84,6 +100,7 @@ 'Clear CACHE?': 'Vymazat CACHE?', 'Clear DISK': 'Vymazat DISK', 'Clear RAM': 'Vymazat RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Pro rozbalení stopy, klikněte na řádek', 'Click row to view a ticket': 'Pro zobrazení chyby (ticketu), klikněte na řádku...', 'Client IP': 'IP adresa klienta', @@ -168,6 +185,9 @@ 'Editing Language file': 'Úprava jazykového souboru', 'Editing Plural Forms File': 'Editing Plural Forms File', 'Email and SMS': 'Email a SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'Odblokovat', 'enter a number between %(min)g and %(max)g': 'zadejte číslo mezi %(min)g a %(max)g', 'enter an integer between %(min)g and %(max)g': 'zadejte celé číslo mezi %(min)g a %(max)g', @@ -201,6 +221,7 @@ 'Forms and Validators': 'Formuláře a validátory', 'Frames': 'Frames', 'Free Applications': 'Aplikace zdarma', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Functions with no doctests will result in [passed] tests.', 'Generate': 'Vytvořit', 'Get from URL:': 'Stáhnout z internetu:', @@ -211,8 +232,11 @@ 'Goto': 'Goto', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Grid Example': 'Grid Example', 'Group %(group_id)s created': 'Skupina %(group_id)s vytvořena', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'ID skupiny', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Groups': 'Skupiny', 'Hello World': 'Ahoj světe', 'Help': 'Nápověda', @@ -228,26 +252,35 @@ 'import': 'import', 'Import/Export': 'Import/Export', 'includes': 'zahrnuje', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Index': 'Index', 'insert new': 'vložit nový záznam ', 'insert new %s': 'vložit nový záznam %s', 'inspect attributes': 'inspect attributes', 'Install': 'Instalovat', 'Installed applications': 'Nainstalované aplikace', +'Insufficient privileges': 'Insufficient privileges', 'Interaction at %s line %s': 'Interakce v %s, na řádce %s', 'Interactive console': 'Interaktivní příkazová řádka', 'Internal State': 'Vnitřní stav', 'Introduction': 'Úvod', 'Invalid email': 'Neplatný email', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'Invalid password': 'Nesprávné heslo', 'invalid password.': 'neplatné heslo', 'Invalid Query': 'Neplatný dotaz', 'invalid request': 'Neplatný požadavek', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Is Active': 'Je aktivní', 'It is %s %%{day} today.': 'Dnes je to %s %%{den}.', 'Key': 'Klíč', 'Key bindings': 'Vazby klíčů', 'Key bindings for ZenCoding Plugin': 'Key bindings for ZenCoding Plugin', +'Key verified': 'Key verified', 'languages': 'jazyky', 'Languages': 'Jazyky', 'Last name': 'Příjmení', @@ -267,12 +300,14 @@ 'Logged out': 'Odhlášení proběhlo úspěšně', 'Login': 'Přihlásit se', 'login': 'přihlásit se', +'Login disabled by administrator': 'Login disabled by administrator', 'Login to the Administrative Interface': 'Přihlásit se do Správce aplikací', 'logout': 'odhlásit se', 'Logout': 'Odhlásit se', 'Lost Password': 'Zapomněl jste heslo', 'Lost password?': 'Zapomněl jste heslo?', 'lost password?': 'zapomněl jste heslo?', +'Lost your password?': 'Lost your password?', 'Manage': 'Manage', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', @@ -321,11 +356,15 @@ 'pack plugin': 'pack plugin', 'password': 'heslo', 'Password': 'Heslo', +'Password changed': 'Password changed', "Password fields don't match": 'Hesla se neshodují', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Peeking at file': 'Peeking at file', 'Permission': 'Permission', 'Permissions': 'Permissions', 'Please': 'Prosím', +'please input your password again': 'please input your password again', 'Plugin "%s" in application': 'Plugin "%s" in application', 'plugins': 'zásuvné moduly', 'Plugins': 'Zásuvné moduly', @@ -338,6 +377,8 @@ 'Private files': 'Soukromé soubory', 'private files': 'soukromé soubory', 'profile': 'profil', +'Profile': 'Profile', +'Profile updated': 'Profile updated', 'Project Progress': 'Vývoj projektu', 'pygraphviz library not found': 'pygraphviz library not found', 'Python': 'Python', @@ -350,14 +391,24 @@ 'Readme': 'Nápověda', 'Recipes': 'Postupy jak na to', 'Record': 'Záznam', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'záznam neexistuje', 'Record ID': 'ID záznamu', 'Record id': 'id záznamu', +'Record Updated': 'Record Updated', 'refresh': 'obnovte', 'register': 'registrovat', 'Register': 'Zaregistrovat se', 'Registration identifier': 'Registrační identifikátor', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Registrační klíč', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'reload': 'reload', 'Reload routes': 'Znovu nahrát cesty', 'Remember me (for 30 days)': 'Zapamatovat na 30 dní', @@ -366,6 +417,7 @@ 'Replace': 'Zaměnit', 'Replace All': 'Zaměnit vše', 'request': 'request', +'Request reset password': 'Request reset password', 'Reset Password key': 'Reset registračního klíče', 'response': 'response', 'restart': 'restart', @@ -392,6 +444,7 @@ 'Set Breakpoint on %s at line %s: %s': 'Bod přerušení nastaven v souboru %s na řádce %s: %s', 'shell': 'příkazová řádka', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'Singular Form': 'Singular Form', 'Site': 'Správa aplikací', 'Size of cache:': 'Velikost cache:', @@ -436,6 +489,8 @@ 'These files are not served, they are only available from within your app': 'Tyto soubory jsou klientům nepřístupné. K dispozici jsou pouze v rámci aplikace.', 'These files are served without processing, your images go here': 'Tyto soubory jsou servírovány bez přídavné logiky, sem patří např. obrázky.', 'This App': 'Tato aplikace', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'This is a copy of the scaffolding application': 'Toto je kopie aplikace skelet.', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk', 'This is the %(filename)s template': 'This is the %(filename)s template', @@ -457,10 +512,12 @@ 'Try the mobile interface': 'Zkuste rozhraní pro mobilní zařízení', 'try view': 'try view', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Type python statement in here and hit Return (Enter) to execute it.': 'Type python statement in here and hit Return (Enter) to execute it.', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', 'Unable to check for upgrades': 'Unable to check for upgrades', 'unable to parse csv file': 'csv soubor nedá sa zpracovat', +'Unable to send email': 'Unable to send email', 'uncheck all': 'vše odznačit', 'Uninstall': 'Odinstalovat', 'update': 'aktualizovat', @@ -477,14 +534,21 @@ 'upload plugin file:': 'nahrát soubor modulu:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Použijte (...)&(...) pro AND, (...)|(...) pro OR a ~(...) pro NOT pro sestavení složitějších dotazů.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': 'Uživatel %(id)s přihlášen', 'User %(id)s Logged-out': 'Uživatel %(id)s odhlášen', 'User %(id)s Password changed': 'Uživatel %(id)s změnil heslo', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', 'User %(id)s Profile updated': 'Uživatel %(id)s upravil profil', 'User %(id)s Registered': 'Uživatel %(id)s se zaregistroval', 'User %(id)s Username retrieved': 'Uživatel %(id)s si nachal zaslat přihlašovací jméno', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'ID uživatele', 'Username': 'Přihlašovací jméno', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'variables': 'variables', 'Verify Password': 'Zopakujte heslo', @@ -502,15 +566,22 @@ 'web2py upgrade': 'web2py upgrade', 'web2py upgraded; please restart it': 'web2py upgraded; please restart it', 'Welcome': 'Vítejte', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py': 'Vitejte ve web2py', 'Welcome to web2py!': 'Vítejte ve web2py!', 'Which called the function %s located in the file %s': 'která zavolala funkci %s v souboru (kontroléru) %s.', +'Wiki Example': 'Wiki Example', 'Working...': 'Working...', 'You are successfully running web2py': 'Úspěšně jste spustili web2py.', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'Nastavovat a mazat body přerušení je též možno v rámci editování zdrojového souboru přes tlačítko Vyp./Zap. bod přerušení', 'You can inspect variables using the console bellow': 'Níže pomocí příkazové řádky si můžete prohlédnout proměnné', 'You can modify this application and adapt it to your needs': 'Tuto aplikaci si můžete upravit a přizpůsobit ji svým potřebám.', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You need to set up and reach a': 'Je třeba nejprve nastavit a dojít až na', 'You visited the url %s': 'Navštívili jste stránku %s,', 'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Aplikace bude blokována než se klikne na jedno z tlačítek (další, krok, pokračovat, atd.)', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/de.py b/applications/welcome/languages/de.py old mode 100755 new mode 100644 index 2aefeb38..fa218ce9 --- a/applications/welcome/languages/de.py +++ b/applications/welcome/languages/de.py @@ -3,216 +3,155 @@ '!langcode!': 'de', '!langname!': 'Deutsch (DE)', '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"Update" ist ein optionaler Ausdruck wie "feld1=\'newvalue\'". JOIN Ergebnisse können nicht aktualisiert oder gelöscht werden', -'%s %%(shop)': '%s %%(shop)', -'%s %%(shop[0])': '%s %%(shop[0])', -'%s %%{quark[0]}': '%s %%{quark[0]}', -'%s %%{row} deleted': '%s %%{row} gelöscht', -'%s %%{row} updated': '%s %%{row} aktualisiert', -'%s %%{shop[0]}': '%s %%{shop[0]}', -'%s %%{shop}': '%s %%{shop}', -'%s selected': '%s ausgewählt', '%Y-%m-%d': '%Y-%m-%d', '%Y-%m-%d %H:%M:%S': '%Y-%m-%d %H:%M:%S', +'%s %%{row} deleted': '%s %%{row} gelöscht', +'%s %%{row} updated': '%s %%{row} aktualisiert', +'%s selected': '%s ausgewählt', '(**%.0d MB**)': '(**%.0d MB**)', -'**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', -'**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{Item(Items)}, **%(bytes)s** %%{Byte(Bytes)}', +'**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** Items, **%(bytes)s** %%{Byte(Bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**nicht verfügbar** (benötigt die Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] Bibliothek)', '?': '?', -'@markmin\x01**Hello World**': '**Hallo Welt**', -'@markmin\x01An error occured, please [[reload %s]] the page': 'Ein Fehler ist aufgetreten, bitte [[laden %s]] Sie die Seite neu', -'``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**nicht verfügbar**``:rot (benötigt die Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] Bibliothek)', 'About': 'Über', 'Access Control': 'Zugangskontrolle', -'admin': 'admin', -'Administrative Interface': 'Administrationsoberfläche', 'Ajax Recipes': 'Ajax Rezepte', 'An error occured, please [[reload %s]] the page': 'Ein Fehler ist aufgetreten, bitte [[laden %s]] Sie die Seite neu', -'appadmin is disabled because insecure channel': 'Appadmin ist deaktiviert, wegen der Benutzung eines unsicheren Kanals', 'Are you sure you want to delete this object?': 'Sind Sie sich sicher, dass Sie dieses Objekt löschen wollen?', 'Available Databases and Tables': 'Verfügbare Datenbanken und Tabellen', -'Buy this book': 'Dieses Buch kaufen', "Buy web2py's book": "web2py's Buch kaufen", -'cache': 'cache', 'Cache': 'Cache', 'Cache Cleared': 'Cache geleert', -'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache enthält items die bis zu **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} alt sind.', 'Cache Keys': 'Cache Schlüssel', +'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache enthält items die bis zu **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} alt sind.', 'Cannot be empty': 'Darf nicht leer sein', 'Check to delete': 'Auswählen um zu löschen', 'Clear CACHE?': 'CACHE löschen?', 'Clear DISK': 'DISK löschen', 'Clear RAM': 'RAM löschen', -'Client IP': 'Client IP', 'Community': 'Community', 'Components and Plugins': 'Komponenten und Plugins', 'Config.ini': 'Config.ini', 'Controller': 'Controller', 'Copyright': 'Copyright', -'Created By': 'Erstellt von', -'Created On': 'Erstellt am', 'Current request': 'Derzeitiger Request', 'Current response': 'Derzeitige Response', 'Current session': 'Derzeitige Session', -'customize me!': 'Pass mich an!', -'data uploaded': 'Datei hochgeladen', +'DB Model': 'Muster-DB', +'DISK': 'DISK', +'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK enthält items die bis zu **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} alt sind.', 'Database': 'Datenbank', 'Database %s select': 'Datenbank %s ausgewählt', 'Database Administration (appadmin)': 'Datenbankadministration (appadmin)', -'db': 'db', -'DB Model': 'Muster-DB', 'Delete:': 'Lösche:', 'Demo': 'Demo', 'Deployment Recipes': 'Entwicklungsrezepte', -'Description': 'Beschreibung', -'design': 'Design', 'Design': 'Design', -'DISK': 'DISK', -'Disk Cache Keys': 'Disk Cache Keys', +'Disk Cache Keys': 'Festplatten-Cache-Schlüssel', 'Disk Cleared': 'Disk gelöscht', -'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK enthält items die bis zu **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} alt sind.', 'Documentation': 'Dokumentation', "Don't know what to do?": 'Wissen Sie nicht weiter?', -'done!': 'Fertig!', 'Download': 'Download', -'E-mail': 'E-mail', 'Edit current record': 'Diesen Eintrag editieren', 'Email and SMS': 'Email und SMS', -'Enter an integer between %(min)g and %(max)g': 'Eine Zahl zwischen %(min)g und %(max)g eingeben', -'enter an integer between %(min)g and %(max)g': 'eine Zahl zwischen %(min)g und %(max)g eingeben', -'enter date and time as %(format)s': 'ein Datum und eine Uhrzeit als %(format)s eingeben', 'Errors': 'Fehlermeldungen', -'export as csv file': 'als csv Datei exportieren', 'FAQ': 'FAQ', -'First name': 'Vorname', 'Forms and Validators': 'Forms und Validators', 'Free Applications': 'Kostenlose Anwendungen', 'Graph Model': 'Muster-Graph', -'Group %(group_id)s created': 'Gruppe %(group_id)s erstellt', -'Group ID': 'Gruppen ID', -'Group uniquely assigned to user %(id)s': 'Gruppe eindeutigem Benutzer %(id)s zugewiesen', 'Groups': 'Gruppen', 'Hello World': 'Hallo Welt', -'Hello World ## Kommentar': 'Hallo Welt ', -'Hello World## Kommentar': 'Hallo Welt', -'Helping web2py': 'Helping web2py', +'Helping web2py': 'web 2py helfen', 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Trefferquote: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} und **%(misses)s** %%{miss(misses)})', 'Home': 'Startseite', 'How did you get here?': 'Wie sind Sie hier her gelangt?', -'import': 'Importieren', 'Import/Export': 'Importieren/Exportieren', 'Internal State': 'Innerer Zustand', 'Introduction': 'Einführung', -'Invalid email': 'Ungültige Email', 'Invalid Query': 'Ungültige Query', -'invalid request': 'Ungültiger Request', -'Is Active': 'Ist aktiv', 'Key': 'Schlüssel', -'Last name': 'Nachname', 'Layout': 'Layout', -'Layout Plugins': 'Layout Plugins', -'Layouts': 'Layouts', 'Live Chat': 'Live Chat', 'Log In': 'Log In', -'Logged in': 'Eingeloggt', -'Logged out': 'Ausgeloggt', -'Login': 'Einloggen', -'Logout': 'Ausloggen', 'Lost Password': 'Passwort vergessen', -'Lost password?': 'Passwort vergessen?', 'Manage %(action)s': '%(action)s verwalten', 'Manage Access Control': 'Zugangskontrolle verwalten', 'Manage Cache': 'Cache verwalten', 'Memberships': 'Mitgliedschaften', 'Menu Model': 'Menü-Muster', -'Modified By': 'Verändert von', -'Modified On': 'Verändert am', 'My Sites': 'Meine Seiten', -'Name': 'Name', 'New Record': 'Neuer Eintrag', -'new record inserted': 'neuer Eintrag hinzugefügt', -'next %s rows': 'nächste %s Reihen', 'No databases in this application': 'Keine Datenbank in dieser Anwendung', 'Number of entries: **%s**': 'Nummer der Einträge: **%s**', -'Object or table name': 'Objekt- oder Tabellenname', 'Online book': 'Online Buch', 'Online examples': 'Online Beispiele', -'or import from csv file': 'oder von csv Datei importieren', -'Origin': 'Ursprung', -'Other Plugins': 'Andere Plugins', 'Other Recipes': 'Andere Rezepte', 'Overview': 'Überblick', -'Password': 'Passwort', -"Password fields don't match": 'Passwortfelder sind nicht gleich', 'Permission': 'Erlaubnis', 'Permissions': 'Erlaubnisse', -'please input your password again': 'Bitte geben Sie ihr Passwort erneut ein', 'Plugins': 'Plugins', 'Powered by': 'Unterstützt von', 'Preface': 'Allgemeines', -'previous %s rows': 'vorherige %s Reihen', -'Profile': 'Profil', -'pygraphviz library not found': 'pygraphviz Bibliothek wurde nicht gefunden', 'Python': 'Python', 'Query:': 'Query:', 'Quick Examples': 'Kurze Beispiele', 'RAM': 'RAM', -'RAM Cache Keys': 'RAM Cache Keys', -'Ram Cleared': 'Ram gelöscht', +'RAM Cache Keys': 'RAM Cache-Schlüssel', 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM enthält items die bis zu **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} alt sind.', +'Ram Cleared': 'Ram gelöscht', 'Recipes': 'Rezepte', 'Record': 'Eintrag', -'record does not exist': 'Eintrag existiert nicht', -'Record ID': 'ID des Eintrags', 'Record id': 'id des Eintrags', -'Register': 'Register', -'Registration identifier': 'Registrierungsbezeichnung', -'Registration key': 'Registierungsschlüssel', -'Registration successful': 'Registrierung erfolgreich', -'Remember me (for 30 days)': 'Eingeloggt bleiben (30 Tage lang)', -'Reset Password key': 'Passwortschlüssel zurücksetzen', 'Role': 'Rolle', 'Roles': 'Rollen', 'Rows in Table': 'Tabellenreihen', 'Rows selected': 'Reihen ausgewählt', 'Save model as...': 'Speichere Vorlage als...', -'Semantic': 'Semantik', 'Services': 'Dienste', 'Sign Up': 'Registrieren', 'Size of cache:': 'Cachegröße:', -'state': 'Status', 'Statistics': 'Statistik', 'Stylesheet': 'Stylesheet', -'submit': 'Submit', 'Support': 'Support', 'Table': 'Tabelle', 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'Die "query" ist eine Bedingung wie "db.tabelle1.feld1==\'wert\'". So etwas wie "db.tabelle1.feld1==db.tabelle2.feld2" resultiert in einem SQL JOIN.', 'The Core': 'Der Core', -'The output of the file is a dictionary that was rendered by the view %s': 'Die Ausgabe der Datei ist ein "dictionary", welches vom "view" %s gerendert wurde', 'The Views': 'Die Views', +'The output of the file is a dictionary that was rendered by the view %s': 'Die Ausgabe der Datei ist ein "dictionary", welches vom "view" %s gerendert wurde', 'This App': 'Diese App', -'This email already has an account': 'Diese email besitzt bereits ein Konto', 'Time in Cache (h:m:s)': 'Zeit im Cache (h:m:s)', -'Timestamp': 'Zeitstempel', 'Traceback': 'Zurückverfolgen', 'Twitter': 'Twitter', -'unable to parse csv file': 'csv Datei konnte nicht geparst werden', 'Update:': 'Update:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Benutze (...)&(...) für AND, (...)|(...) für OR, und ~(...) für NOT um komplexere Queries zu erstellen.', 'User': 'Benutzer', -'User %(id)s Logged-in': 'Benutzer %(id)s hat sich eingeloggt', -'User %(id)s Logged-out': 'Benutzer %(id)s hat sich ausgeloggt', -'User %(id)s Registered': 'Benutzer %(id)s hat sich registriert', -'User ID': 'Benutzer ID', 'Users': 'Benutzer', -'value already in database or empty': 'Wert ist bereits in der Datenbank oder leer', -'Verify Password': 'Passwort überprüfen', 'Videos': 'Videos', 'View': 'Ansicht', -'Welcome': 'Willkommen', 'Welcome to web2py!': 'Willkommen bei web2py!', 'Which called the function %s located in the file %s': 'Welche die Funktion %s in der Datei %s aufrief', 'Working...': 'Arbeite...', 'You are successfully running web2py': 'web2py wird erfolgreich ausgeführt', 'You can modify this application and adapt it to your needs': 'Sie können diese Anwendung verändern und Ihren Bedürfnissen anpassen', 'You visited the url %s': 'Sie haben die URL %s besucht', +'``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**nicht verfügbar**``:rot (benötigt die Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] Bibliothek)', +'admin': 'admin', +'appadmin is disabled because insecure channel': 'Appadmin ist deaktiviert, wegen der Benutzung eines unsicheren Kanals', +'cache': 'cache', +'data uploaded': 'Datei hochgeladen', +'db': 'db', +'design': 'Design', +'done!': 'Fertig!', +'export as csv file': 'als csv Datei exportieren', +'import': 'Importieren', +'invalid request': 'Ungültiger Request', +'new record inserted': 'neuer Eintrag hinzugefügt', +'next %s rows': 'nächste %s Reihen', +'or import from csv file': 'oder von csv Datei importieren', +'previous %s rows': 'vorherige %s Reihen', +'pygraphviz library not found': 'pygraphviz Bibliothek wurde nicht gefunden', +'record does not exist': 'Eintrag existiert nicht', +'state': 'Status', +'submit': 'Submit', +'unable to parse csv file': 'csv Datei konnte nicht geparst werden', } diff --git a/applications/welcome/languages/en.py b/applications/welcome/languages/en.py old mode 100755 new mode 100644 index cb50edaa..dd311b79 --- a/applications/welcome/languages/en.py +++ b/applications/welcome/languages/en.py @@ -13,14 +13,29 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'About': 'About', 'Access Control': 'Access Control', 'admin': 'admin', 'Ajax Recipes': 'Ajax Recipes', 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'API Example': 'API Example', 'appadmin is disabled because insecure channel': 'appadmin is disabled because insecure channel', +'Apply changes': 'Apply changes', 'Are you sure you want to delete this object?': 'Are you sure you want to delete this object?', +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'Available Databases and Tables', "Buy web2py's book": "Buy web2py's book", 'cache': 'cache', @@ -29,10 +44,14 @@ 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Cache Keys': 'Cache Keys', 'Cannot be empty': 'Cannot be empty', +'Change Password': 'Change Password', +'Change password': 'Change password', 'Check to delete': 'Check to delete', 'Clear CACHE?': 'Clear CACHE?', 'Clear DISK': 'Clear DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', +'Client IP': 'Client IP', 'Community': 'Community', 'Components and Plugins': 'Components and Plugins', 'Config.ini': 'Config.ini', @@ -50,6 +69,7 @@ 'Delete:': 'Delete:', 'Demo': 'Demo', 'Deployment Recipes': 'Deployment Recipes', +'Description': 'Description', 'design': 'design', 'Design': 'Design', 'DISK': 'DISK', @@ -60,14 +80,25 @@ "Don't know what to do?": "Don't know what to do?", 'done!': 'done!', 'Download': 'Download', +'E-mail': 'E-mail', 'Edit current record': 'Edit current record', 'Email and SMS': 'Email and SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Errors': 'Errors', 'export as csv file': 'export as csv file', 'FAQ': 'FAQ', +'First name': 'First name', 'Forms and Validators': 'Forms and Validators', 'Free Applications': 'Free Applications', +'Function disabled': 'Function disabled', 'Graph Model': 'Graph Model', +'Grid Example': 'Grid Example', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Groups': 'Groups', 'Hello World': 'Hello World', 'Helping web2py': 'Helping web2py', @@ -76,37 +107,68 @@ 'How did you get here?': 'How did you get here?', 'import': 'import', 'Import/Export': 'Import/Export', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': 'Internal State', 'Introduction': 'Introduction', +'Invalid email': 'Invalid email', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', +'Invalid password': 'Invalid password', 'Invalid Query': 'Invalid Query', 'invalid request': 'invalid request', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', +'Last name': 'Last name', 'Layout': 'Layout', 'Live Chat': 'Live Chat', 'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', +'Login': 'Login', +'Login disabled by administrator': 'Login disabled by administrator', +'Logout': 'Logout', 'Lost Password': 'Lost Password', +'Lost your password?': 'Lost your password?', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', 'Manage Cache': 'Manage Cache', 'Memberships': 'Memberships', 'Menu Model': 'Menu Model', 'My Sites': 'My Sites', +'Name': 'Name', +'New password': 'New password', 'New Record': 'New Record', 'new record inserted': 'new record inserted', 'next %s rows': 'next %s rows', 'No databases in this application': 'No databases in this application', 'Number of entries: **%s**': 'Number of entries: **%s**', +'Object or table name': 'Object or table name', +'Old password': 'Old password', 'Online book': 'Online book', 'Online examples': 'Online examples', 'or import from csv file': 'or import from csv file', +'Origin': 'Origin', 'Other Recipes': 'Other Recipes', 'Overview': 'Overview', +'Password': 'Password', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Permission': 'Permission', 'Permissions': 'Permissions', +'please input your password again': 'please input your password again', 'Plugins': 'Plugins', 'Powered by': 'Powered by', 'Preface': 'Preface', 'previous %s rows': 'previous %s rows', +'Profile': 'Profile', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'pygraphviz library not found', 'Python': 'Python', 'Query:': 'Query:', @@ -117,8 +179,25 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Recipes', 'Record': 'Record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'record does not exist', 'Record id': 'Record id', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', +'Register': 'Register', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', +'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', +'Remember me (for 30 days)': 'Remember me (for 30 days)', +'Request reset password': 'Request reset password', +'Reset Password key': 'Reset Password key', 'Role': 'Role', 'Roles': 'Roles', 'Rows in Table': 'Rows in Table', @@ -126,11 +205,13 @@ 'Save model as...': 'Save model as...', 'Services': 'Services', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'Size of cache:': 'Size of cache:', 'state': 'state', 'Statistics': 'Statistics', 'Stylesheet': 'Stylesheet', 'submit': 'submit', +'Submit': 'Submit', 'Support': 'Support', 'Table': 'Table', 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.', @@ -138,20 +219,48 @@ 'The output of the file is a dictionary that was rendered by the view %s': 'The output of the file is a dictionary that was rendered by the view %s', 'The Views': 'The Views', 'This App': 'This App', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', +'Timestamp': 'Timestamp', 'Traceback': 'Traceback', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'unable to parse csv file': 'unable to parse csv file', +'Unable to send email': 'Unable to send email', 'Update:': 'Update:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', +'Verify Password': 'Verify Password', 'Videos': 'Videos', 'View': 'View', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py!': 'Welcome to web2py!', 'Which called the function %s located in the file %s': 'Which called the function %s located in the file %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Working...', 'You are successfully running web2py': 'You are successfully running web2py', 'You can modify this application and adapt it to your needs': 'You can modify this application and adapt it to your needs', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'You visited the url %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/es.py b/applications/welcome/languages/es.py old mode 100755 new mode 100644 index 7865c42d..4c3b7f7d --- a/applications/welcome/languages/es.py +++ b/applications/welcome/languages/es.py @@ -16,9 +16,19 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**no disponible** (requiere la libreria [[guppy http://pypi.python.org/pypi/guppy/ popup]] de Python)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'Ha ocurrido un error, por favor [[recargar %s]] la página', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', '@markmin\x01Number of entries: **%s**': 'Número de entradas: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**no disponible**``:red (Necesita libreria de Python: [[guppy http://pypi.python.org/pypi/guppy/ popup]])', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': 'Hay una nueva versión de web2py disponible', 'A new version of web2py is available: %s': 'Hay una nueva versión de web2py disponible: %s', 'About': 'Acerca de', @@ -42,6 +52,7 @@ 'And': 'Y', 'and rename it (required):': 'y renómbrela (requerido):', 'and rename it:': ' y renómbrelo:', +'API Example': 'API Example', 'appadmin': 'appadmin', 'appadmin is disabled because insecure channel': 'appadmin deshabilitado, el canal no es seguro', 'application "%s" uninstalled': 'aplicación "%s" desinstalada', @@ -58,6 +69,7 @@ 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATENCION: NO EJECUTE VARIAS PRUEBAS SIMULTANEAMENTE, NO SON THREAD SAFE.', 'ATTENTION: you cannot edit the running application!': 'ATENCION: no puede modificar la aplicación que está ejecutandose!', 'Authentication': 'Autenticación', +'Authentication code': 'Authentication code', 'Authentication failed at client DB!': '¡La autenticación ha fallado en la BDD cliente!', 'Authentication failed at main DB!': '¡La autenticación ha fallado en la BDD principal!', 'Available Databases and Tables': 'Bases de datos y tablas disponibles', @@ -156,6 +168,8 @@ 'Editing file "%s"': 'Editando archivo "%s"', 'Email and SMS': 'Correo electrónico y SMS', 'Email sent': 'Correo electrónico enviado', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'End of impersonation': 'Fin de suplantación', 'enter a number between %(min)g and %(max)g': 'introduzca un numero entre %(min)g y %(max)g', 'enter a value': 'introduzca un valor', @@ -184,9 +198,12 @@ 'Forgot username?': '¿Olvidó el nombre de usuario?', 'Forms and Validators': 'Formularios y validadores', 'Free Applications': 'Aplicaciones Libres', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Funciones sin doctests equivalen a pruebas [aceptadas].', 'Graph Model': 'Modelo en Grafo', +'Grid Example': 'Grid Example', 'Group %(group_id)s created': 'Grupo %(group_id)s creado', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'ID de Grupo', 'Group uniquely assigned to user %(id)s': 'Grupo asignado únicamente al usuario %(id)s', 'Groups': 'Grupos', @@ -202,6 +219,7 @@ 'Import/Export': 'Importar/Exportar', 'in': 'en', 'includes': 'incluye', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Index': 'Índice', 'insert new': 'inserte nuevo', 'insert new %s': 'inserte nuevo %s', @@ -213,14 +231,20 @@ 'Invalid action': 'Acción inválida', 'Invalid email': 'Correo electrónico inválido', 'invalid expression': 'expresión inválida', +'Invalid key': 'Invalid key', 'Invalid login': 'Inicio de sesión inválido', 'invalid password': 'contraseña inválida', +'Invalid password': 'Invalid password', 'Invalid Query': 'Consulta inválida', 'invalid request': 'Solicitud inválida', 'Invalid reset password': 'Reinicio de contraseña inválido', 'invalid ticket': 'Tiquete inválido', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Is Active': 'Está Activo', 'Key': 'Llave', +'Key verified': 'Key verified', 'language file "%(filename)s" created/updated': 'archivo de lenguaje "%(filename)s" creado/actualizado', 'Language files (static strings) updated': 'Archivos de lenguaje (cadenas estáticas) actualizados', 'languages': 'lenguajes', @@ -246,6 +270,7 @@ 'Lost Password': 'Contraseña perdida', 'Lost password?': '¿Olvidó la contraseña?', 'lost password?': '¿olvidó la contraseña?', +'Lost your password?': 'Lost your password?', 'Main Menu': 'Menú principal', 'Manage %(action)s': 'Gestionar %(action)s', 'Manage Access Control': 'Gestionar control de acceso', @@ -295,6 +320,7 @@ 'Password changed': 'Contraseña cambiada', "Password fields don't match": 'Los campos de contraseña no coinciden', 'Password reset': 'Reinicio de contraseña', +'Password retrieve': 'Password retrieve', 'Peeking at file': 'Visualizando archivo', 'Permission': 'Permiso', 'Permissions': 'Permisos', @@ -319,14 +345,21 @@ 'Recipes': 'Recetas', 'Record': 'Registro', 'Record %(id)s created': 'Registro %(id)s creado', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', 'Record Created': 'Registro Creado', +'Record Deleted': 'Record Deleted', 'record does not exist': 'el registro no existe', 'Record ID': 'ID de Registro', 'Record id': 'Id de registro', +'Record Updated': 'Record Updated', 'register': 'regístrese', 'Register': 'Regístrese', 'Registration identifier': 'Identificador de Registro', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Llave de registro', +'Registration needs verification': 'Registration needs verification', 'Registration successful': 'Registro con éxito', 'reload': 'recargar', 'Remember me (for 30 days)': 'Recuérdame (durante 30 días)', @@ -351,6 +384,7 @@ 'session expired': 'sesión expirada', 'shell': 'terminal', 'Sign Up': 'Registrarse', +'Sign up': 'Sign up', 'site': 'sitio', 'Size of cache:': 'Tamaño de la Caché:', 'some files could not be removed': 'algunos archivos no pudieron ser removidos', @@ -385,6 +419,7 @@ 'There are no views': 'No hay vistas', 'these files are served without processing, your images go here': 'estos archivos son servidos sin procesar, sus imágenes van aquí', 'This App': 'Esta Aplicación', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', 'This email already has an account': 'Este correo electrónico ya tiene una cuenta', 'This is a copy of the scaffolding application': 'Esta es una copia de la aplicación de andamiaje', 'This is the %(filename)s template': 'Esta es la plantilla %(filename)s', @@ -405,12 +440,14 @@ 'TSV (Excel compatible)': 'TSV (compatible con Excel)', 'TSV (Excel compatible, hidden cols)': 'TSV (compatible con Excel, columnas ocultas)', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Unable to check for upgrades': 'No es posible verificar la existencia de actualizaciones', 'unable to create application "%s"': 'no es posible crear la aplicación "%s"', 'unable to delete file "%(filename)s"': 'no es posible eliminar el archivo "%(filename)s"', 'Unable to download': 'No es posible la descarga', 'Unable to download app': 'No es posible descargar la aplicación', 'unable to parse csv file': 'no es posible analizar el archivo CSV', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'no es posible instalar "%s"', 'uncheck all': 'desmarcar todos', 'uninstall': 'desinstalar', @@ -428,9 +465,12 @@ 'User %(id)s Logged-out': 'El usuario %(id)s finalizó la sesión', 'User %(id)s Password changed': 'Contraseña del usuario %(id)s cambiada', 'User %(id)s Password reset': 'Contraseña del usuario %(id)s reiniciada', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', 'User %(id)s Profile updated': 'Actualizado el perfil del usuario %(id)s', 'User %(id)s Registered': 'Usuario %(id)s Registrado', 'User %(id)s Username retrieved': 'Se ha recuperado el nombre de usuario del usuario %(id)s', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User %(username)s Logged-in': 'El usuario %(username)s inició la sesión', "User '%(username)s' Logged-in": "El usuario '%(username)s' inició la sesión", "User '%(username)s' Logged-out": "El usuario '%(username)s' finalizó la sesión", @@ -438,6 +478,7 @@ 'User ID': 'ID de Usuario', 'User Logged-out': 'El usuario finalizó la sesión', 'Username': 'Nombre de usuario', +'Username already taken': 'Username already taken', 'Username retrieve': 'Recuperar nombre de usuario', 'Users': 'Usuarios', 'value already in database or empty': 'el valor ya existe en la base de datos o está vacío', @@ -455,14 +496,20 @@ 'web2py is up to date': 'web2py está actualizado', 'web2py Recent Tweets': 'Tweets Recientes de web2py', 'Welcome': 'Bienvenido', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': 'Bienvenido %s', 'Welcome to web2py': 'Bienvenido a web2py', 'Welcome to web2py!': '¡Bienvenido a web2py!', 'Which called the function %s located in the file %s': 'La cual llamó la función %s localizada en el archivo %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Trabajando...', 'YES': 'SÍ', 'You are successfully running web2py': 'Usted está ejecutando web2py exitosamente', 'You can modify this application and adapt it to your needs': 'Usted puede modificar esta aplicación y adaptarla a sus necesidades', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'Usted visitó la url %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', 'Your username is: %(username)s': 'Su nombre de usuario es: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/fr-ca.py b/applications/welcome/languages/fr-ca.py old mode 100755 new mode 100644 index 611a107e..bd93ce54 --- a/applications/welcome/languages/fr-ca.py +++ b/applications/welcome/languages/fr-ca.py @@ -13,8 +13,19 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'about': 'à propos', 'About': 'À propos', 'Access Control': "Contrôle d'accès", @@ -23,9 +34,12 @@ 'Administrative interface': "Interface d'administration", 'Ajax Recipes': 'Recettes Ajax', 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'API Example': 'API Example', 'appadmin is disabled because insecure channel': "appadmin est désactivée parce que le canal n'est pas sécurisé", +'Apply changes': 'Apply changes', 'Are you sure you want to delete this object?': 'Êtes-vous sûr de vouloir supprimer cet objet?', 'Authentication': 'Authentification', +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'Bases de données et tables disponibles', 'Buy this book': 'Acheter ce livre', "Buy web2py's book": "Buy web2py's book", @@ -36,11 +50,14 @@ 'Cache Keys': 'Cache Keys', 'Cannot be empty': 'Ne peut pas être vide', 'change password': 'changer le mot de passe', +'Change Password': 'Change Password', +'Change password': 'Change password', 'Check to delete': 'Cliquez pour supprimer', 'Check to delete:': 'Cliquez pour supprimer:', 'Clear CACHE?': 'Vider le CACHE?', 'Clear DISK': 'Vider le DISQUE', 'Clear RAM': 'Vider la RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Client IP': 'IP client', 'Community': 'Communauté', 'Components and Plugins': 'Composants et Plugiciels', @@ -79,6 +96,9 @@ 'edit profile': 'modifier le profil', 'Edit This App': 'Modifier cette application', 'Email and SMS': 'Courriel et texto', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enter an integer between %(min)g and %(max)g': 'Enter an integer between %(min)g and %(max)g', 'enter an integer between %(min)g and %(max)g': 'entrer un entier compris entre %(min)g et %(max)g', 'Errors': 'Erreurs', @@ -89,7 +109,9 @@ 'Free Applications': 'Applications gratuites', 'Function disabled': 'Fonction désactivée', 'Graph Model': 'Représentation graphique du modèle', +'Grid Example': 'Grid Example', 'Group %(group_id)s created': '%(group_id)s groupe créé', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'ID du groupe', 'Group uniquely assigned to user %(id)s': "Groupe unique attribué à l'utilisateur %(id)s", 'Groups': 'Groupes', @@ -100,16 +122,26 @@ 'How did you get here?': 'How did you get here?', 'import': 'importer', 'Import/Export': 'Importer/Exporter', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Index': 'Index', 'insert new': 'insérer un nouveau', 'insert new %s': 'insérer un nouveau %s', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': 'État interne', 'Introduction': 'Présentation', 'Invalid email': 'Courriel invalide', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', +'Invalid password': 'Invalid password', 'Invalid Query': 'Requête Invalide', 'invalid request': 'requête invalide', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Is Active': 'Est actif', 'Key': 'Clé', +'Key verified': 'Key verified', 'Last name': 'Nom', 'Layout': 'Mise en page', 'Layout Plugins': 'Plugins de mise en page', @@ -120,13 +152,17 @@ 'loading...': 'chargement...', 'Log In': 'Connexion', 'Logged in': 'Connecté', +'Logged out': 'Logged out', 'login': 'connexion', 'Login': 'Connexion', +'Login disabled by administrator': 'Login disabled by administrator', 'logout': 'déconnexion', +'Logout': 'Logout', 'lost password': 'mot de passe perdu', 'Lost Password': 'Mot de passe perdu', 'Lost password?': 'Mot de passe perdu?', 'lost password?': 'mot de passe perdu?', +'Lost your password?': 'Lost your password?', 'Main Menu': 'Menu principal', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', @@ -137,6 +173,7 @@ 'Modified On': 'Modifié le', 'My Sites': 'Mes sites', 'Name': 'Nom', +'New password': 'New password', 'New Record': 'Nouvel enregistrement', 'new record inserted': 'nouvel enregistrement inséré', 'next %s rows': '%s prochaine lignes', @@ -144,6 +181,7 @@ 'No databases in this application': "Cette application n'a pas de bases de données", 'Number of entries: **%s**': 'Number of entries: **%s**', 'Object or table name': 'Objet ou nom de table', +'Old password': 'Old password', 'Online book': 'Online book', 'Online examples': 'Exemples en ligne', 'or import from csv file': "ou importer d'un fichier CSV", @@ -153,7 +191,10 @@ 'Overview': 'Présentation', 'password': 'mot de passe', 'Password': 'Mot de passe', +'Password changed': 'Password changed', "Password fields don't match": 'Les mots de passe ne correspondent pas', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Permission': 'Permission', 'Permissions': 'Permissions', 'please input your password again': "S'il vous plaît entrer votre mot de passe à nouveau", @@ -163,6 +204,8 @@ 'previous %s rows': '%s lignes précédentes', 'previous 100 rows': '100 lignes précédentes', 'profile': 'profil', +'Profile': 'Profile', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'Bibliothèque pygraphviz introuvable', 'Python': 'Python', 'Query:': 'Requête:', @@ -175,8 +218,11 @@ 'Recipes': 'Recettes', 'Record': 'enregistrement', 'Record %(id)s created': 'Enregistrement %(id)s créé', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', 'Record %(id)s updated': 'Enregistrement %(id)s modifié', 'Record Created': 'Enregistrement créé', +'Record Deleted': 'Record Deleted', 'record does not exist': "l'archive n'existe pas", 'Record ID': "ID de l'enregistrement", 'Record id': "id de l'enregistrement", @@ -184,7 +230,9 @@ 'Register': "S'inscrire", 'register': "s'inscrire", 'Registration identifier': "Identifiant d'inscription", +'Registration is pending approval': 'Registration is pending approval', 'Registration key': "Clé d'enregistrement", +'Registration needs verification': 'Registration needs verification', 'Registration successful': 'Inscription réussie', 'Remember me (for 30 days)': 'Se souvenir de moi (pendant 30 jours)', 'Request reset password': 'Demande de réinitialiser le mot clé', @@ -198,6 +246,7 @@ 'Semantic': 'Sémantique', 'Services': 'Services', 'Sign Up': "S'inscrire", +'Sign up': 'Sign up', 'Size of cache:': 'Taille de la mémoire cache:', 'state': 'état', 'Statistics': 'Statistiques', @@ -213,19 +262,35 @@ 'The output of the file is a dictionary that was rendered by the view %s': 'La sortie de ce fichier est un dictionnaire qui été restitué par la vue %s', 'The Views': 'Les Vues', 'This App': 'Cette Appli', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'This is a copy of the scaffolding application': "Ceci est une copie de l'application échafaudage", 'Time in Cache (h:m:s)': 'Temps en Cache (h:m:s)', 'Timestamp': 'Horodatage', 'Traceback': 'Traceback', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'unable to parse csv file': "incapable d'analyser le fichier cvs", +'Unable to send email': 'Unable to send email', 'Update:': 'Mise à jour:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Employez (...)&(...) pour AND, (...)|(...) pour OR, and ~(...) pour NOT afin de construire des requêtes plus complexes.', 'User': 'Utilisateur', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': 'Utilisateur %(id)s connecté', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', 'User %(id)s Registered': 'Utilisateur %(id)s enregistré', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'ID utilisateur', 'User Voice': "Voix de l'utilisateur", +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Utilisateurs', 'value already in database or empty': 'valeur déjà dans la base ou inexistante', 'Verify Password': 'Vérifiez le mot de passe', @@ -233,12 +298,19 @@ 'View': 'Vue', 'Web2py': 'Web2py', 'Welcome': 'Bienvenue', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': 'Bienvenue %s', 'Welcome to web2py': 'Bienvenue à web2py', 'Welcome to web2py!': 'Bienvenue à web2py!', 'Which called the function %s located in the file %s': 'Qui a appelé la fonction %s se trouvant dans le fichier %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Traitement en cours...', 'You are successfully running web2py': 'Vous exécutez avec succès web2py', 'You can modify this application and adapt it to your needs': "Vous pouvez modifier cette application et l'adapter à vos besoins", +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': "Vous avez visité l'URL %s", +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/fr.py b/applications/welcome/languages/fr.py old mode 100755 new mode 100644 index 730b0485..a599483b --- a/applications/welcome/languages/fr.py +++ b/applications/welcome/languages/fr.py @@ -13,8 +13,19 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'about': 'à propos', 'About': 'À propos', 'Access Control': "Contrôle d'accès", @@ -23,9 +34,12 @@ 'Administrative interface': "Interface d'administration", 'Ajax Recipes': 'Recettes Ajax', 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'API Example': 'API Example', 'appadmin is disabled because insecure channel': "appadmin est désactivée parce que le canal n'est pas sécurisé", +'Apply changes': 'Apply changes', 'Are you sure you want to delete this object?': 'Êtes-vous sûr de vouloir supprimer cet objet?', 'Authentication': 'Authentification', +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'Bases de données et tables disponibles', 'Buy this book': 'Acheter ce livre', "Buy web2py's book": "Buy web2py's book", @@ -36,11 +50,14 @@ 'Cache Keys': 'Cache Keys', 'Cannot be empty': 'Ne peut pas être vide', 'change password': 'changer le mot de passe', +'Change Password': 'Change Password', +'Change password': 'Change password', 'Check to delete': 'Cliquez pour supprimer', 'Check to delete:': 'Cliquez pour supprimer:', 'Clear CACHE?': 'Vider le CACHE?', 'Clear DISK': 'Vider le DISQUE', 'Clear RAM': 'Vider la RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Client IP': 'IP client', 'Community': 'Communauté', 'Components and Plugins': 'Composants et Plugiciels', @@ -79,6 +96,9 @@ 'edit profile': 'modifier le profil', 'Edit This App': 'Modifier cette application', 'Email and SMS': 'Courriel et texto', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enter an integer between %(min)g and %(max)g': 'Enter an integer between %(min)g and %(max)g', 'enter an integer between %(min)g and %(max)g': 'entrez un entier entre %(min)g et %(max)g', 'Errors': 'Erreurs', @@ -89,7 +109,9 @@ 'Free Applications': 'Applications gratuites', 'Function disabled': 'Fonction désactivée', 'Graph Model': 'Représentation graphique du modèle', +'Grid Example': 'Grid Example', 'Group %(group_id)s created': '%(group_id)s groupe créé', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'ID du groupe', 'Group uniquely assigned to user %(id)s': "Groupe unique attribué à l'utilisateur %(id)s", 'Groups': 'Groupes', @@ -100,16 +122,26 @@ 'How did you get here?': 'How did you get here?', 'import': 'importer', 'Import/Export': 'Importer/Exporter', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Index': 'Index', 'insert new': 'insérer un nouveau', 'insert new %s': 'insérer un nouveau %s', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': 'État interne', 'Introduction': 'Présentation', 'Invalid email': 'Courriel invalide', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', +'Invalid password': 'Invalid password', 'Invalid Query': 'Requête Invalide', 'invalid request': 'requête invalide', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Is Active': 'Est actif', 'Key': 'Clé', +'Key verified': 'Key verified', 'Last name': 'Nom', 'Layout': 'Mise en page', 'Layout Plugins': 'Plugins de mise en page', @@ -120,13 +152,17 @@ 'loading...': 'chargement...', 'Log In': 'Connexion', 'Logged in': 'Connecté', +'Logged out': 'Logged out', 'login': 'connexion', 'Login': 'Connexion', +'Login disabled by administrator': 'Login disabled by administrator', 'logout': 'déconnexion', +'Logout': 'Logout', 'lost password': 'mot de passe perdu', 'Lost Password': 'Mot de passe perdu', 'Lost password?': 'Mot de passe perdu?', 'lost password?': 'mot de passe perdu?', +'Lost your password?': 'Lost your password?', 'Main Menu': 'Menu principal', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', @@ -137,6 +173,7 @@ 'Modified On': 'Modifié le', 'My Sites': 'Mes sites', 'Name': 'Nom', +'New password': 'New password', 'New Record': 'Nouvel enregistrement', 'new record inserted': 'nouvel enregistrement inséré', 'next %s rows': '%s prochaine lignes', @@ -144,6 +181,7 @@ 'No databases in this application': "Cette application n'a pas de bases de données", 'Number of entries: **%s**': 'Number of entries: **%s**', 'Object or table name': 'Objet ou nom de table', +'Old password': 'Old password', 'Online book': 'Online book', 'Online examples': 'Exemples en ligne', 'or import from csv file': "ou importer d'un fichier CSV", @@ -153,7 +191,10 @@ 'Overview': 'Présentation', 'password': 'mot de passe', 'Password': 'Mot de passe', +'Password changed': 'Password changed', "Password fields don't match": 'Les mots de passe ne correspondent pas', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Permission': 'Permission', 'Permissions': 'Permissions', 'please input your password again': "S'il vous plaît entrer votre mot de passe à nouveau", @@ -163,6 +204,8 @@ 'previous %s rows': '%s lignes précédentes', 'previous 100 rows': '100 lignes précédentes', 'profile': 'profil', +'Profile': 'Profile', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'Bibliothèque pygraphviz introuvable', 'Python': 'Python', 'Query:': 'Requête:', @@ -175,8 +218,11 @@ 'Recipes': 'Recettes', 'Record': 'enregistrement', 'Record %(id)s created': 'Enregistrement %(id)s créé', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', 'Record %(id)s updated': 'Enregistrement %(id)s modifié', 'Record Created': 'Enregistrement créé', +'Record Deleted': 'Record Deleted', 'record does not exist': "l'archive n'existe pas", 'Record ID': "ID de l'enregistrement", 'Record id': "id de l'enregistrement", @@ -184,7 +230,9 @@ 'Register': "S'inscrire", 'register': "s'inscrire", 'Registration identifier': "Identifiant d'inscription", +'Registration is pending approval': 'Registration is pending approval', 'Registration key': "Clé d'enregistrement", +'Registration needs verification': 'Registration needs verification', 'Registration successful': 'Inscription réussie', 'Remember me (for 30 days)': 'Se souvenir de moi (pendant 30 jours)', 'Request reset password': 'Demande de réinitialiser le mot clé', @@ -198,6 +246,7 @@ 'Semantic': 'Sémantique', 'Services': 'Services', 'Sign Up': "S'inscrire", +'Sign up': 'Sign up', 'Size of cache:': 'Taille de la mémoire cache:', 'state': 'état', 'Statistics': 'Statistiques', @@ -213,31 +262,54 @@ 'The output of the file is a dictionary that was rendered by the view %s': 'La sortie de ce fichier est un dictionnaire qui été restitué par la vue %s', 'The Views': 'Les Vues', 'This App': 'Cette Appli', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'This is a copy of the scaffolding application': "Ceci est une copie de l'application échafaudage", 'Time in Cache (h:m:s)': 'Temps en Cache (h:m:s)', 'Timestamp': 'Horodatage', 'Traceback': 'Traceback', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'unable to parse csv file': "incapable d'analyser le fichier cvs", +'Unable to send email': 'Unable to send email', 'Update:': 'Mise à jour:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Employez (...)&(...) pour AND, (...)|(...) pour OR, and ~(...) pour NOT afin de construire des requêtes plus complexes.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': 'Utilisateur %(id)s connecté', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', 'User %(id)s Registered': 'Utilisateur %(id)s enregistré', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'ID utilisateur', 'User Voice': "Voix de l'utilisateur", +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Verify Password': 'Vérifiez le mot de passe', 'Videos': 'Vidéos', 'View': 'Présentation', 'Web2py': 'Web2py', 'Welcome': 'Bienvenue', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': 'Bienvenue %s', 'Welcome to web2py': 'Bienvenue à web2py', 'Welcome to web2py!': 'Bienvenue à web2py!', 'Which called the function %s located in the file %s': 'Qui a appelé la fonction %s se trouvant dans le fichier %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Working...', 'You are successfully running web2py': 'Vous exécutez avec succès web2py', 'You can modify this application and adapt it to your needs': "Vous pouvez modifier cette application et l'adapter à vos besoins", +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': "Vous avez visité l'URL %s", +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/hi.py b/applications/welcome/languages/hi.py old mode 100755 new mode 100644 index 5c741e1c..c113fef7 --- a/applications/welcome/languages/hi.py +++ b/applications/welcome/languages/hi.py @@ -13,7 +13,19 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'About': 'About', 'Access Control': 'Access Control', 'admin': 'admin', @@ -21,8 +33,11 @@ 'Administrative interface': 'प्रशासनिक इंटरफेस के लिए यहाँ क्लिक करें', 'Ajax Recipes': 'Ajax Recipes', 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'API Example': 'API Example', 'appadmin is disabled because insecure channel': 'अप आडमिन (appadmin) अक्षम है क्योंकि असुरक्षित चैनल', +'Apply changes': 'Apply changes', 'Are you sure you want to delete this object?': 'Are you sure you want to delete this object?', +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'उपलब्ध डेटाबेस और तालिका', 'Buy this book': 'Buy this book', "Buy web2py's book": "Buy web2py's book", @@ -34,10 +49,13 @@ 'Cannot be empty': 'खाली नहीं हो सकता', 'Change Password': 'पासवर्ड बदलें', 'change password': 'change password', +'Change password': 'Change password', 'Check to delete': 'हटाने के लिए चुनें', 'Clear CACHE?': 'Clear CACHE?', 'Clear DISK': 'Clear DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', +'Client IP': 'Client IP', 'Community': 'Community', 'Components and Plugins': 'Components and Plugins', 'Config.ini': 'Config.ini', @@ -56,6 +74,7 @@ 'Delete:': 'मिटाना:', 'Demo': 'Demo', 'Deployment Recipes': 'Deployment Recipes', +'Description': 'Description', 'design': 'रचना करें', 'Design': 'Design', 'DISK': 'DISK', @@ -66,18 +85,29 @@ "Don't know what to do?": "Don't know what to do?", 'done!': 'हो गया!', 'Download': 'Download', +'E-mail': 'E-mail', 'Edit': 'Edit', 'Edit current record': 'वर्तमान रेकॉर्ड संपादित करें ', 'edit profile': 'edit profile', 'Edit Profile': 'प्रोफ़ाइल संपादित करें', 'Edit This App': 'Edit This App', 'Email and SMS': 'Email and SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Errors': 'Errors', 'export as csv file': 'csv फ़ाइल के रूप में निर्यात', 'FAQ': 'FAQ', +'First name': 'First name', 'Forms and Validators': 'Forms and Validators', 'Free Applications': 'Free Applications', +'Function disabled': 'Function disabled', 'Graph Model': 'Graph Model', +'Grid Example': 'Grid Example', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Groups': 'Groups', 'Hello from MyApp': 'Hello from MyApp', 'Hello World': 'Hello World', @@ -87,24 +117,40 @@ 'How did you get here?': 'How did you get here?', 'import': 'import', 'Import/Export': 'आयात / निर्यात', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Index': 'Index', 'insert new': 'नया डालें', 'insert new %s': 'नया %s डालें', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': 'आंतरिक स्थिति', 'Introduction': 'Introduction', +'Invalid email': 'Invalid email', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', +'Invalid password': 'Invalid password', 'Invalid Query': 'अमान्य प्रश्न', 'invalid request': 'अवैध अनुरोध', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', +'Last name': 'Last name', 'Layout': 'Layout', 'Layout Plugins': 'Layout Plugins', 'Layouts': 'Layouts', 'Live Chat': 'Live Chat', 'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'login': 'login', 'Login': 'लॉग इन', +'Login disabled by administrator': 'Login disabled by administrator', 'logout': 'logout', 'Logout': 'लॉग आउट', 'Lost Password': 'पासवर्ड खो गया', +'Lost your password?': 'Lost your password?', 'Main Menu': 'Main Menu', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', @@ -112,25 +158,38 @@ 'Memberships': 'Memberships', 'Menu Model': 'Menu Model', 'My Sites': 'My Sites', +'Name': 'Name', +'New password': 'New password', 'New Record': 'नया रेकॉर्ड', 'new record inserted': 'नया रेकॉर्ड डाला', 'next %s rows': 'next %s rows', 'next 100 rows': 'अगले 100 पंक्तियाँ', 'No databases in this application': 'इस अनुप्रयोग में कोई डेटाबेस नहीं हैं', 'Number of entries: **%s**': 'Number of entries: **%s**', +'Object or table name': 'Object or table name', +'Old password': 'Old password', 'Online book': 'Online book', 'Online examples': 'ऑनलाइन उदाहरण के लिए यहाँ क्लिक करें', 'or import from csv file': 'या csv फ़ाइल से आयात', +'Origin': 'Origin', 'Other Plugins': 'Other Plugins', 'Other Recipes': 'Other Recipes', 'Overview': 'Overview', +'Password': 'Password', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Permission': 'Permission', 'Permissions': 'Permissions', +'please input your password again': 'please input your password again', 'Plugins': 'Plugins', 'Powered by': 'Powered by', 'Preface': 'Preface', 'previous %s rows': 'previous %s rows', 'previous 100 rows': 'पिछले 100 पंक्तियाँ', +'Profile': 'Profile', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'pygraphviz library not found', 'Python': 'Python', 'Query:': 'प्रश्न:', @@ -141,10 +200,26 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Recipes', 'Record': 'Record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'रिकॉर्ड मौजूद नहीं है', 'Record id': 'रिकॉर्ड पहचानकर्ता (आईडी)', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', 'Register': 'पंजीकृत (रजिस्टर) करना ', 'register': 'register', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', +'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', +'Remember me (for 30 days)': 'Remember me (for 30 days)', +'Request reset password': 'Request reset password', +'Reset Password key': 'Reset Password key', 'Role': 'Role', 'Roles': 'Roles', 'Rows in Table': 'तालिका में पंक्तियाँ ', @@ -153,11 +228,13 @@ 'Semantic': 'Semantic', 'Services': 'Services', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'Size of cache:': 'Size of cache:', 'state': 'स्थिति', 'Statistics': 'Statistics', 'Stylesheet': 'Stylesheet', 'submit': 'submit', +'Submit': 'Submit', 'Support': 'Support', 'Sure you want to delete this object?': 'सुनिश्चित हैं कि आप इस वस्तु को हटाना चाहते हैं?', 'Table': 'तालिका', @@ -166,22 +243,50 @@ 'The output of the file is a dictionary that was rendered by the view %s': 'The output of the file is a dictionary that was rendered by the view %s', 'The Views': 'The Views', 'This App': 'This App', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', +'Timestamp': 'Timestamp', 'Traceback': 'Traceback', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'unable to parse csv file': 'csv फ़ाइल पार्स करने में असमर्थ', +'Unable to send email': 'Unable to send email', 'Update:': 'अद्यतन करना:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', +'Verify Password': 'Verify Password', 'Videos': 'Videos', 'View': 'View', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': 'Welcome %s', 'Welcome to web2py': 'वेब२पाइ (web2py) में आपका स्वागत है', 'Welcome to web2py!': 'Welcome to web2py!', 'Which called the function %s located in the file %s': 'Which called the function %s located in the file %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Working...', 'You are successfully running web2py': 'You are successfully running web2py', 'You can modify this application and adapt it to your needs': 'You can modify this application and adapt it to your needs', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'You visited the url %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/hu.py b/applications/welcome/languages/hu.py old mode 100755 new mode 100644 index 992140ee..5672d225 --- a/applications/welcome/languages/hu.py +++ b/applications/welcome/languages/hu.py @@ -13,7 +13,19 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'About': 'About', 'Access Control': 'Access Control', 'admin': 'admin', @@ -21,8 +33,11 @@ 'Administrative interface': 'az adminisztrációs felületért kattints ide', 'Ajax Recipes': 'Ajax Recipes', 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'API Example': 'API Example', 'appadmin is disabled because insecure channel': 'az appadmin a biztonságtalan csatorna miatt letiltva', +'Apply changes': 'Apply changes', 'Are you sure you want to delete this object?': 'Are you sure you want to delete this object?', +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'Elérhető adatbázisok és táblák', 'Buy this book': 'Buy this book', "Buy web2py's book": "Buy web2py's book", @@ -33,10 +48,13 @@ 'Cache Keys': 'Cache Keys', 'Cannot be empty': 'Nem lehet üres', 'change password': 'jelszó megváltoztatása', +'Change Password': 'Change Password', +'Change password': 'Change password', 'Check to delete': 'Törléshez válaszd ki', 'Clear CACHE?': 'Clear CACHE?', 'Clear DISK': 'Clear DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Client IP': 'Client IP', 'Community': 'Community', 'Components and Plugins': 'Components and Plugins', @@ -73,14 +91,22 @@ 'edit profile': 'profil szerkesztése', 'Edit This App': 'Alkalmazást szerkeszt', 'Email and SMS': 'Email and SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Errors': 'Errors', 'export as csv file': 'exportál csv fájlba', 'FAQ': 'FAQ', 'First name': 'First name', 'Forms and Validators': 'Forms and Validators', 'Free Applications': 'Free Applications', +'Function disabled': 'Function disabled', 'Graph Model': 'Graph Model', +'Grid Example': 'Grid Example', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Groups': 'Groups', 'Hello World': 'Hello Világ', 'Helping web2py': 'Helping web2py', @@ -89,25 +115,41 @@ 'How did you get here?': 'How did you get here?', 'import': 'import', 'Import/Export': 'Import/Export', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Index': 'Index', 'insert new': 'új beillesztése', 'insert new %s': 'új beillesztése %s', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': 'Internal State', 'Introduction': 'Introduction', 'Invalid email': 'Invalid email', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', +'Invalid password': 'Invalid password', 'Invalid Query': 'Hibás lekérdezés', 'invalid request': 'hibás kérés', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'Last name': 'Last name', 'Layout': 'Szerkezet', 'Layout Plugins': 'Layout Plugins', 'Layouts': 'Layouts', 'Live Chat': 'Live Chat', 'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'login': 'belép', +'Login': 'Login', +'Login disabled by administrator': 'Login disabled by administrator', 'logout': 'kilép', +'Logout': 'Logout', 'lost password': 'elveszett jelszó', 'Lost Password': 'Lost Password', +'Lost your password?': 'Lost your password?', 'Main Menu': 'Főmenü', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', @@ -116,12 +158,15 @@ 'Menu Model': 'Menü model', 'My Sites': 'My Sites', 'Name': 'Name', +'New password': 'New password', 'New Record': 'Új bejegyzés', 'new record inserted': 'új bejegyzés felvéve', 'next %s rows': 'next %s rows', 'next 100 rows': 'következő 100 sor', 'No databases in this application': 'Nincs adatbázis ebben az alkalmazásban', 'Number of entries: **%s**': 'Number of entries: **%s**', +'Object or table name': 'Object or table name', +'Old password': 'Old password', 'Online book': 'Online book', 'Online examples': 'online példákért kattints ide', 'or import from csv file': 'vagy betöltés csv fájlból', @@ -130,13 +175,20 @@ 'Other Recipes': 'Other Recipes', 'Overview': 'Overview', 'Password': 'Password', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Permission': 'Permission', 'Permissions': 'Permissions', +'please input your password again': 'please input your password again', 'Plugins': 'Plugins', 'Powered by': 'Powered by', 'Preface': 'Preface', 'previous %s rows': 'previous %s rows', 'previous 100 rows': 'előző 100 sor', +'Profile': 'Profile', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'pygraphviz library not found', 'Python': 'Python', 'Query:': 'Lekérdezés:', @@ -147,12 +199,25 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Recipes', 'Record': 'bejegyzés', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'bejegyzés nem létezik', 'Record ID': 'Record ID', 'Record id': 'bejegyzés id', +'Record Updated': 'Record Updated', 'Register': 'Register', 'register': 'regisztráció', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', +'Remember me (for 30 days)': 'Remember me (for 30 days)', +'Request reset password': 'Request reset password', 'Reset Password key': 'Reset Password key', 'Role': 'Role', 'Roles': 'Roles', @@ -162,11 +227,13 @@ 'Semantic': 'Semantic', 'Services': 'Services', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'Size of cache:': 'Size of cache:', 'state': 'állapot', 'Statistics': 'Statistics', 'Stylesheet': 'Stylesheet', 'submit': 'submit', +'Submit': 'Submit', 'Support': 'Support', 'Sure you want to delete this object?': 'Biztos törli ezt az objektumot?', 'Table': 'tábla', @@ -176,24 +243,50 @@ 'The output of the file is a dictionary that was rendered by the view %s': 'The output of the file is a dictionary that was rendered by the view %s', 'The Views': 'The Views', 'This App': 'This App', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', 'Timestamp': 'Timestamp', 'Traceback': 'Traceback', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'unable to parse csv file': 'nem lehet a csv fájlt beolvasni', +'Unable to send email': 'Unable to send email', 'Update:': 'Frissít:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'User ID', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', +'Verify Password': 'Verify Password', 'Videos': 'Videos', 'View': 'Nézet', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': 'Welcome %s', 'Welcome to web2py': 'Isten hozott a web2py-ban', 'Welcome to web2py!': 'Welcome to web2py!', 'Which called the function %s located in the file %s': 'Which called the function %s located in the file %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Working...', 'You are successfully running web2py': 'You are successfully running web2py', 'You can modify this application and adapt it to your needs': 'You can modify this application and adapt it to your needs', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'You visited the url %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/id.py b/applications/welcome/languages/id.py old mode 100755 new mode 100644 index 621d3d8a..c690eb03 --- a/applications/welcome/languages/id.py +++ b/applications/welcome/languages/id.py @@ -31,7 +31,19 @@ '1 year ago': '1 tahun yang lalu', '< Previous': '< Sebelumnya', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'About': 'Tentang', 'About application': 'Tentang Aplikasi', 'Access Control': 'Access Control', @@ -48,13 +60,16 @@ 'And': 'Dan', 'and rename it:': 'dan memberi nama baru itu:', 'Answer': 'Jawaban', +'API Example': 'API Example', 'appadmin is disabled because insecure channel': 'AppAdmin dinonaktifkan karena kanal tidak aman', 'application "%s" uninstalled': 'applikasi "%s" dihapus', 'application compiled': 'aplikasi dikompilasi', 'Application name:': 'Nama Applikasi:', +'Apply changes': 'Apply changes', 'are not used yet': 'tidak digunakan lagi', 'Are you sure you want to delete this object?': 'Apakah Anda yakin ingin menghapus ini?', 'Are you sure you want to uninstall application "%s"?': 'Apakah Anda yakin ingin menghapus aplikasi "%s"?', +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'Database dan Tabel yang tersedia', 'Back': 'Kembali', 'Buy this book': 'Beli buku ini', @@ -70,6 +85,7 @@ 'Cannot be empty': 'Tidak boleh kosong', 'Change admin password': 'Ubah kata sandi admin', 'Change password': 'Ubah kata sandi', +'Change Password': 'Change Password', 'Check for upgrades': 'Periksa upgrade', 'Check to delete': 'Centang untuk menghapus', 'Checking for upgrades...': 'Memeriksa untuk upgrade...', @@ -78,7 +94,9 @@ 'Clear CACHE?': 'Hapus CACHE?', 'Clear DISK': 'Hapus DISK', 'Clear RAM': 'Hapus RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Klik baris untuk memperluas traceback', +'Client IP': 'Client IP', 'Close': 'Tutup', 'collapse/expand all': 'kempis / memperluas semua', 'Community': 'Komunitas', @@ -130,11 +148,14 @@ 'download layouts': 'unduh layouts', 'download plugins': 'unduh plugins', 'Duration': 'Durasi', +'E-mail': 'E-mail', 'Edit': 'Mengedit', 'Edit application': 'Mengedit Aplikasi', 'Edit current record': 'Edit current record', 'Email and SMS': 'Email and SMS', 'Email sent': 'Email dikirim', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'enter a valid email address': 'masukkan alamat email yang benar', 'enter a valid URL': 'masukkan URL yang benar', 'enter a value': 'masukkan data', @@ -148,12 +169,17 @@ 'FAQ': 'FAQ', 'filter': 'menyaring', 'First Name': 'Nama Depan', +'First name': 'First name', 'Forgot username?': 'Lupa nama pengguna?', 'Forms and Validators': 'Forms and Validators', 'Free Applications': 'Aplikasi Gratis', +'Function disabled': 'Function disabled', 'Gender': 'Jenis Kelamin', 'Graph Model': 'Graph Model', +'Grid Example': 'Grid Example', 'Group %(group_id)s created': 'Grup %(group_id)s dibuat', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', 'Group uniquely assigned to user %(id)s': 'Grup unik yang diberikan kepada pengguna %(id)s', 'Groups': 'Grup', 'Guest': 'Tamu', @@ -167,19 +193,30 @@ 'import': 'impor', 'Import/Export': 'Impor/Ekspor', 'includes': 'termasuk', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Install': 'Memasang', 'Installation': 'Instalasi', 'Installed applications': 'Aplikasi yang diinstal', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': 'Internal State', 'Introduction': 'Pengenalan', 'Invalid email': 'Email tidak benar', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', +'Invalid password': 'Invalid password', 'Invalid Query': 'Invalid Query', 'invalid request': 'invalid request', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'Language': 'Bahasa', 'languages': 'bahasa', 'Languages': 'Bahasa', 'Last Name': 'Nama Belakang', +'Last name': 'Last name', 'Layout': 'Layout', 'License for': 'Lisensi untuk', 'Live Chat': 'Live Chat', @@ -188,10 +225,12 @@ 'Logged in': 'Masuk', 'Logged out': 'Keluar', 'Login': 'Masuk', +'Login disabled by administrator': 'Login disabled by administrator', 'Login to the Administrative Interface': 'Masuk ke antarmuka Administrasi', 'Logout': 'Keluar', 'Lost Password': 'Lupa Kata Sandi', 'Lost password?': 'Lupa kata sandi?', +'Lost your password?': 'Lost your password?', 'Maintenance': 'Pemeliharaan', 'Manage': 'Mengelola', 'Manage %(action)s': 'Manage %(action)s', @@ -204,6 +243,7 @@ 'Modules': 'Modul', 'modules': 'modul', 'My Sites': 'Situs Saya', +'Name': 'Name', 'New': 'Baru', 'new application "%s" created': 'aplikasi baru "%s" dibuat', 'New password': 'Kata sandi baru', @@ -220,6 +260,7 @@ 'not a Zip Code': 'bukan Kode Pos', 'Note': 'Catatan', 'Number of entries: **%s**': 'Number of entries: **%s**', +'Object or table name': 'Object or table name', 'Old password': 'Kata sandi lama', 'Online book': 'Online book', 'Online examples': 'Contoh Online', @@ -227,6 +268,7 @@ 'or alternatively': 'atau alternatif', 'Or Get from URL:': 'Atau Dapatkan dari URL:', 'or import from csv file': 'atau impor dari file csv', +'Origin': 'Origin', 'Other Plugins': 'Plugin Lainnya', 'Other Recipes': 'Resep Lainnya', 'Overview': 'Ikhtisar', @@ -237,6 +279,8 @@ 'Password': 'Kata sandi', 'Password changed': 'Kata sandi berubah', "Password fields don't match": 'Kata sandi tidak sama', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Permission': 'Permission', 'Permissions': 'Permissions', 'please input your password again': 'silahkan masukan kata sandi anda lagi', @@ -263,15 +307,28 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Resep', 'Record': 'Record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'record does not exist', 'Record id': 'Record id', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', 'Register': 'Daftar', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', +'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', 'Registration successful': 'Pendaftaran berhasil', 'reload': 'memuat kembali', 'Reload routes': 'Memuat rute kembali', 'Remember me (for 30 days)': 'Ingat saya (selama 30 hari)', 'Remove compiled': 'Hapus Kompilasi', 'Request reset password': 'Meminta reset kata sandi', +'Reset Password key': 'Reset Password key', 'Role': 'Role', 'Roles': 'Roles', 'Rows in Table': 'Baris dalam Tabel', @@ -286,6 +343,7 @@ 'Service': 'Layanan', 'Services': 'Services', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'Site': 'Situs', 'Size of cache:': 'Ukuran cache:', 'starts with': 'dimulai dengan', @@ -295,6 +353,7 @@ 'Statistics': 'Statistik', 'Stylesheet': 'Stylesheet', 'submit': 'submit', +'Submit': 'Submit', 'Support': 'Mendukung', 'Table': 'Tabel', 'test': 'tes', @@ -309,15 +368,20 @@ 'These files are not served, they are only available from within your app': 'File-file ini tidak dilayani, mereka hanya tersedia dari dalam aplikasi Anda', 'These files are served without processing, your images go here': 'File-file ini disajikan tanpa pengolahan, gambar Anda di sini', 'This App': 'App Ini', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'Time in Cache (h:m:s)': 'Waktu di Cache (h: m: s)', +'Timestamp': 'Timestamp', 'To create a plugin, name a file/folder plugin_[name]': 'Untuk membuat sebuah plugin, nama file / folder plugin_ [nama]', 'too short': 'terlalu pendek', 'Traceback': 'Traceback', 'Translation strings for the application': 'Terjemahan string untuk aplikasi', 'Try the mobile interface': 'Coba antarmuka ponsel', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Unable to download because:': 'Tidak dapat mengunduh karena:', 'unable to parse csv file': 'tidak mampu mengurai file csv', +'Unable to send email': 'Unable to send email', 'update all languages': 'memperbarui semua bahasa', 'Update:': 'Perbarui:', 'Upload': 'Unggah', @@ -327,12 +391,21 @@ 'upload plugin file:': 'unggah file plugin:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': 'Pengguna %(id)s Masuk', 'User %(id)s Logged-out': 'Pengguna %(id)s Keluar', 'User %(id)s Password changed': 'Pengguna %(id)s Kata Sandi berubah', 'User %(id)s Password reset': 'Pengguna %(id)s Kata Sandi telah direset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', 'User %(id)s Profile updated': 'Pengguna %(id)s Profil diperbarui', 'User %(id)s Registered': 'Pengguna %(id)s Terdaftar', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'value already in database or empty': 'data sudah ada dalam database atau kosong', 'value not allowed': 'data tidak benar', @@ -348,10 +421,17 @@ 'web2py Recent Tweets': 'Tweet web2py terbaru', 'Website': 'Situs Web', 'Welcome': 'Selamat Datang', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py!': 'Selamat Datang di web2py!', 'Which called the function %s located in the file %s': 'Which called the function %s located in the file %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Working...', 'You are successfully running web2py': 'Anda berhasil menjalankan web2py', 'You can modify this application and adapt it to your needs': 'Anda dapat memodifikasi aplikasi ini dan menyesuaikan dengan kebutuhan Anda', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'Anda mengunjungi url %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/it.py b/applications/welcome/languages/it.py old mode 100755 new mode 100644 index 2b123998..fbe3bbcb --- a/applications/welcome/languages/it.py +++ b/applications/welcome/languages/it.py @@ -21,9 +21,19 @@ '>': '>', '>=': '>=', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', '@markmin\x01Number of entries: **%s**': 'Numero di entità: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'About': 'About', 'Access Control': 'Controllo Accessi', 'Add': 'Aggiungi', @@ -36,7 +46,9 @@ 'And': 'E', 'API Example': 'API Example', 'appadmin is disabled because insecure channel': 'Amministrazione (appadmin) disabilitata: comunicazione non sicura', +'Apply changes': 'Apply changes', 'Are you sure you want to delete this object?': 'Sicuro di voler cancellare questo oggetto ?', +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'Database e tabelle disponibili', 'Back': 'Indietro', 'Buy this book': 'Compra questo libro', @@ -49,17 +61,20 @@ 'Cannot be empty': 'Non può essere vuoto', 'Change password': 'Cambia Password', 'change password': 'Cambia password', +'Change Password': 'Change Password', 'Check to delete': 'Seleziona per cancellare', 'Clear': 'Resetta', 'Clear CACHE?': 'Resetta CACHE?', 'Clear DISK': 'Resetta DISK', 'Clear RAM': 'Resetta RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Client IP': 'Client IP', 'Close': 'Chiudi', 'Cognome': 'Cognome', 'Community': 'Community', 'Components and Plugins': 'Componenti and Plugin', 'Config.ini': 'Config.ini', +'Confirm Password': 'Confirm Password', 'contains': 'contiene', 'Controller': 'Controller', 'Copyright': 'Copyright', @@ -99,9 +114,13 @@ 'Edit This App': 'Modifica questa applicazione', 'Email and SMS': 'Email e SMS', 'Email non valida': 'Email non valida', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'enter a number between %(min)g and %(max)g': 'enter a number between %(min)g and %(max)g', 'Enter an integer between %(min)g and %(max)g': 'Enter an integer between %(min)g and %(max)g', 'enter an integer between %(min)g and %(max)g': 'inserisci un intero tra %(min)g e %(max)g', +'Enter an integer greater than or equal to %(min)g': 'Enter an integer greater than or equal to %(min)g', 'Errors': 'Errori', 'Errors in form, please check it out.': 'Errori nel form, ricontrollalo', 'export as csv file': 'esporta come file CSV', @@ -111,9 +130,11 @@ 'Forgot username?': 'Dimenticato lo username?', 'Forms and Validators': 'Forms and Validators', 'Free Applications': 'Free Applications', +'Function disabled': 'Function disabled', 'Graph Model': 'Graph Model', 'Grid Example': 'Grid Example', 'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'ID Gruppo', 'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Groups': 'Groups', @@ -128,17 +149,26 @@ 'HTML': 'HTML', 'import': 'importa', 'Import/Export': 'Importa/Esporta', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Index': 'Indice', 'insert new': 'inserisci nuovo', 'insert new %s': 'inserisci nuovo %s', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': 'Stato interno', 'Introduction': 'Introduzione', 'Invalid email': 'Email non valida', +'Invalid key': 'Invalid key', 'Invalid login': 'Login non valido', +'Invalid password': 'Invalid password', 'Invalid Query': 'Richiesta (query) non valida', 'invalid request': 'richiesta non valida', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Is Active': "E' attivo", 'Key': 'Chiave', +'Key verified': 'Key verified', 'Last name': 'Cognome', 'Layout': 'Layout', 'Layout Plugins': 'Layout Plugins', @@ -149,11 +179,13 @@ 'Logged out': 'Disconnesso', 'login': 'accesso', 'Login': 'Login', +'Login disabled by administrator': 'Login disabled by administrator', 'logout': 'uscita', 'Logout': 'Logout', 'Lost Password': 'Password Smarrita', 'Lost password?': 'Password smarrita?', 'lost password?': 'dimenticato la password?', +'Lost your password?': 'Lost your password?', 'Main Menu': 'Menu principale', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', @@ -187,7 +219,10 @@ 'Other Recipes': 'Other Recipes', 'Overview': 'Overview', 'Password': 'Password', +'Password changed': 'Password changed', "Password fields don't match": 'I campi password non sono uguali', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Permission': 'Permission', 'Permissions': 'Permissions', 'please input your password again': 'perfavore reimmeti la tua password', @@ -197,6 +232,7 @@ 'previous %s rows': 'previous %s rows', 'previous 100 rows': '100 righe precedenti', 'Profile': 'Profilo', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'pygraphviz library not found', 'Python': 'Python', 'Query:': 'Richiesta (query):', @@ -207,13 +243,22 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Recipes', 'Record': 'Record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'il record non esiste', 'Record ID': 'Record ID', 'Record id': 'Record id', +'Record Updated': 'Record Updated', 'Register': 'Registrati', 'register': 'registrazione', 'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Chiave di Registazione', +'Registration needs verification': 'Registration needs verification', 'Registration successful': 'Registrazione avvenuta', 'reload': 'reload', 'Remember me (for 30 days)': 'Ricordami (per 30 giorni)', @@ -246,6 +291,7 @@ 'The output of the file is a dictionary that was rendered by the view %s': 'L\'output del file è un "dictionary" che è stato visualizzato dalla vista %s', 'The Views': 'The Views', 'This App': 'This App', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', 'This email already has an account': 'This email already has an account', 'This is a copy of the scaffolding application': "Questa è una copia dell'applicazione di base (scaffold)", 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', @@ -255,24 +301,35 @@ 'TSV (Excel compatible)': 'TSV (Excel compatibile)', 'TSV (Excel compatible, hidden cols)': 'TSV (Excel compatibile, hidden cols)', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'unable to parse csv file': 'non riesco a decodificare questo file CSV', +'Unable to send email': 'Unable to send email', 'Update': 'Aggiorna', 'Update:': 'Aggiorna:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Per costruire richieste (query) più complesse si usano (...)&(...) come "e" (AND), (...)|(...) come "o" (OR), e ~(...) come negazione (NOT).', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': 'User %(id)s Logged-in', 'User %(id)s Logged-out': 'User %(id)s Logged-out', 'User %(id)s Password changed': 'User %(id)s Password changed', 'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', 'User %(id)s Profile updated': 'User %(id)s Profile updated', 'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'ID Utente', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'value already in database or empty': 'valore già presente nel database o vuoto', 'Verify Password': 'Verifica Password', 'Videos': 'Videos', 'View': 'Vista', 'Welcome': 'Benvenuto', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': 'Benvenuto %s', 'Welcome to web2py': 'Benvenuto su web2py', 'Welcome to web2py!': 'Benvenuto in web2py!', @@ -282,5 +339,10 @@ 'XML': 'XML', 'You are successfully running web2py': 'Stai eseguendo web2py con successo', 'You can modify this application and adapt it to your needs': 'Puoi modificare questa applicazione adattandola alle tue necessità', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': "Hai visitato l'URL %s", +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/my-mm.py b/applications/welcome/languages/my-mm.py old mode 100755 new mode 100644 index 38bd3523..edd9efb7 --- a/applications/welcome/languages/my-mm.py +++ b/applications/welcome/languages/my-mm.py @@ -15,8 +15,19 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'About': 'အကြောင်း', 'Access Control': 'အသုံးပြု ခြင်းဆိုင်ရာ ထိန်းချုပ်ရန်', 'Additional code for your application': 'Additional code for your application', @@ -28,11 +39,14 @@ 'Ajax Recipes': 'Ajax Recipes', 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', 'and rename it:': 'and rename it:', +'API Example': 'API Example', 'appadmin is disabled because insecure channel': 'စိတ်မချရသော လမ်းကြောင်းမှ ဝင်ရောက်သဖြင့် appadmin ကို အသုံးပြု၍ မရပါ', 'Application name:': 'Application name:', +'Apply changes': 'Apply changes', 'are not used': 'အသုံးမပြုပါ', 'are not used yet': 'အသုံးမပြုသေးပါ', 'Are you sure you want to delete this object?': 'သင် ဒီအရာ ဖျက်ရန် သေချာပါသလား။', +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'အသုံးပြုနိုင်သော ဒေတာဘေစ့်များနှင့် ဇယားများ', 'Buy this book': 'ဒီစာအုပ်ကို ဝယ်ပါ', "Buy web2py's book": "Buy web2py's book", @@ -44,12 +58,15 @@ 'can be a git repo': 'can be a git repo', 'Cannot be empty': 'အလွတ် မဖြစ်ရပါ', 'Change admin password': 'Change admin password', +'Change Password': 'Change Password', +'Change password': 'Change password', 'Check to delete': 'ဖျက်ရန် စစ်ဆေးပါ', 'Checking for upgrades...': 'အဆင့်မြှင့်တင်မှုများအတွက် စစ်ဆေးနေသည် ...', 'Clean': 'ရှင်းလင်းရန်', 'Clear CACHE?': 'CACHE ကို ရှင်းလင်းမည်မှာ ဟုတ်ပါသလား။', 'Clear DISK': 'DISK ကို ရှင်းလင်းမည်။', 'Clear RAM': 'RAM ကို ရှင်းလင်းမည်။', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Client IP': 'Client IP', 'collapse/expand all': 'collapse/expand all', 'Community': 'အသိုင်းအဝိုင်း', @@ -106,6 +123,9 @@ 'Edit application': 'Application ကို ပြင်ရန်', 'Edit current record': 'လက်ရှိ မှတ်တမ်းကို ပြင်ရန်', 'Email and SMS': 'အီးမေးလ်နှင့် SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enable': 'ဖွင့်ရန်', 'enter an integer between %(min)g and %(max)g': 'enter an integer between %(min)g and %(max)g', 'Errors': 'အမှားများ', @@ -117,9 +137,14 @@ 'First name': 'အမည်၏ ပထမဆုံး စာလုံး', 'Forms and Validators': 'Forms and Validators', 'Free Applications': 'အခမဲ့ Applications', +'Function disabled': 'Function disabled', 'graph model': 'graph model', 'Graph Model': 'Graph Model', +'Grid Example': 'Grid Example', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Groups': 'အဖွဲ့များ', 'Hello World': 'မင်္ဂလာပါ ကမ္ဘာကြီး။', 'Help': 'အကူအညီ', @@ -130,15 +155,25 @@ 'import': 'သွင်းယူရန်', 'Import/Export': 'သွင်းယူရန်/ထုတ်ယူရန်', 'includes': 'includes', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Install': 'Install', 'Installed applications': 'ထည့်သွင်းပြီး application များ', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': 'Internal State', 'Introduction': 'မိတ်ဆက်', 'Invalid email': 'အီးမေးလ် ဖြည့်သွင်းမှုမှားနေသည်', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', +'Invalid password': 'Invalid password', 'Invalid Query': 'Invalid Query', 'invalid request': 'invalid request', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Is Active': 'Is Active', 'Key': 'Key', +'Key verified': 'Key verified', 'Language': 'ဘာသာစကား', 'languages': 'ဘာသာစကားများ', 'Languages': 'ဘာသာစကားများ', @@ -148,11 +183,15 @@ 'Layouts': 'အပြင်အဆင်များ', 'Live Chat': 'တိုက်ရိုက် ဆက်သွယ် ပြောကြားရန်', 'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'Login': 'ဝင်ရောက်အသုံးပြုရန်', +'Login disabled by administrator': 'Login disabled by administrator', 'Login to the Administrative Interface': 'Login to the Administrative Interface', 'Logout': 'ထွက်ရန်', 'Lost Password': 'စကားဝှက် မသိတော့ပါ', 'Lost password?': 'စကားဝှက် မသိတော့ဘူးလား။', +'Lost your password?': 'Lost your password?', 'Manage': 'စီမံခန့်ခွဲရန်', 'Manage %(action)s': '%(action)s ကို စီမံရန်', 'Manage Access Control': 'အသုံးပြုခြင်းဆိုင်ရာ ထိန်းချုပ်မှု စီမံခန့်ခွဲရန်', @@ -168,6 +207,7 @@ 'My Sites': 'ကျွန်ုပ်၏ Site များ', 'Name': 'အမည်', 'New application wizard': 'New application wizard', +'New password': 'New password', 'New Record': 'မှတ်တမ်း အသစ်', 'new record inserted': 'မှတ်တမ်း အသစ် ဖြည့်သွင်းပြီးပြီ', 'New simple application': 'ရိုးရိုး application အသစ်', @@ -176,6 +216,7 @@ 'no package selected': 'no package selected', 'Number of entries: **%s**': 'Number of entries: **%s**', 'Object or table name': 'Object or table name', +'Old password': 'Old password', 'Online book': 'Online book', 'Online examples': 'အွန်လိုင်း နမူနာများ', 'or alternatively': 'or alternatively', @@ -189,7 +230,10 @@ 'Pack all': 'အားလုံးကို ထုပ်ပိုးရန်', 'Pack custom': 'ရွေးချယ်ထုပ်ပိုးရန်', 'Password': 'စကားဝှက်', +'Password changed': 'Password changed', "Password fields don't match": 'စကားဝှက်များ ကိုက်ညီမှု မရှိပါ', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Permission': 'ခွင့်ပြုချက်', 'Permissions': 'ခွင့်ပြုချက်များ', 'please input your password again': 'ကျေးဇူးပြု၍ စကားဝှက်ကို ထပ်မံ ဖြည့်သွင်းပေးပါ', @@ -201,6 +245,8 @@ 'previous %s rows': 'previous %s rows', 'Private files': 'Private files', 'private files': 'private files', +'Profile': 'Profile', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'pygraphviz library ကို မတွေ့ပါ', 'Python': 'Python', 'Query:': 'Query:', @@ -211,12 +257,22 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Recipes', 'Record': 'မှတ်တမ်း', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'မှတ်တမ်း မရှိပါ', 'Record ID': 'Record ID', 'Record id': 'Record id', +'Record Updated': 'Record Updated', 'Register': 'မှတ်ပုံတင်ရန်', 'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Reload routes': 'Reload routes', 'Remember me (for 30 days)': 'Remember me (for 30 days)', 'Request reset password': 'စကားဝှက် အသစ် တောင်းဆိုရန်', @@ -232,6 +288,7 @@ 'Services': 'Services', 'shell': 'shell', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'Site': 'Site', 'Size of cache:': 'Size of cache:', 'Start wizard': 'Start wizard', @@ -257,6 +314,7 @@ 'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'These files are served without processing, your images go here': 'These files are served without processing, your images go here', 'This App': 'ဒီ App', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', 'This email already has an account': 'ဒီအီးမေးလ်တွင် အကောင့် ရှိပြီး ဖြစ်ပါသည်', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', 'Timestamp': 'Timestamp', @@ -265,7 +323,9 @@ 'Translation strings for the application': 'Translation strings for the application', 'Try the mobile interface': 'Try the mobile interface', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'unable to parse csv file': 'unable to parse csv file', +'Unable to send email': 'Unable to send email', 'Uninstall': 'Uninstall', 'update all languages': 'update all languages', 'Update:': 'Update:', @@ -276,7 +336,21 @@ 'upload plugin file:': 'upload plugin file:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.', 'User': 'အသုံးပြုသူ', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'User ID', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'အသုံးပြုသူများ', 'Verify Password': 'စကားဝှက်ကို အတည်ပြုပါ', 'Version': 'Version', @@ -287,12 +361,19 @@ 'Views': 'ဗျူးများ', 'Web Framework': 'Web Framework', 'Welcome': 'ကြိုဆိုပါ၏', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py!': 'web2py မှ ကြိုဆိုပါသည်။', 'Which called the function %s located in the file %s': 'Which called the function %s located in the file %s', +'Wiki Example': 'Wiki Example', 'Working...': 'ဆောင်ရွက်နေပါသည် ။ ။ ။', 'You are successfully running web2py': 'သင်သည် web2py ကို အောင်မြင်စွာ လည်ပတ်မောင်းနှင်စေပါသည်။', 'You can modify this application and adapt it to your needs': 'သင် ဒီ application ကို ပြုပြင်မွမ်းမံနိုင်ပါသည်။ ထို့အပြင် သင့်လိုအပ်ချက်များနှင့် ကိုက်ညီစေရန် ပြုလုပ်နိုင်ပါသည်။', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'သင် လည်ပတ်ခဲ့သော URL %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', 'စကားဝှက် အသစ် တောင်းဆိုရန်': 'စကားဝှက် အသစ် တောင်းဆိုရန်', 'မှတ်ပုံတင်ရန်': 'မှတ်ပုံတင်ရန်', 'ဝင်ရောက်အသုံးပြုရန်': 'ဝင်ရောက်အသုံးပြုရန်', diff --git a/applications/welcome/languages/my.py b/applications/welcome/languages/my.py old mode 100755 new mode 100644 index ef29f272..6d3295ba --- a/applications/welcome/languages/my.py +++ b/applications/welcome/languages/my.py @@ -31,7 +31,19 @@ '1 year ago': '1 tahun yang lalu', '< Previous': '< Sebelumnya', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'About': 'Mengenai', 'Access Control': 'Access Control', 'Add': 'Tambah', @@ -43,9 +55,12 @@ 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', 'And': 'Dan', 'and rename it:': 'dan menamakan itu:', +'API Example': 'API Example', 'appadmin is disabled because insecure channel': 'appadmin is disabled because insecure channel', +'Apply changes': 'Apply changes', 'are not used yet': 'tidak digunakan lagi', 'Are you sure you want to delete this object?': 'Apakah anda yakin anda mahu memadam ini?', +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'Available Databases and Tables', 'Back': 'Kembali', 'Buy this book': 'Beli buku ini', @@ -60,13 +75,16 @@ 'Cannot be empty': 'Tidak boleh kosong', 'Change admin password': 'Tukar kata laluan admin', 'Change password': 'Tukar kata laluan', +'Change Password': 'Change Password', 'Check to delete': 'Check to delete', 'Clean': 'Bersihkan', 'Clear': 'Hapus', 'Clear CACHE?': 'Hapus CACHE?', 'Clear DISK': 'Hapus DISK', 'Clear RAM': 'Hapus RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Click row to expand traceback': 'Klik baris untuk mengembangkan traceback', +'Client IP': 'Client IP', 'Close': 'Tutup', 'Community': 'Komuniti', 'Components and Plugins': 'Komponen dan Plugin', @@ -92,6 +110,7 @@ 'Delete:': 'Hapus:', 'Demo': 'Demo', 'Deployment Recipes': 'Deployment Recipes', +'Description': 'Description', 'design': 'disain', 'Design': 'Design', 'direction: ltr': 'arah: ltr', @@ -104,10 +123,13 @@ 'done!': 'selesai!', 'Download': 'Unduh', 'Duration': 'Tempoh', +'E-mail': 'E-mail', 'Edit current record': 'Edit current record', 'Email : ': 'Emel : ', 'Email and SMS': 'Email and SMS', 'Email sent': 'Emel dihantar', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'enter a valid email address': 'masukkan alamat emel yang benar', 'enter a valid URL': 'masukkan URL yang benar', 'enter a value': 'masukkan data', @@ -119,12 +141,17 @@ 'File': 'Fail', 'filter': 'menapis', 'First Name': 'Nama Depan', +'First name': 'First name', 'Forgot username?': 'Lupa nama pengguna?', 'Forms and Validators': 'Forms and Validators', 'Free Applications': 'Aplikasi Percuma', +'Function disabled': 'Function disabled', 'Gender': 'Jenis Kelamin', 'Graph Model': 'Graph Model', +'Grid Example': 'Grid Example', 'Group %(group_id)s created': 'Kumpulan %(group_id)s dicipta', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', 'Group uniquely assigned to user %(id)s': 'Kumpulan unik yang diberikan kepada pengguna %(id)s', 'Groups': 'Kumpulan', 'Hello World': 'Halo Dunia', @@ -137,18 +164,29 @@ 'import': 'import', 'Import/Export': 'Import/Eksport', 'includes': 'termasuk', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Install': 'Pasang', 'Installation': 'Pemasangan', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': 'Internal State', 'Introduction': 'Pengenalan', 'Invalid email': 'Emel tidak benar', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', +'Invalid password': 'Invalid password', 'Invalid Query': 'Invalid Query', 'invalid request': 'invalid request', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'Language': 'Bahasa', 'languages': 'bahasa', 'Languages': 'Bahasa', 'Last Name': 'Nama Belakang', +'Last name': 'Last name', 'Layout': 'Layout', 'License for': 'lesen untuk', 'Live Chat': 'Live Chat', @@ -157,9 +195,11 @@ 'Logged in': 'Masuk', 'Logged out': 'Keluar', 'Login': 'Masuk', +'Login disabled by administrator': 'Login disabled by administrator', 'Logout': 'Keluar', 'Lost Password': 'Lupa Kata Laluan', 'Lost password?': 'Lupa kata laluan?', +'Lost your password?': 'Lost your password?', 'Maintenance': 'Penyelenggaraan', 'Manage': 'Menguruskan', 'Manage %(action)s': 'Manage %(action)s', @@ -172,6 +212,7 @@ 'Modules': 'Modul', 'modules': 'modul', 'My Sites': 'Laman Saya', +'Name': 'Name', 'New': 'Baru', 'New password': 'Kata laluan baru', 'New Record': 'New Record', @@ -184,6 +225,7 @@ 'No ticket_storage.txt found under /private folder': 'Ticket_storage.txt tidak dijumpai di bawah folder /private', 'not a Zip Code': 'bukan Pos', 'Number of entries: **%s**': 'Number of entries: **%s**', +'Object or table name': 'Object or table name', 'Old password': 'Kata laluan lama', 'Online book': 'Online book', 'Online examples': 'Contoh Online', @@ -191,6 +233,7 @@ 'or alternatively': 'atau sebagai alternatif', 'Or Get from URL:': 'Atau Dapatkan dari URL:', 'or import from csv file': 'atau import dari file csv', +'Origin': 'Origin', 'Other Plugins': 'Plugin Lain', 'Other Recipes': 'Resipi Lain', 'Overview': 'Tinjauan', @@ -198,6 +241,8 @@ 'Password': 'Kata laluan', 'Password changed': 'Kata laluan berubah', "Password fields don't match": 'Kata laluan tidak sama', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Permission': 'Permission', 'Permissions': 'Permissions', 'please input your password again': 'sila masukan kata laluan anda lagi', @@ -223,14 +268,27 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Resipi', 'Record': 'Record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'record does not exist', 'Record id': 'Record id', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', 'Register': 'Daftar', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', +'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', 'Registration successful': 'Pendaftaran berjaya', 'reload': 'memuat kembali', 'Reload routes': 'Memuat laluan kembali', 'Remember me (for 30 days)': 'Ingat saya (selama 30 hari)', 'Request reset password': 'Meminta reset kata laluan', +'Reset Password key': 'Reset Password key', 'Role': 'Role', 'Roles': 'Roles', 'Rows in Table': 'Rows in Table', @@ -243,6 +301,7 @@ 'Send Email': 'Kirim Emel', 'Services': 'Services', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'Size of cache:': 'Saiz cache:', 'Solution': 'Penyelesaian', 'starts with': 'bermula dengan', @@ -252,6 +311,7 @@ 'Statistics': 'Statistik', 'Stylesheet': 'Stylesheet', 'submit': 'submit', +'Submit': 'Submit', 'Support': 'Menyokong', 'Table': 'Table', 'test': 'ujian', @@ -264,14 +324,19 @@ 'These files are not served, they are only available from within your app': 'Fail-fail ini tidak disampaikan, mereka hanya boleh didapati dari dalam aplikasi anda', 'These files are served without processing, your images go here': 'Ini fail disampaikan tanpa pemprosesan, imej anda di sini', 'This App': 'App Ini', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'Time in Cache (h:m:s)': 'Waktu di Cache (h: m: s)', +'Timestamp': 'Timestamp', 'Title': 'Judul', 'To create a plugin, name a file/folder plugin_[name]': 'Untuk mencipta plugin, nama fail/folder plugin_ [nama]', 'too short': 'terlalu pendek', 'Traceback': 'Traceback', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Unable to download because:': 'Tidak dapat memuat turun kerana:', 'unable to parse csv file': 'tidak mampu mengurai file csv', +'Unable to send email': 'Unable to send email', 'update all languages': 'mengemaskini semua bahasa', 'Update:': 'Kemas kini:', 'Upgrade': 'Menaik taraf', @@ -281,12 +346,21 @@ 'upload plugin file:': 'unggah fail plugin:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': 'Pengguna %(id)s Masuk', 'User %(id)s Logged-out': 'Pengguna %(id)s Keluar', 'User %(id)s Password changed': 'Pengguna %(id)s Kata Laluan berubah', 'User %(id)s Password reset': 'Pengguna %(id)s Kata Laluan telah direset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', 'User %(id)s Profile updated': 'Pengguna %(id)s Profil dikemaskini', 'User %(id)s Registered': 'Pengguna %(id)s Didaftarkan', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'value not allowed': 'data tidak benar', 'Verify Password': 'Pengesahan Kata Laluan', @@ -300,10 +374,17 @@ 'web2py Recent Tweets': 'Tweet terbaru web2py', 'Website': 'Laman Web', 'Welcome': 'Selamat Datang', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py!': 'Selamat Datang di web2py!', 'Which called the function %s located in the file %s': 'Which called the function %s located in the file %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Working...', 'You are successfully running web2py': 'Anda berjaya menjalankan web2py', 'You can modify this application and adapt it to your needs': 'Anda boleh mengubah suai aplikasi ini dan menyesuaikan dengan keperluan anda', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'Anda melawat url %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/nl.py b/applications/welcome/languages/nl.py old mode 100755 new mode 100644 index 7ff72e46..efe4083d --- a/applications/welcome/languages/nl.py +++ b/applications/welcome/languages/nl.py @@ -24,7 +24,19 @@ '>': '>', '>=': '>=', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': 'Een nieuwe versie van web2py is beschikbaar', 'A new version of web2py is available: %s': 'Een nieuwe versie van web2py is beschikbaar: %s', 'About': 'Over', @@ -47,11 +59,13 @@ 'And': 'En', 'and rename it (required):': 'en hernoem deze (vereist)', 'and rename it:': 'en hernoem:', +'API Example': 'API Example', 'appadmin': 'appadmin', 'appadmin is disabled because insecure channel': 'appadmin is uitgezet vanwege een onveilig kanaal', 'application "%s" uninstalled': 'applicatie "%s" gedeïnstalleerd', 'application compiled': 'applicatie gecompileerd', 'application is compiled and cannot be designed': 'applicatie is gecompileerd en kan niet worden ontworpen', +'Apply changes': 'Apply changes', 'Are you sure you want to delete file "%s"?': 'Weet je zeker dat je bestand "%s" wilt verwijderen?', 'Are you sure you want to delete this object?': 'Weet je zeker dat je dit object wilt verwijderen?', 'Are you sure you want to uninstall application "%s"?': 'Weet je zeker dat je applicatie "%s" wilt deïnstalleren?', @@ -59,6 +73,7 @@ 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'LET OP: TESTEN IS NIET THREAD SAFE, PROBEER NIET GELIJKTIJDIG MEERDERE TESTS TE DOEN.', 'ATTENTION: you cannot edit the running application!': 'LET OP: je kan de applicatie die nu draait niet editen!', 'Authentication': 'Authenticatie', +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'Beschikbare databases en tabellen', 'Back': 'Terug', 'Buy this book': 'Koop dit boek', @@ -83,6 +98,7 @@ 'Clear CACHE?': 'Leeg CACHE?', 'Clear DISK': 'Leeg DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'click to check for upgrades': 'Klik om voor upgrades te controleren', 'Client IP': 'Client IP', 'Community': 'Community', @@ -149,6 +165,9 @@ 'Editing file': 'Bewerk bestand', 'Editing file "%s"': 'Bewerk bestand "%s"', 'Email and SMS': 'E-mail en SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'enter a number between %(min)g and %(max)g': 'geef een getal tussen %(min)g en %(max)g', 'enter an integer between %(min)g and %(max)g': 'geef een integer tussen %(min)g en %(max)g', 'Error logs for "%(app)s"': 'Error logs voor "%(app)s"', @@ -174,9 +193,12 @@ 'Forbidden': 'Verboden', 'Forms and Validators': 'Formulieren en Validators', 'Free Applications': 'Gratis Applicaties', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Functies zonder doctests zullen resulteren in [passed] tests.', 'Graph Model': 'Graph Model', +'Grid Example': 'Grid Example', 'Group %(group_id)s created': 'Groep %(group_id)s gemaakt', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'Groep ID', 'Group uniquely assigned to user %(id)s': 'Groep is uniek toegekend aan gebruiker %(id)s', 'Groups': 'Groepen', @@ -190,22 +212,31 @@ 'import': 'import', 'Import/Export': 'Import/Export', 'includes': 'includes', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Index': 'Index', 'insert new': 'voeg nieuwe', 'insert new %s': 'voeg nieuwe %s', 'Installed applications': 'Geïnstalleerde applicaties', +'Insufficient privileges': 'Insufficient privileges', 'internal error': 'interne error', 'Internal State': 'Interne State', 'Introduction': 'Introductie', 'Invalid action': 'Ongeldige actie', 'Invalid email': 'Ongeldig emailadres', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'ongeldig wachtwoord', 'Invalid password': 'Ongeldig wachtwoord', 'Invalid Query': 'Ongeldige Query', 'invalid request': 'ongeldige request', +'Invalid reset password': 'Invalid reset password', 'invalid ticket': 'ongeldige ticket', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Is Active': 'Is Actief', 'Key': 'Key', +'Key verified': 'Key verified', 'language file "%(filename)s" created/updated': 'taalbestand "%(filename)s" gemaakt/geupdate', 'Language files (static strings) updated': 'Taalbestanden (statische strings) geupdate', 'languages': 'talen', @@ -224,11 +255,13 @@ 'Logged out': 'Uitgelogd', 'Login': 'Login', 'login': 'login', +'Login disabled by administrator': 'Login disabled by administrator', 'Login to the Administrative Interface': 'Inloggen op de Administratieve Interface', 'logout': 'logout', 'Logout': 'Logout', 'Lost Password': 'Wachtwoord Kwijt', 'Lost password?': 'Wachtwoord kwijt?', +'Lost your password?': 'Lost your password?', 'Main Menu': 'Hoofdmenu', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', @@ -269,7 +302,10 @@ 'pack all': 'pack all', 'pack compiled': 'pack compiled', 'Password': 'Wachtwoord', +'Password changed': 'Password changed', "Password fields don't match": 'Wachtwoordvelden komen niet overeen', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Peeking at file': 'Naar bestand aan het gluren', 'Permission': 'Permission', 'Permissions': 'Permissions', @@ -280,6 +316,7 @@ 'previous %s rows': 'previous %s rows', 'previous 100 rows': 'vorige 100 rijen', 'Profile': 'Profiel', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'pygraphviz library not found', 'Python': 'Python', 'Query': 'Query', @@ -291,13 +328,22 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Recepten', 'Record': 'Record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'record bestaat niet', 'Record ID': 'Record ID', 'Record id': 'Record id', +'Record Updated': 'Record Updated', 'register': 'registreer', 'Register': 'Registreer', 'Registration identifier': 'Registratie identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Registratie sleutel', +'Registration needs verification': 'Registration needs verification', 'Registration successful': 'Registratie succesvol', 'Remember me (for 30 days)': 'Onthoudt mij (voor 30 dagen)', 'remove compiled': 'verwijder gecompileerde', @@ -320,6 +366,7 @@ 'session expired': 'sessie verlopen', 'shell': 'shell', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'site': 'site', 'Size of cache:': 'Grootte van cache:', 'some files could not be removed': 'sommige bestanden konden niet worden verwijderd', @@ -352,6 +399,8 @@ 'There are no views': 'Er zijn geen views', 'these files are served without processing, your images go here': 'Deze bestanden worden geserveerd zonder verdere verwerking, je afbeeldingen horen hier', 'This App': 'Deze App', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'This is a copy of the scaffolding application': 'Dit is een kopie van de steiger-applicatie', 'This is the %(filename)s template': 'Dit is de %(filename)s template', 'Ticket': 'Ticket', @@ -365,12 +414,14 @@ 'try': 'probeer', 'try something like': 'probeer zoiets als', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Unable to check for upgrades': 'Niet mogelijk om te controleren voor upgrades', 'unable to create application "%s"': 'niet mogelijk om applicatie "%s" te maken', 'unable to delete file "%(filename)s"': 'niet mogelijk om bestand "%(filename)s" te verwijderen', 'Unable to download': 'Niet mogelijk om te downloaden', 'Unable to download app': 'Niet mogelijk om app te downloaden', 'unable to parse csv file': 'niet mogelijk om csv-bestand te parsen', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'niet mogelijk om "%s" te deïnstalleren', 'uncheck all': 'vink alles uit', 'uninstall': ' deïnstalleer', @@ -382,13 +433,21 @@ 'upload file:': 'upload bestand', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Gebruik (...)&(...) voor AND, (...)|(...) voor OR, en ~(...) voor NOT om meer complexe queries te maken.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': 'Gebruiker %(id)s Logged-in', 'User %(id)s Logged-out': 'Gebruiker %(id)s Logged-out', 'User %(id)s Password changed': 'Wachtwoord van gebruiker %(id)s is veranderd', 'User %(id)s Password reset': 'Wachtwoord van gebruiker %(id)s is gereset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', 'User %(id)s Profile updated': 'Profiel van Gebruiker %(id)s geupdate', 'User %(id)s Registered': 'Gebruiker %(id)s Geregistreerd', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'User ID', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'value already in database or empty': 'waarde al in database of leeg', 'Verify Password': 'Verifieer Wachtwoord', @@ -401,13 +460,20 @@ 'web2py is up to date': 'web2py is up to date', 'web2py Recent Tweets': 'web2py Recente Tweets', 'Welcome': 'Welkom', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': 'Welkom %s', 'Welcome to web2py': 'Welkom bij web2py', 'Welcome to web2py!': 'Welkom bij web2py!', 'Which called the function %s located in the file %s': 'Die functie %s aanriep en zich bevindt in het bestand %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Working...', 'YES': 'JA', 'You are successfully running web2py': 'Je draait web2py succesvol', 'You can modify this application and adapt it to your needs': 'Je kan deze applicatie aanpassen naar je eigen behoeften', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'Je bezocht de url %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/pl.py b/applications/welcome/languages/pl.py old mode 100755 new mode 100644 index f77b6adf..824e1b42 --- a/applications/welcome/languages/pl.py +++ b/applications/welcome/languages/pl.py @@ -13,7 +13,19 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'About': 'About', 'Access Control': 'Access Control', 'admin': 'admin', @@ -21,9 +33,12 @@ 'Administrative interface': 'Kliknij aby przejść do panelu administracyjnego', 'Ajax Recipes': 'Ajax Recipes', 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'API Example': 'API Example', 'appadmin is disabled because insecure channel': 'administracja aplikacji wyłączona z powodu braku bezpiecznego połączenia', +'Apply changes': 'Apply changes', 'Are you sure you want to delete this object?': 'Are you sure you want to delete this object?', 'Authentication': 'Uwierzytelnienie', +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'Dostępne bazy danych i tabele', 'Buy this book': 'Buy this book', "Buy web2py's book": "Buy web2py's book", @@ -35,11 +50,13 @@ 'Cannot be empty': 'Nie może być puste', 'Change Password': 'Zmień hasło', 'change password': 'change password', +'Change password': 'Change password', 'Check to delete': 'Zaznacz aby usunąć', 'Check to delete:': 'Zaznacz aby usunąć:', 'Clear CACHE?': 'Clear CACHE?', 'Clear DISK': 'Clear DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Client IP': 'IP klienta', 'Community': 'Community', 'Components and Plugins': 'Components and Plugins', @@ -77,6 +94,9 @@ 'Edit Profile': 'Edytuj profil', 'Edit This App': 'Edytuj tę aplikację', 'Email and SMS': 'Email and SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Errors': 'Errors', 'export as csv file': 'eksportuj jako plik csv', 'FAQ': 'FAQ', @@ -85,7 +105,11 @@ 'Free Applications': 'Free Applications', 'Function disabled': 'Funkcja wyłączona', 'Graph Model': 'Graph Model', +'Grid Example': 'Grid Example', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'ID grupy', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Groups': 'Groups', 'Hello World': 'Witaj Świecie', 'Helping web2py': 'Helping web2py', @@ -94,26 +118,40 @@ 'How did you get here?': 'How did you get here?', 'import': 'import', 'Import/Export': 'Importuj/eksportuj', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Index': 'Indeks', 'insert new': 'wstaw nowy rekord tabeli', 'insert new %s': 'wstaw nowy rekord do tabeli %s', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': 'Stan wewnętrzny', 'Introduction': 'Introduction', 'Invalid email': 'Błędny adres email', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', +'Invalid password': 'Invalid password', 'Invalid Query': 'Błędne zapytanie', 'invalid request': 'Błędne żądanie', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'Last name': 'Nazwisko', 'Layout': 'Układ', 'Layout Plugins': 'Layout Plugins', 'Layouts': 'Layouts', 'Live Chat': 'Live Chat', 'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'login': 'login', 'Login': 'Zaloguj', +'Login disabled by administrator': 'Login disabled by administrator', 'logout': 'logout', 'Logout': 'Wyloguj', 'Lost Password': 'Przypomnij hasło', +'Lost your password?': 'Lost your password?', 'Main Menu': 'Menu główne', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', @@ -122,12 +160,15 @@ 'Menu Model': 'Model menu', 'My Sites': 'My Sites', 'Name': 'Nazwa', +'New password': 'New password', 'New Record': 'Nowy rekord', 'new record inserted': 'nowy rekord został wstawiony', 'next %s rows': 'next %s rows', 'next 100 rows': 'następne 100 wierszy', 'No databases in this application': 'Brak baz danych w tej aplikacji', 'Number of entries: **%s**': 'Number of entries: **%s**', +'Object or table name': 'Object or table name', +'Old password': 'Old password', 'Online book': 'Online book', 'Online examples': 'Kliknij aby przejść do interaktywnych przykładów', 'or import from csv file': 'lub zaimportuj z pliku csv', @@ -136,14 +177,20 @@ 'Other Recipes': 'Other Recipes', 'Overview': 'Overview', 'Password': 'Hasło', +'Password changed': 'Password changed', "Password fields don't match": 'Pola hasła nie są zgodne ze sobą', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Permission': 'Permission', 'Permissions': 'Permissions', +'please input your password again': 'please input your password again', 'Plugins': 'Plugins', 'Powered by': 'Zasilane przez', 'Preface': 'Preface', 'previous %s rows': 'previous %s rows', 'previous 100 rows': 'poprzednie 100 wierszy', +'Profile': 'Profile', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'pygraphviz library not found', 'Python': 'Python', 'Query:': 'Zapytanie:', @@ -154,12 +201,26 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Recipes', 'Record': 'rekord', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'rekord nie istnieje', 'Record ID': 'ID rekordu', 'Record id': 'id rekordu', +'Record Updated': 'Record Updated', 'Register': 'Zarejestruj', 'register': 'register', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Klucz rejestracji', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', +'Remember me (for 30 days)': 'Remember me (for 30 days)', +'Request reset password': 'Request reset password', +'Reset Password key': 'Reset Password key', 'Role': 'Rola', 'Roles': 'Roles', 'Rows in Table': 'Wiersze w tabeli', @@ -168,6 +229,7 @@ 'Semantic': 'Semantic', 'Services': 'Services', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'Size of cache:': 'Size of cache:', 'state': 'stan', 'Statistics': 'Statistics', @@ -183,26 +245,50 @@ 'The output of the file is a dictionary that was rendered by the view %s': 'The output of the file is a dictionary that was rendered by the view %s', 'The Views': 'The Views', 'This App': 'This App', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', 'Timestamp': 'Znacznik czasu', 'Traceback': 'Traceback', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'unable to parse csv file': 'nie można sparsować pliku csv', +'Unable to send email': 'Unable to send email', 'Update:': 'Uaktualnij:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Użyj (...)&(...) jako AND, (...)|(...) jako OR oraz ~(...) jako NOT do tworzenia bardziej skomplikowanych zapytań.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', 'User %(id)s Registered': 'Użytkownik %(id)s został zarejestrowany', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'ID użytkownika', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Verify Password': 'Potwierdź hasło', 'Videos': 'Videos', 'View': 'Widok', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': 'Welcome %s', 'Welcome to web2py': 'Witaj w web2py', 'Welcome to web2py!': 'Welcome to web2py!', 'Which called the function %s located in the file %s': 'Which called the function %s located in the file %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Working...', 'You are successfully running web2py': 'You are successfully running web2py', 'You can modify this application and adapt it to your needs': 'You can modify this application and adapt it to your needs', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'You visited the url %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/plural-cs.py b/applications/welcome/languages/plural-cs.py old mode 100755 new mode 100644 diff --git a/applications/welcome/languages/plural-en.py b/applications/welcome/languages/plural-en.py old mode 100755 new mode 100644 diff --git a/applications/welcome/languages/plural-es.py b/applications/welcome/languages/plural-es.py old mode 100755 new mode 100644 diff --git a/applications/welcome/languages/plural-ru.py b/applications/welcome/languages/plural-ru.py old mode 100755 new mode 100644 diff --git a/applications/welcome/languages/plural-uk.py b/applications/welcome/languages/plural-uk.py old mode 100755 new mode 100644 diff --git a/applications/welcome/languages/pt-br.py b/applications/welcome/languages/pt-br.py old mode 100755 new mode 100644 index b94d3178..3d850653 --- a/applications/welcome/languages/pt-br.py +++ b/applications/welcome/languages/pt-br.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- { +'!=': '!=', '!langcode!': 'pt-br', '!langname!': 'Português (do Brasil)', '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" é uma expressão opcional como "campo1=\'novovalor\'". Você não pode atualizar ou apagar os resultados de um JOIN', +'%(nrows)s records found': '%(nrows)s registros encontrados', '%s %%{row} deleted': '%s linha apagadas', '%s %%{row} updated': '%s linha atualizadas', '%s selected': '%s selecionado', @@ -12,20 +14,45 @@ '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'+ And': '+ E', +'+ Or': '+ Ou', +'<': '<', +'<=': '<=', +'=': '=', +'>': '>', +'>=': '>=', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'Ocorreu um erro, por favor [[reload %s]] a página', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Número de entradas: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'Uma nova senha foi enviada por email para você', 'About': 'Sobre', 'Access Control': 'Controle de Acesso', +'Add Record': 'Novo Registro', +'Add record to database': 'Adicionar registro ao banco de dados', +'Add this to the search as an AND term': 'Adicionar à pesquisa como um termo E', +'Add this to the search as an OR term': 'Adicionar à pesquisa como um termo OU', 'admin': 'admin', 'Administrative Interface': 'Interface Administrativa', 'Administrative interface': 'Interface administrativa', 'Ajax Recipes': 'Receitas de Ajax', 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', -'API Example': 'API Example', +'API Example': 'Exmplo de API', 'appadmin is disabled because insecure channel': 'Administração desativada porque o canal não é seguro', +'Apply changes': 'Aplicar Mudanças', 'Are you sure you want to delete this object?': 'Você tem certeza que quer apagar este objeto?', +'Authentication code': 'Código de autenticação', 'Available Databases and Tables': 'Bancos de dados e tabelas disponíveis', +'Back': 'Voltar', 'Buy this book': 'Compre o livro', "Buy web2py's book": 'Compre o livro do web2py', 'cache': 'cache', @@ -35,16 +62,27 @@ 'Cache Keys': 'Chaves de cache', 'Cannot be empty': 'Não pode estar vazio', 'change password': 'mudar senha', +'Change Password': 'Trocar Senhar', +'Change password': 'Trocar senha', 'Check to delete': 'Marque para apagar', +'Clear': 'Limpar', 'Clear CACHE?': 'Limpar CACHE?', 'Clear DISK': 'Limpar DISCO', 'Clear RAM': 'Limpar memória RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Client IP': 'IP do cliente', +'Close': 'Fechar', +'Comma-separated export including columns not shown; fields from other tables are exported as raw values for faster export': 'Comma-separated export including columns not shown; fields from other tables are exported as raw values for faster export', +'Comma-separated export of visible columns. Fields from other tables are exported as they appear on-screen but this may be slow for many rows': 'Comma-separated export of visible columns. Fields from other tables are exported as they appear on-screen but this may be slow for many rows', 'Community': 'Comunidade', 'Components and Plugins': 'Componentes e Plugins', 'Config.ini': 'Config.ini', +'Confirm Password': 'Confirme a Senha', +'contains': 'contém', 'Controller': 'Controlador', 'Copyright': 'Copyright', +'CSV': 'CSV', +'CSV (hidden cols)': 'CSV (col. ocultas)', 'Current request': 'Requisição atual', 'Current response': 'Resposta atual', 'Current session': 'Sessão atual', @@ -52,9 +90,10 @@ 'data uploaded': 'dados enviados', 'Database': 'banco de dados', 'Database %s select': 'Selecionar banco de dados %s', -'Database Administration (appadmin)': 'Database Administration (appadmin)', +'Database Administration (appadmin)': 'Administração de Banco de Dados (appadmin)', 'db': 'bd', 'DB Model': 'Modelo BD', +'Delete': 'Excluir', 'Delete:': 'Apagar:', 'Demo': 'Demo', 'Deployment Recipes': 'Receitas de deploy', @@ -75,33 +114,57 @@ 'edit profile': 'editar perfil', 'Edit This App': 'Editar esta aplicação', 'Email and SMS': 'Email e SMS', +'Email sent': 'Email enviado', +'Email verification': 'Verificação de email', +'Email verified': 'Email verificado', 'Enter an integer between %(min)g and %(max)g': 'Informe um valor inteiro entre %(min)g e %(max)g', 'Errors': 'Erros', 'export as csv file': 'exportar como um arquivo csv', +'Export:': 'Exportar:', 'FAQ': 'Perguntas frequentes', 'First name': 'Nome', 'Forms and Validators': 'Formulários e Validadores', 'Free Applications': 'Aplicações gratuitas', -'Graph Model': 'Graph Model', +'Function disabled': 'Função desabilitada', +'Graph Model': 'Modelo em Grafo', 'Grid Example': 'Exemplo de Grade', +'Group %(group_id)s created': 'Grupo %(group_id)s criado', +'Group %(group_id)s deleted': 'Grupo %(group_id)s excluído', 'Group ID': 'ID do Grupo', +'Group uniquely assigned to user %(id)s': 'Gurpo unicamente atribuído ao usuário %(id)s', 'Groups': 'Grupos', 'Hello World': 'Olá Mundo', -'Helping web2py': 'Helping web2py', +'Helping web2py': 'Ajudando web2py', 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', 'Home': 'Principal', 'How did you get here?': 'Como você chegou aqui?', +'HTML': 'HTML', +'HTML export of visible columns': 'HTML exportar colunas visíveis', +'Id': 'Id', 'import': 'importar', 'Import/Export': 'Importar/Exportar', +'in': 'em', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Index': 'Início', 'insert new': 'inserir novo', 'insert new %s': 'inserir novo %s', +'Insufficient privileges': 'Privilégios insuficientes', 'Internal State': 'Estado Interno', 'Introduction': 'Introdução', 'Invalid email': 'Email inválido', +'Invalid key': 'Chave inválida', +'Invalid login': 'Login Inválido', +'Invalid password': 'Senha inválida', 'Invalid Query': 'Consulta Inválida', 'invalid request': 'requisição inválida', +'Invalid reset password': 'Recriação de senha inválida', +'Invalid user': 'Usuário inválido', +'Invalid username': 'Nome de usuário inválido', +'Invitation to join %(site)s': 'Convite para entrar %(site)s', +'JSON': 'JSON', +'JSON export of visible columns': 'JSON exportar colunas visíveis', 'Key': 'Chave', +'Key verified': 'Chave verificada', 'Last name': 'Sobrenome', 'Layout': 'Layout', 'Layout Plugins': 'Plugins de Layout', @@ -109,9 +172,13 @@ 'Live chat': 'Chat ao vivo', 'Live Chat': 'Chat ao vivo', 'Log In': 'Entrar', +'Logged in': 'Conectado', +'Logged out': 'Desconectado', 'login': 'Entrar', 'Login': 'Entrar', +'Login disabled by administrator': 'Login desabilitado pelo administrador', 'logout': 'Sair', +'Logout': 'Sair', 'Lost Password': 'Esqueceu sua senha?', 'lost password?': 'esqueceu sua senha?', 'Lost your password?': 'Esqueceu sua senha?', @@ -123,14 +190,19 @@ 'Menu Model': 'Modelo de Menu', 'My Sites': 'Meus sites', 'Name': 'Nome', +'New password': 'Nova senha', 'New Record': 'Novo Registro', 'new record inserted': 'novo registro inserido', +'New Search': 'Nova pesquisa', 'next %s rows': 'próximas %s ´linhas', 'next 100 rows': 'próximas 100 linhas', 'No databases in this application': 'Não há bancos de dados nesta aplicação', +'No records found': 'Não foram encontrados registros', +'not in': 'não está em', 'Number of entries: **%s**': 'Número de entradas: **%s**', 'Object or table name': 'Nome do objeto do da tabela', -'Online book': 'Online book', +'Old password': 'Senha antiga', +'Online book': 'Livro online', 'Online examples': 'Exemplos online', 'or import from csv file': 'ou importar de um arquivo csv', 'Origin': 'Origem', @@ -138,47 +210,71 @@ 'Other Recipes': 'Outras Receitas', 'Overview': 'Visão Geral', 'Password': 'Senha', -'Permission': 'Permission', -'Permissions': 'Permissions', +'Password changed': 'Senha trocada', +"Password fields don't match": 'Senhas não conferem', +'Password reset': 'Recriar senha', +'Password retrieve': 'Recuperar senha', +'Permission': 'Permissão', +'Permissions': 'Permissões', +'please input your password again': 'por favor digite a senha novamente', 'Plugins': 'Plugins', 'Powered by': 'Desenvolvido com', 'Preface': 'Prefácio', 'previous %s rows': '%s linhas anteriores', 'previous 100 rows': '100 linhas anteriores', +'Profile': 'Perfil', +'Profile updated': 'Perfil atualizado', 'pygraphviz library not found': 'biblioteca pygraphviz não encontrada', 'Python': 'Python', 'Query:': 'Consulta:', 'Quick Examples': 'Exemplos rápidos', 'RAM': 'RAM', -'RAM Cache Keys': 'RAM Cache Keys', +'RAM Cache Keys': 'Chaves de Cache RAM ', 'Ram Cleared': 'Ram Limpa', 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Receitas', 'Record': 'Registro', +'Record %(id)s created': 'Registro %(id)s criado', +'Record %(id)s deleted': 'Registro %(id)s excluído', +'Record %(id)s read': 'Registro %(id)s lido', +'Record %(id)s updated': 'Registro %(id)s atualizado', +'Record Created': 'Registro Criado', +'Record Deleted': 'Registro Excluído', 'record does not exist': 'registro não existe', 'Record ID': 'ID do Registro', 'Record id': 'id do registro', -'register': 'Registre-se', -'Register': 'Registre-se', -'Registration identifier': 'Idenficador de registro', -'Registration key': 'Chave de registro', +'Record Updated': 'Registro Atualizado', +'register': 'Cadastre-se', +'Register': 'Cadastre-se', +'Registration identifier': 'Idenficador de Cadastro', +'Registration is pending approval': 'Aprovação de cadastro pendente', +'Registration key': 'Chave de cadastro', +'Registration needs verification': 'Cadastro necessita verficação', +'Registration successful': 'Cadastro finalizado', 'Remember me (for 30 days)': 'Mantenha-me logado (por 30 dias)', +'Request reset password': 'Requerer recriação de senha', 'Reset Password key': 'Resetar chave de senha', 'Resources': 'Recursos', 'Role': 'Papel', -'Roles': 'Roles', +'Roles': 'Papéis', 'Rows in Table': 'Linhas na tabela', 'Rows selected': 'Linhas selecionadas', 'Save model as...': 'Salvar modelo como...', +'Search': 'Pesquisar', 'Semantic': 'Semântico', 'Services': 'Serviço', 'Sign Up': 'Cadastrar', 'Sign up': 'Cadastrar', 'Size of cache:': 'Tamanho do cache:', +'Spreadsheet-optimised export of tab-separated content including hidden columns. May be slow': 'Spreadsheet-optimised export of tab-separated content including hidden columns. May be slow', +'Spreadsheet-optimised export of tab-separated content, visible columns only. May be slow.': 'Spreadsheet-optimised export of tab-separated content, visible columns only. May be slow.', +'Start building a new search': 'Comerçar um nova pesquisa', +'starts with': 'começa com', 'state': 'estado', 'Statistics': 'Estatísticas', 'Stylesheet': 'Folha de estilo', 'submit': 'enviar', +'Submit': 'Enviar', 'Support': 'Suporte', 'Sure you want to delete this object?': 'Está certo(a) que deseja apagar este objeto?', 'Table': 'Tabela', @@ -188,31 +284,59 @@ 'The output of the file is a dictionary that was rendered by the view %s': 'A saída do arquivo é um dicionário que foi apresentado pela visão %s', 'The Views': 'As views', 'This App': 'Esta aplicação', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', 'This email already has an account': 'Este email já tem uma conta', 'This is a copy of the scaffolding application': 'Isto é uma cópia da aplicação modelo', 'Time in Cache (h:m:s)': 'Tempo em Cache (h:m:s)', 'Timestamp': 'Timestamp', 'Traceback': 'Traceback', +'TSV (Spreadsheets)': 'TSV (Planilhas)', +'TSV (Spreadsheets, hidden cols)': 'TSV (Planilhas, col. ocultas)', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Código de Autenticação de Login em Dois Fatores', 'unable to parse csv file': 'não foi possível analisar arquivo csv', +'Unable to send email': 'Não foi possível enviar email', 'Update:': 'Atualizar:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) para AND, (...)|(...) para OR, e ~(...) para NOT para construir consultas mais complexas.', -'User': 'User', +'User': 'Usuário', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'Usuário %(id)s entrou', +'User %(id)s Logged-out': 'Usuário %(id)s saiu', +'User %(id)s Password changed': 'Usuário %(id)s trocou a senha', +'User %(id)s Password reset': 'Usuário %(id)s recirar a senha', +'User %(id)s Password retrieved': 'Usuário %(id)s Recuperou a senha', +'User %(id)s Profile updated': 'Usuário %(id)s Atualizou perfil', +'User %(id)s Registered': 'Usuário %(id)s Cadastrou-se', +'User %(id)s Username retrieved': 'Usuário %(id)s Recuperou nome de usuário', +'User %(id)s Verification email sent': 'Usuário %(id)s Email de verificação enviado', +'User %(id)s verified registration key': 'Usuário %(id)s chave de cadastro verificada', 'User ID': 'ID do Usuário', 'User Voice': 'Opinião dos usuários', -'Users': 'Users', +'Username': 'Nome de Usuário', +'Username already taken': 'Nome de usuário já existe', +'Username retrieve': 'Recuperar nome de usuário', +'Users': 'Usuários', +'Verify Password': 'Verificar Senha', 'Videos': 'Vídeos', 'View': 'Visualização', 'Web2py': 'Web2py', 'Welcome': 'Bem-vindo', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': 'Bem-vindo %s', 'Welcome to web2py': 'Bem-vindo ao web2py', 'Welcome to web2py!': 'Bem-vindo ao web2py!', 'Which called the function %s located in the file %s': 'Que chamou a função %s localizada no arquivo %s', -'Wiki Example': 'Wiki Example', +'Wiki Example': 'Exmplo de Wiki', 'Working...': 'Trabalhando...', +'XML': 'XML', +'XML export of columns shown': 'XML exportar colunas visíveis', 'You are successfully running web2py': 'Você está executando o web2py com sucesso', 'You are successfully running web2py.': 'Você está executando o web2py com sucesso.', 'You can modify this application and adapt it to your needs': 'Você pode modificar esta aplicação e adaptá-la às suas necessidades', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'Você acessou a url %s', +'Your password is: %(password)s': 'Sua senha é: %(password)s', +'Your temporary login code is {0}': 'Seu código temporário de login é {0}', +'Your username is: %(username)s': 'Seu nome de usuário é: %(username)s', +'Your username was emailed to you': 'Seu nome de usuário foi enviado por email para você', } diff --git a/applications/welcome/languages/pt.py b/applications/welcome/languages/pt.py old mode 100755 new mode 100644 index c71eb7b1..c9972f31 --- a/applications/welcome/languages/pt.py +++ b/applications/welcome/languages/pt.py @@ -13,9 +13,19 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', '@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'About': 'Sobre', 'Access Control': 'Controle de acesso', 'admin': 'administrador', @@ -25,7 +35,9 @@ 'An error occured, please [[reload %s]] the page': 'Ocorreu um erro, por favor [[recarregue %s]] a página', 'API Example': 'API Example', 'appadmin is disabled because insecure channel': 'appadmin está desactivada pois o canal é inseguro', +'Apply changes': 'Apply changes', 'Are you sure you want to delete this object?': 'Tem a certeza que quer deletar este objeto?', +'Authentication code': 'Authentication code', 'Author Reference Auth User': 'Author Reference Auth User', 'Author Reference Auth User.username': 'Author Reference Auth User.username', 'Available Databases and Tables': 'bases de dados e tabelas disponíveis', @@ -40,10 +52,14 @@ 'Category Create': 'Criar Categoria', 'Category Select': 'Selecionar Categoria', 'change password': 'alterar palavra-chave', +'Change Password': 'Change Password', +'Change password': 'Change password', 'Check to delete': 'seleccione para eliminar', 'Clear CACHE?': 'apagar CACHE?', 'Clear DISK': 'apagar DISK', 'Clear RAM': 'apagar RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', +'Client IP': 'Client IP', 'Comment Create': 'Criar Comentário', 'Comment Select': 'Selecionar Comentário', 'Community': 'Comunidade', @@ -70,6 +86,7 @@ 'Delete:': 'Eliminar:', 'Demo': 'Demo', 'Deployment Recipes': 'Deployment Recipes', +'Description': 'Description', 'design': 'design', 'Design': 'Design', 'DISK': 'DISK', @@ -90,6 +107,9 @@ 'Edit This App': 'Edite esta aplicação', 'Email': 'Email', 'Email and SMS': 'Email e SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Enter an integer between %(min)g and %(max)g': 'Enter an integer between %(min)g and %(max)g', 'Errors': 'Erros', 'export as csv file': 'exportar como ficheiro csv', @@ -99,8 +119,13 @@ 'For %s #%s': 'For %s #%s', 'Forms and Validators': 'Forms and Validators', 'Free Applications': 'Free Applications', +'Function disabled': 'Function disabled', 'Graph Model': 'Graph Model', 'Grid Example': 'Grid Example', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', +'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Groups': 'Groups', 'Hello World': 'Olá Mundo', 'Helping web2py': 'Helping web2py', @@ -109,14 +134,25 @@ 'How did you get here?': 'How did you get here?', 'import': 'import', 'Import/Export': 'Importar/Exportar', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Index': 'Índice', 'insert new': 'inserir novo', 'insert new %s': 'inserir novo %s', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': 'Estado interno', 'Introduction': 'Introdução', +'Invalid email': 'Invalid email', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', +'Invalid password': 'Invalid password', 'Invalid Query': 'Consulta Inválida', 'invalid request': 'Pedido Inválido', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'Last name': 'Last name', 'Last Name': 'Last Name', 'Layout': 'Esboço', @@ -124,10 +160,15 @@ 'Layouts': 'Layouts', 'Live Chat': 'Live Chat', 'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'login': 'login', 'Login': 'Login', +'Login disabled by administrator': 'Login disabled by administrator', 'logout': 'logout', +'Logout': 'Logout', 'Lost Password': 'Perdeu a Senha', +'Lost your password?': 'Lost your password?', 'Main Menu': 'Menu Principal', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', @@ -138,6 +179,7 @@ 'Modified On': 'Modified On', 'My Sites': 'My Sites', 'Name': 'Nome', +'New password': 'New password', 'New Record': 'Novo Registo', 'new record inserted': 'novo registo inserido', 'next %s rows': 'next %s rows', @@ -145,15 +187,23 @@ 'No Data': 'No Data', 'No databases in this application': 'Não há bases de dados nesta aplicação', 'Number of entries: **%s**': 'Number of entries: **%s**', +'Object or table name': 'Object or table name', +'Old password': 'Old password', 'Online book': 'Livro Online', 'Online examples': 'Exemplos online', 'or import from csv file': 'ou importe a partir de ficheiro csv', +'Origin': 'Origin', 'Other Plugins': 'Other Plugins', 'Other Recipes': 'Other Recipes', 'Overview': 'Overview', 'Password': 'Password', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Permission': 'Permission', 'Permissions': 'Permissions', +'please input your password again': 'please input your password again', 'Plugins': 'Plugins', 'Post Create': 'Post Create', 'Post Select': 'Post Select', @@ -161,6 +211,8 @@ 'Preface': 'Preface', 'previous %s rows': 'previous %s rows', 'previous 100 rows': '100 linhas anteriores', +'Profile': 'Profile', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'pygraphviz library not found', 'Python': 'Python', 'Query:': 'Interrogação:', @@ -171,14 +223,26 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Receitas', 'Record': 'registo', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'registo inexistente', 'Record id': 'id de registo', +'Record ID': 'Record ID', +'Record Updated': 'Record Updated', 'Register': 'Registar', 'register': 'registar', 'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Registration key', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Remember me (for 30 days)': 'Lembrar de mim (por 30 dias)', 'Replyto Reference Post': 'Replyto Reference Post', +'Request reset password': 'Request reset password', 'Reset Password key': 'Reset Password key', 'Role': 'Role', 'Roles': 'Roles', @@ -212,18 +276,39 @@ 'The output of the file is a dictionary that was rendered by the view %s': 'The output of the file is a dictionary that was rendered by the view %s', 'The Views': 'The Views', 'This App': 'This App', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', +'Timestamp': 'Timestamp', 'Title': 'Title', 'Traceback': 'Traceback', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'unable to parse csv file': 'não foi possível carregar ficheiro csv', +'Unable to send email': 'Unable to send email', 'Update:': 'Actualização:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Utilize (...)&(...) para AND, (...)|(...) para OR, e ~(...) para NOT para construir interrogações mais complexas.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', +'User %(id)s Logged-in': 'User %(id)s Logged-in', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', +'User %(id)s Registered': 'User %(id)s Registered', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', +'User ID': 'User ID', 'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', +'Verify Password': 'Verify Password', 'Videos': 'Videos', 'View': 'Vista', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': 'Bem-vindo(a) %s', 'Welcome to Gluonization': 'Bem vindo ao Web2py', 'Welcome to web2py': 'Bem-vindo(a) ao web2py', @@ -234,5 +319,10 @@ 'Working...': 'Working...', 'You are successfully running web2py': 'You are successfully running web2py', 'You can modify this application and adapt it to your needs': 'You can modify this application and adapt it to your needs', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'You visited the url %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/ro.py b/applications/welcome/languages/ro.py old mode 100755 new mode 100644 index 059a19be..ba4010e0 --- a/applications/welcome/languages/ro.py +++ b/applications/welcome/languages/ro.py @@ -25,7 +25,19 @@ '>': '>', '>=': '>=', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': 'O nouă versiune de web2py este disponibilă', 'A new version of web2py is available: %s': 'O nouă versiune de web2py este disponibilă: %s', 'About': 'Despre', @@ -48,11 +60,13 @@ 'And': 'Și', 'and rename it (required):': 'și renumiți (obligatoriu):', 'and rename it:': ' și renumiți:', +'API Example': 'API Example', 'appadmin': 'appadmin', 'appadmin is disabled because insecure channel': 'appadmin dezactivat deoarece conexiunea nu e sigură', 'application "%s" uninstalled': 'aplicația "%s" a fost dezinstalată', 'application compiled': 'aplicația a fost compilată', 'application is compiled and cannot be designed': 'aplicația este compilată și nu poate fi editată', +'Apply changes': 'Apply changes', 'Are you sure you want to delete file "%s"?': 'Sigur ștergeți fișierul "%s"?', 'Are you sure you want to delete this object?': 'Sigur ștergeți acest obiect?', 'Are you sure you want to uninstall application "%s"': 'Sigur dezinstalați aplicația "%s"', @@ -61,6 +75,7 @@ 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATENȚIE: Nu puteți efectua mai multe teste o dată deoarece lansarea în execuție a mai multor subpocese nu este sigură.', 'ATTENTION: you cannot edit the running application!': 'ATENȚIE: nu puteți edita o aplicație în curs de execuție!', 'Authentication': 'Autentificare', +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'Baze de date și tabele disponibile', 'Back': 'Înapoi', 'Buy this book': 'Cumpără această carte', @@ -85,6 +100,7 @@ 'Clear CACHE?': 'Clear CACHE?', 'Clear DISK': 'Clear DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'click to check for upgrades': 'Clic pentru a verifica dacă există upgrade-uri', 'Client IP': 'IP client', 'Community': 'Comunitate', @@ -149,6 +165,9 @@ 'Editing file': 'Editare fișier', 'Editing file "%s"': 'Editare fișier "%s"', 'Email and SMS': 'E-mail și SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'enter a number between %(min)g and %(max)g': 'introduceți un număr între %(min)g și %(max)g', 'enter an integer between %(min)g and %(max)g': 'introduceți un întreg între %(min)g și %(max)g', 'Error logs for "%(app)s"': 'Log erori pentru "%(app)s"', @@ -174,9 +193,12 @@ 'Forbidden': 'Interzis', 'Forms and Validators': 'Formulare și validatori', 'Free Applications': 'Aplicații gratuite', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': 'Funcțiile fără doctests vor genera teste [trecute].', 'Graph Model': 'Graph Model', +'Grid Example': 'Grid Example', 'Group %(group_id)s created': 'Grup %(group_id)s creat', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'ID grup', 'Group uniquely assigned to user %(id)s': 'Grup asociat în mod unic utilizatorului %(id)s', 'Groups': 'Grupuri', @@ -190,21 +212,30 @@ 'import': 'import', 'Import/Export': 'Import/Export', 'includes': 'include', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Index': 'Index', 'insert new': 'adaugă nou', 'insert new %s': 'adaugă nou %s', 'Installed applications': 'Aplicații instalate', +'Insufficient privileges': 'Insufficient privileges', 'internal error': 'eroare internă', 'Internal State': 'Stare internă', 'Introduction': 'Introducere', 'Invalid action': 'Acțiune invalidă', 'Invalid email': 'E-mail invalid', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'invalid password': 'parolă invalidă', 'Invalid password': 'Parolă invalidă', 'Invalid Query': 'Interogare invalidă', 'invalid request': 'cerere invalidă', +'Invalid reset password': 'Invalid reset password', 'invalid ticket': 'tichet invalid', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'language file "%(filename)s" created/updated': 'fișier de limbă "%(filename)s" creat/actualizat', 'Language files (static strings) updated': 'Fișierele de limbă (șirurile statice de caractere) actualizate', 'languages': 'limbi', @@ -223,11 +254,13 @@ 'Logged out': 'Delogat', 'Login': 'Autentificare', 'login': 'autentificare', +'Login disabled by administrator': 'Login disabled by administrator', 'Login to the Administrative Interface': 'Logare interfață de administrare', 'logout': 'ieșire', 'Logout': 'Ieșire', 'Lost Password': 'Parolă pierdută', 'Lost password?': 'Parolă pierdută?', +'Lost your password?': 'Lost your password?', 'Main Menu': 'Meniu principal', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', @@ -266,7 +299,10 @@ 'pack all': 'împachetează toate', 'pack compiled': 'pachet compilat', 'Password': 'Parola', +'Password changed': 'Password changed', "Password fields don't match": 'Câmpurile de parolă nu se potrivesc', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Peeking at file': 'Vizualizare fișier', 'Permission': 'Permission', 'Permissions': 'Permissions', @@ -277,6 +313,7 @@ 'previous %s rows': 'previous %s rows', 'previous 100 rows': '100 de linii anterioare', 'Profile': 'Profil', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'pygraphviz library not found', 'Python': 'Python', 'Query': 'Interogare', @@ -288,13 +325,22 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Rețete', 'Record': 'înregistrare', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'înregistrare inexistentă', 'Record ID': 'ID înregistrare', 'Record id': 'id înregistrare', +'Record Updated': 'Record Updated', 'register': 'înregistrare', 'Register': 'Înregistrare', 'Registration identifier': 'Identificator de autentificare', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Cheie înregistrare', +'Registration needs verification': 'Registration needs verification', 'Registration successful': 'Autentificare reușită', 'Remember me (for 30 days)': 'Ține-mă minte (timp de 30 de zile)', 'remove compiled': 'șterge compilate', @@ -317,6 +363,7 @@ 'session expired': 'sesiune expirată', 'shell': 'line de commandă', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'site': 'site', 'Size of cache:': 'Size of cache:', 'some files could not be removed': 'anumite fișiere n-au putut fi șterse', @@ -349,6 +396,8 @@ 'There are no views': 'Nu există vederi', 'these files are served without processing, your images go here': 'aceste fișiere sunt servite fără procesare, imaginea se plasează acolo', 'This App': 'Această aplicație', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'This is a copy of the scaffolding application': 'Aceasta este o copie a aplicației schelet', 'This is the %(filename)s template': 'Aceasta este șablonul fișierului %(filename)s', 'Ticket': 'Tichet', @@ -362,12 +411,14 @@ 'try': 'încearcă', 'try something like': 'încearcă ceva de genul', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Unable to check for upgrades': 'Imposibil de verificat dacă există actualizări', 'unable to create application "%s"': 'imposibil de creat aplicația "%s"', 'unable to delete file "%(filename)s"': 'imposibil de șters fișierul "%(filename)s"', 'Unable to download': 'Imposibil de descărcat', 'Unable to download app': 'Imposibil de descărcat aplicația', 'unable to parse csv file': 'imposibil de analizat fișierul csv', +'Unable to send email': 'Unable to send email', 'unable to uninstall "%s"': 'imposibil de dezinstalat "%s"', 'uncheck all': 'decoșează tot', 'uninstall': 'dezinstalează', @@ -379,13 +430,21 @@ 'upload file:': 'încarcă fișier:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Folosiți (...)&(...) pentru AND, (...)|(...) pentru OR, și ~(...) pentru NOT, pentru a crea interogări complexe.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': 'Utilizator %(id)s autentificat', 'User %(id)s Logged-out': 'Utilizator %(id)s delogat', 'User %(id)s Password changed': 'Parola utilizatorului %(id)s a fost schimbată', 'User %(id)s Password reset': 'Resetare parola utilizator %(id)s', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', 'User %(id)s Profile updated': 'Profil utilizator %(id)s actualizat', 'User %(id)s Registered': 'Utilizator %(id)s înregistrat', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'ID utilizator', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'value already in database or empty': 'Valoare existentă în baza de date sau vidă', 'Verify Password': 'Verifică parola', @@ -398,13 +457,20 @@ 'web2py is up to date': 'web2py este la zi', 'web2py Recent Tweets': 'Ultimele tweet-uri web2py', 'Welcome': 'Bine ați venit', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': 'Bine ați venit %s', 'Welcome to web2py': 'Bun venit la web2py', 'Welcome to web2py!': 'Bun venit la web2py!', 'Which called the function %s located in the file %s': 'Care a apelat funcția %s prezentă în fișierul %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Working...', 'YES': 'DA', 'You are successfully running web2py': 'Rulați cu succes web2py', 'You can modify this application and adapt it to your needs': 'Puteți modifica și adapta aplicația nevoilor dvs.', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'Ați vizitat adresa %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/ru.py b/applications/welcome/languages/ru.py old mode 100755 new mode 100644 index 0f9e09db..c5ad3202 --- a/applications/welcome/languages/ru.py +++ b/applications/welcome/languages/ru.py @@ -27,7 +27,19 @@ '1 week ago': '1 неделю тому', '1 year ago': '1 год тому', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'About': 'About', 'Access Control': 'Access Control', 'admin': 'admin', @@ -35,8 +47,11 @@ 'Administrative interface': 'административный интерфейс', 'Ajax Recipes': 'Ajax Recipes', 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'API Example': 'API Example', 'appadmin is disabled because insecure channel': 'appadmin is disabled because insecure channel', +'Apply changes': 'Apply changes', 'Are you sure you want to delete this object?': 'Вы уверены, что хотите удалить этот объект?', +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'Базы данных и таблицы', 'Buy this book': 'Buy this book', "Buy web2py's book": "Buy web2py's book", @@ -47,11 +62,13 @@ 'Cache Keys': 'Cache Keys', 'Cannot be empty': 'Пустое значение недопустимо', 'Change Password': 'Смените пароль', +'Change password': 'Change password', 'Check to delete': 'Удалить', 'Check to delete:': 'Удалить:', 'Clear CACHE?': 'Clear CACHE?', 'Clear DISK': 'Clear DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Client IP': 'Client IP', 'Community': 'Community', 'Components and Plugins': 'Components and Plugins', @@ -86,6 +103,9 @@ 'Edit current record': 'Редактировать текущую запись', 'Edit Profile': 'Редактировать профиль', 'Email and SMS': 'Email and SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'enter an integer between %(min)g and %(max)g': 'enter an integer between %(min)g and %(max)g', 'Errors': 'Errors', 'export as csv file': 'экспорт в csv-файл', @@ -93,8 +113,13 @@ 'First name': 'Имя', 'Forms and Validators': 'Forms and Validators', 'Free Applications': 'Free Applications', +'Function disabled': 'Function disabled', 'Graph Model': 'Graph Model', +'Grid Example': 'Grid Example', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'Group ID', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Groups': 'Groups', 'Hello World': 'Заработало!', 'Helping web2py': 'Helping web2py', @@ -103,16 +128,24 @@ 'How did you get here?': 'How did you get here?', 'import': 'import', 'Import/Export': 'Импорт/экспорт', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'insert new': 'добавить', 'insert new %s': 'добавить %s', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': 'Внутренне состояние', 'Introduction': 'Introduction', 'Invalid email': 'Неверный email', +'Invalid key': 'Invalid key', 'Invalid login': 'Неверный логин', 'Invalid password': 'Неверный пароль', 'Invalid Query': 'Неверный запрос', 'invalid request': 'неверный запрос', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'Last name': 'Фамилия', 'Layout': 'Layout', 'Layout Plugins': 'Layout Plugins', @@ -123,10 +156,12 @@ 'Logged out': 'Выход выполнен', 'login': 'вход', 'Login': 'Вход', +'Login disabled by administrator': 'Login disabled by administrator', 'logout': 'выход', 'Logout': 'Выход', 'Lost Password': 'Забыли пароль?', 'Lost password?': 'Lost password?', +'Lost your password?': 'Lost your password?', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', 'Manage Cache': 'Manage Cache', @@ -153,15 +188,21 @@ 'Overview': 'Overview', 'Password': 'Пароль', 'password': 'пароль', +'Password changed': 'Password changed', "Password fields don't match": 'Пароли не совпадают', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Permission': 'Permission', 'Permissions': 'Permissions', +'please input your password again': 'please input your password again', 'Plugins': 'Plugins', 'Powered by': 'Powered by', 'Preface': 'Preface', 'previous %s rows': 'previous %s rows', 'previous 100 rows': 'предыдущие 100 строк', 'profile': 'профиль', +'Profile': 'Profile', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'pygraphviz library not found', 'Python': 'Python', 'Query:': 'Запрос:', @@ -172,13 +213,24 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Recipes', 'Record': 'Record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'запись не найдена', 'Record ID': 'ID записи', 'Record id': 'id записи', +'Record Updated': 'Record Updated', 'Register': 'Зарегистрироваться', 'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Ключ регистрации', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Remember me (for 30 days)': 'Запомнить меня (на 30 дней)', +'Request reset password': 'Request reset password', 'Reset Password key': 'Сбросить ключ пароля', 'Role': 'Роль', 'Roles': 'Roles', @@ -188,6 +240,7 @@ 'Semantic': 'Semantic', 'Services': 'Services', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'Size of cache:': 'Size of cache:', 'state': 'состояние', 'Statistics': 'Statistics', @@ -203,30 +256,50 @@ 'The output of the file is a dictionary that was rendered by the view %s': 'The output of the file is a dictionary that was rendered by the view %s', 'The Views': 'The Views', 'This App': 'This App', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', 'Timestamp': 'Отметка времени', 'Traceback': 'Traceback', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'unable to parse csv file': 'нечитаемый csv-файл', +'Unable to send email': 'Unable to send email', 'Update:': 'Изменить:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Для построение сложных запросов используйте операторы "И": (...)&(...), "ИЛИ": (...)|(...), "НЕ": ~(...).', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': 'Пользователь %(id)s вошёл', 'User %(id)s Logged-out': 'Пользователь %(id)s вышел', 'User %(id)s Password changed': 'Пользователь %(id)s сменил пароль', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', 'User %(id)s Profile updated': 'Пользователь %(id)s обновил профиль', 'User %(id)s Registered': 'Пользователь %(id)s зарегистрировался', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'ID пользователя', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Verify Password': 'Повторите пароль', 'Videos': 'Videos', 'View': 'View', 'Welcome': 'Welcome', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py': 'Добро пожаловать в web2py', 'Welcome to web2py!': 'Welcome to web2py!', 'Which called the function %s located in the file %s': 'Which called the function %s located in the file %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Working...', 'You are successfully running web2py': 'You are successfully running web2py', 'You can modify this application and adapt it to your needs': 'You can modify this application and adapt it to your needs', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'You visited the url %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/sk.py b/applications/welcome/languages/sk.py old mode 100755 new mode 100644 index 01d31069..646c5799 --- a/applications/welcome/languages/sk.py +++ b/applications/welcome/languages/sk.py @@ -13,7 +13,19 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'About': 'O nás', 'Access Control': 'Kontrola prístupu', 'admin': 'admin', @@ -21,8 +33,11 @@ 'Administrative interface': 'pre administrátorské rozhranie kliknite sem', 'Ajax Recipes': 'Ajax recepty', 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'API Example': 'API Example', 'appadmin is disabled because insecure channel': 'appadmin je zakázaný bez zabezpečeného spojenia', +'Apply changes': 'Apply changes', 'Are you sure you want to delete this object?': 'Ste si istý, že chcete vymazať tento objekt?', +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'Dostupné databázy a tabuľky', 'Buy this book': 'Kúpte si túto knihu', "Buy web2py's book": "Kúpte si web2py's knihu", @@ -32,10 +47,14 @@ 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Cache Keys': 'Cache kľúče', 'Cannot be empty': 'Nemôže byť prázdne', +'Change Password': 'Change Password', +'Change password': 'Change password', 'Check to delete': 'Označiť na zmazanie', 'Clear CACHE?': 'Vyčistiť CACHE?', 'Clear DISK': 'Vyčistiť DISK', 'Clear RAM': 'Vyčistiť RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', +'Client IP': 'Client IP', 'Community': 'komunita', 'Components and Plugins': 'Komponenty a pluginy', 'Config.ini': 'Config.ini', @@ -65,18 +84,27 @@ "Don't know what to do?": 'Neviete, čo robiť?', 'done!': 'hotovo!', 'Download': 'Stiahnuť', +'E-mail': 'E-mail', 'Edit': 'Upraviť', 'Edit current record': 'Upraviť aktuálny záznam', 'Edit Profile': 'Upraviť profil', 'Email and SMS': 'Email a SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Errors': 'Chyby', 'export as csv file': 'exportovať do csv súboru', 'FAQ': 'FAQ', 'First name': 'Krstné meno', 'Forms and Validators': 'Formuláre a schvalovače', 'Free Applications': 'Aplikácie zadarmo', +'Function disabled': 'Function disabled', 'Graph Model': 'Model grafu', +'Grid Example': 'Grid Example', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'ID skupiny', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Groups': 'Skupiny', 'Hello World': 'Ahoj svet', 'Helping web2py': 'Pomáhať web2py', @@ -85,16 +113,25 @@ 'How did you get here?': 'Ako ste sa sem dostali?', 'import': 'import', 'Import/Export': 'Import/Export', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Index': 'Index', 'insert new': 'vložiť nový', 'insert new %s': 'vložiť nový%s', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': 'Vnútorný stav', 'Introduction': 'Úvod', 'Invalid email': 'Neplatný email', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', 'Invalid password': 'Nesprávne heslo', 'Invalid Query': 'Neplatná otázka', 'invalid request': 'Neplatná požiadavka', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Kľúč', +'Key verified': 'Key verified', 'Last name': 'Priezvisko', 'Layout': 'Usporiadanie', 'Layout Plugins': 'Pluginy pre usporiadanie', @@ -104,9 +141,13 @@ 'Logged in': 'Prihlásený', 'Logged out': 'Odhlásený', 'login': 'prihlásiť', +'Login': 'Login', +'Login disabled by administrator': 'Login disabled by administrator', 'logout': 'odhlásiť', +'Logout': 'Logout', 'Lost Password': 'Stratené heslo?', 'lost password?': 'stratené heslo?', +'Lost your password?': 'Lost your password?', 'Manage %(action)s': 'Spravovať %(action)s', 'Manage Access Control': 'Spravovať kontrolu prístupu', 'Manage Cache': 'Spravovať cache', @@ -121,6 +162,7 @@ 'next 100 rows': 'ďalších 100 riadkov', 'No databases in this application': 'V tejto aplikácii nie sú databázy', 'Number of entries: **%s**': 'Number of entries: **%s**', +'Object or table name': 'Object or table name', 'Old password': 'Staré heslo', 'Online book': 'Online kniha', 'Online examples': 'online príklady', @@ -131,13 +173,20 @@ 'Overview': 'Náhľad', 'password': 'heslo', 'Password': 'Heslo', +'Password changed': 'Password changed', +"Password fields don't match": "Password fields don't match", +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Permission': 'Povolenie', 'Permissions': 'Povolenia', +'please input your password again': 'please input your password again', 'Plugins': 'Pluginy', 'Powered by': 'Beží na', 'Preface': 'Predslov', 'previous %s rows': 'predchádzajúce %s riadky', 'previous 100 rows': 'predchádzajúcich 100 riadkov', +'Profile': 'Profile', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'knižnica pygraphviz nenájdená', 'Python': 'Python', 'Query:': 'Otázka:', @@ -148,13 +197,25 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Recepty', 'Record': 'Záznam', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'záznam neexistuje', 'Record ID': 'ID záznamu', 'Record id': 'id záznamu', +'Record Updated': 'Record Updated', 'Register': 'Zaregistrovať sa', 'register': 'registrovať', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Registračný kľúč', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Remember me (for 30 days)': 'Zapamätaj si ma (na 30 dní)', +'Request reset password': 'Request reset password', 'Reset Password key': 'Nastaviť registračný kľúč', 'Role': 'Rola', 'Roles': 'Role', @@ -164,6 +225,7 @@ 'Semantic': 'Významový', 'Services': 'Služby', 'Sign Up': 'Prihlásiť sa', +'Sign up': 'Sign up', 'Size of cache:': 'Veľkosť cache:', 'state': 'stav', 'Statistics': 'Štatistiky', @@ -179,30 +241,50 @@ 'The output of the file is a dictionary that was rendered by the view %s': 'Výstup zo súboru je slovník, ktorý bol vykreslený v zobrazení %s', 'The Views': 'Zobrazenia', 'This App': 'Táto aplikácia', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'This is a copy of the scaffolding application': 'Toto je kópia skeletu aplikácie', 'Time in Cache (h:m:s)': 'Čas v cache (h:m:s)', 'Timestamp': 'Časová pečiatka', 'Traceback': 'Vystopovať', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'unable to parse csv file': 'nedá sa načítať csv súbor', +'Unable to send email': 'Unable to send email', 'Update:': 'Upraviť:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Použite (...)&(...) pre AND, (...)|(...) pre OR a ~(...) pre NOT na poskladanie komplexnejších otázok.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': 'Používateľ %(id)s prihlásený', 'User %(id)s Logged-out': 'Používateľ %(id)s odhlásený', 'User %(id)s Password changed': 'Používateľ %(id)s zmenil heslo', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', 'User %(id)s Profile updated': 'Používateľ %(id)s upravil profil', 'User %(id)s Registered': 'Používateľ %(id)s sa zaregistroval', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'ID používateľa', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Používatelia', 'Verify Password': 'Zopakujte heslo', 'Videos': 'Videá', 'View': 'Zobraziť', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py': 'Vitajte vo web2py', 'Welcome to web2py!': 'Vitajte vo web2py!', 'Which called the function %s located in the file %s': 'Ktorý zavolal funkciu %s nachádzajúci sa v súbore %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Pracuje...', 'You are successfully running web2py': 'Úspešne ste spustili web2py', 'You can modify this application and adapt it to your needs': 'Môžete upraviť túto aplikáciu a prispôsobiť ju svojim potrebám', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'Navštívili ste URL %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/tr.py b/applications/welcome/languages/tr.py old mode 100755 new mode 100644 index 63fb71af..537c2a83 --- a/applications/welcome/languages/tr.py +++ b/applications/welcome/languages/tr.py @@ -18,9 +18,20 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '@markmin\x01**Hello World**': '**Merhaba Dünya**', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'Bir hata oluştu, lütfen sayfayı [[yenileyin yükleyin %s]] ', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'About': 'Hakkında', 'Access Control': 'Erişim Denetimi', 'admin': 'admin', @@ -28,9 +39,11 @@ 'Ajax Recipes': 'Ajax Tarifleri', 'An error occured, please %s the page': 'Bir hata meydana geldi, lütfen sayfayı %s', 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'API Example': 'API Example', 'appadmin is disabled because insecure channel': 'appadmin is disabled because insecure channel', 'Apply changes': 'Değişiklikleri uygula', 'Are you sure you want to delete this object?': 'Bu nesneyi silmek istediğinden emin misin?', +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'Kullanılabilir Varitabanları ve Tablolar', 'Buy this book': 'Bu kitabı satın alın', "Buy web2py's book": "Buy web2py's book", @@ -41,10 +54,12 @@ 'Cache Keys': 'Cache Keys', 'Cannot be empty': 'Boş bırakılamaz', 'Change password': 'Parolayı değiştir', +'Change Password': 'Change Password', 'Check to delete': 'Silmek için denetle', 'Clear CACHE?': 'Clear CACHE?', 'Clear DISK': 'Clear DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Client IP': 'İstemci IP', 'Community': 'Topluluk', 'Components and Plugins': 'Bileşenler ve Eklentiler', @@ -80,6 +95,9 @@ 'E-mail': 'E-posta', 'Edit current record': 'Edit current record', 'Email and SMS': 'E-posta ve kısa mesaj (SMS)', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'enter a value': 'bir değer giriniz', 'enter an integer between %(min)g and %(max)g': '%(min)g ve %(max)g arasında bir sayı girin', 'enter date and time as %(format)s': 'tarih ve saati %(format)s biçiminde girin', @@ -91,9 +109,12 @@ 'Forgot username?': 'Kullanıcı adını mı unuttun?', 'Forms and Validators': 'Biçimler ve Doğrulayıcılar', 'Free Applications': 'Ücretsiz uygulamalar', +'Function disabled': 'Function disabled', 'Giriş': 'Giriş', 'Graph Model': 'Grafik Modeli', +'Grid Example': 'Grid Example', 'Group %(group_id)s created': '%(group_id)s takımı oluşturuldu', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'Takım ID', 'Group uniquely assigned to user %(id)s': 'Grup özgün olarak %(id)s kullanıcılara atandı', 'Groups': 'Gruplar', @@ -106,15 +127,25 @@ 'How did you get here?': 'Bu sayfayı görüntüleme uğruna neler mi oldu?', 'import': 'import', 'Import/Export': 'Dışa/İçe Aktar', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': 'Internal State', 'Introduction': 'Giriş', 'invalid controller': 'geçersiz denetleyici', 'Invalid email': 'Yanlış eposta', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', +'Invalid password': 'Invalid password', 'Invalid Query': 'Invalid Query', 'invalid request': 'invalid request', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Is Active': 'Etkin', 'Kayıt ol': 'Kayıt ol', 'Key': 'Key', +'Key verified': 'Key verified', 'Last name': 'Soyad', 'Layout': 'Şablon', 'Layout Plugins': 'Şablon Eklentileri', @@ -124,9 +155,11 @@ 'Logged in': 'Giriş yapıldı', 'Logged out': 'Çıkış yapıldı', 'Login': 'Giriş', +'Login disabled by administrator': 'Login disabled by administrator', 'Logout': 'Terket', 'Lost Password': 'Şifremi unuttum', 'Lost password?': 'Şifrenizimi unuttunuz?', +'Lost your password?': 'Lost your password?', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', 'Manage Cache': 'Manage Cache', @@ -152,7 +185,10 @@ 'Other Recipes': 'Diğer Tarifler', 'Overview': 'Göz gezdir', 'Password': 'Parola', +'Password changed': 'Password changed', "Password fields don't match": 'Parolalar uyuşmuyor', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Permission': 'Permission', 'Permissions': 'Permissions', 'please input your password again': 'lütfen parolanızı tekrar girin', @@ -161,6 +197,7 @@ 'Preface': 'Önzös', 'previous %s rows': 'previous %s rows', 'Profile': 'Profil', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'pygraphviz library not found', 'Python': 'Python', 'Query:': 'Sorgu:', @@ -171,12 +208,21 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Tarifeler', 'Record': 'Record', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'record does not exist', 'Record ID': 'Kayıt ID', 'Record id': 'Record id', +'Record Updated': 'Record Updated', 'Register': 'Kayıt ol', 'Registration identifier': 'Kayıt belirleyicisi', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Kayıt anahtarı', +'Registration needs verification': 'Registration needs verification', 'Registration successful': 'Kayıt başarılı', 'reload': 'yeniden yükle', 'Remember me (for 30 days)': 'Beni hatırla (30 gün)', @@ -190,6 +236,7 @@ 'Semantic': 'Anlamsal', 'Services': 'Hizmetler', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'Size of cache:': 'Size of cache:', 'state': 'durum', 'Statistics': 'Statistics', @@ -203,30 +250,50 @@ 'The output of the file is a dictionary that was rendered by the view %s': 'Son olarak fonksiyonların vs. işlenip %s dosyasıyla tasarıma yedirilmesiyle sayfayı görüntüledin', 'The Views': 'Görünümler', 'This App': 'Bu Uygulama', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', 'This email already has an account': 'Bu e-postaya ait bir hesap zaten var', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', 'Timestamp': 'Zaman damgası', 'Traceback': 'Traceback', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'unable to parse csv file': 'unable to parse csv file', +'Unable to send email': 'Unable to send email', 'Update:': 'Güncelle:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Karmaşık sorgularda Ve (AND) için (...)&(...) kullanın, Veya (OR) için (...)|(...) kullanın ve DEĞİL (NOT) için ~(...) kullanın. ', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': '%(id)s Giriş yaptı', 'User %(id)s Logged-out': '%(id)s çıkış yaptı', +'User %(id)s Password changed': 'User %(id)s Password changed', 'User %(id)s Password reset': 'Kullanıc %(id)s Parolasını sıfırla', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', 'User %(id)s Registered': '%(id)s Kayıt oldu', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'Kullanıcı ID', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'value already in database or empty': 'değer boş ya da veritabanında zaten mevcut', 'Verify Password': 'Parolanı Onayla', 'Videos': 'Videolar', 'View': 'Görünüm', 'Welcome': 'Hoşgeldin', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py!': "web2py'ye hoşgeldiniz!", 'Which called the function %s located in the file %s': 'Bu ziyaretle %s fonksiyonunu %s dosyasından çağırmış oldun ', +'Wiki Example': 'Wiki Example', 'Working...': 'Çalışıyor...', 'You are successfully running web2py': 'web2py çatısını çalıştırmayı başardın', 'You can modify this application and adapt it to your needs': 'Artık uygulamayı istediğin gibi düzenleyebilirsin!', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': '%s adresini ziyaret ettin', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/uk.py b/applications/welcome/languages/uk.py old mode 100755 new mode 100644 index eb2678eb..28f77f00 --- a/applications/welcome/languages/uk.py +++ b/applications/welcome/languages/uk.py @@ -29,7 +29,10 @@ '?': '?', '@markmin\x01(**%.0d MB**)': '(**``%.0d``:red МБ**)', '@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{елемент(items)}, **%(bytes)s** %%{байт(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**нема в наявності**``:red (потребує Пітонівської бібліотеки [[guppy [посилання відкриється у новому вікні] http://pypi.python.org/pypi/guppy/ popup]])', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', '@markmin\x01An error occured, please [[reload %s]] the page': 'Сталась помилка, будь-ласка [[перевантажте %s]] сторінку', '@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': "Час життя об'єктів в КЕШІ сягає **%(hours)02d** %%{годину(hours)} **%(min)02d** %%{хвилину(min)} та **%(sec)02d** %%{секунду(sec)}.", '@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': "Час життя об'єктів в ДИСКОВОМУ КЕШІ сягає **%(hours)02d** %%{годину(hours)} **%(min)02d** %%{хвилину(min)} та **%(sec)02d** %%{секунду(sec)}.", @@ -37,14 +40,18 @@ '@markmin\x01Number of entries: **%s**': 'Кількість входжень: ``**%s**``:red', '@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': "Час життя об'єктів в ОЗП-КЕШІ сягає **%(hours)02d** %%{годину(hours)} **%(min)02d** %%{хвилину(min)} та **%(sec)02d** %%{секунду(sec)}.", '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'About': 'Про додаток', 'Access Control': 'Контроль доступу', 'admin': 'admin', 'Administrative Interface': 'Адміністративний інтерфейс', 'Ajax Recipes': 'Рецепти для Ajax', 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'API Example': 'API Example', 'appadmin is disabled because insecure channel': 'використовується незахищенний канал (HTTP). Appadmin вимкнено', +'Apply changes': 'Apply changes', 'Are you sure you want to delete this object?': "Ви впевнені, що хочете вилучити цей об'єкт?", +'Authentication code': 'Authentication code', 'Available Databases and Tables': 'Доступні бази даних та таблиці', 'Buy this book': 'Купити книжку', "Buy web2py's book": "Buy web2py's book", @@ -55,11 +62,13 @@ 'Cache Keys': 'Ключі кешу', 'Cannot be empty': 'Порожнє значення неприпустиме', 'Change password': 'Змінити пароль', +'Change Password': 'Change Password', 'Check to delete': 'Позначити для вилучення', 'Check to delete:': 'Позначте для вилучення:', 'Clear CACHE?': 'Очистити ВЕСЬ кеш?', 'Clear DISK': 'Очистити ДИСКОВИЙ кеш', 'Clear RAM': "Очистити кеш В ПАМ'ЯТІ", +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Client IP': 'IP клієнта', 'Community': 'Спільнота', 'Components and Plugins': 'Компоненти та втулки', @@ -97,6 +106,9 @@ 'Edit current record': 'Редагувати поточний запис', 'Edit Page': 'Редагувати сторінку', 'Email and SMS': 'Ел.пошта та SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'enter a value': 'введіть значення', 'enter an integer between %(min)g and %(max)g': 'введіть ціле число між %(min)g та %(max)g', 'Error!': 'Помилка!', @@ -108,8 +120,11 @@ 'Forgot username?': "Забули ім'я користувача?", 'Forms and Validators': 'Форми та коректність даних', 'Free Applications': 'Вільні додатки', +'Function disabled': 'Function disabled', 'Graph Model': 'Графова Модель', +'Grid Example': 'Grid Example', 'Group %(group_id)s created': 'Групу %(group_id)s створено', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': 'Ідентифікатор групи', 'Group uniquely assigned to user %(id)s': "Група унікально зв'язана з користувачем %(id)s", 'Groups': 'Групи', @@ -120,17 +135,25 @@ 'How did you get here?': 'Як цього було досягнуто?', 'import': 'Імпортувати', 'Import/Export': 'Імпорт/Експорт', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'insert new': 'Створити новий запис', 'insert new %s': 'створити новий запис %s', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': 'Внутрішній стан', 'Introduction': 'Введення', 'Invalid email': 'Невірна адреса ел.пошти', +'Invalid key': 'Invalid key', 'Invalid login': "Невірне ім'я користувача", 'Invalid password': 'Невірний пароль', 'Invalid Query': 'Помилковий запит', 'invalid request': 'хибний запит', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Is Active': 'Активна', 'Key': 'Ключ', +'Key verified': 'Key verified', 'Last name': 'Прізвище', 'Layout': 'Макет (Layout)', 'Layout Plugins': 'Втулки макетів', @@ -140,9 +163,11 @@ 'Logged in': 'Вхід здійснено', 'Logged out': 'Вихід здійснено', 'Login': 'Вхід', +'Login disabled by administrator': 'Login disabled by administrator', 'Logout': 'Вихід', 'Lost Password': 'Забули пароль', 'Lost password?': 'Забули пароль?', +'Lost your password?': 'Lost your password?', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', 'Manage Cache': 'Управління кешем', @@ -174,6 +199,8 @@ 'Password': 'Пароль', 'Password changed': 'Пароль змінено', "Password fields don't match": 'Пароль не співпав', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Permission': 'Permission', 'Permissions': 'Permissions', 'please input your password again': 'Будь-ласка введіть пароль ще раз', @@ -194,14 +221,21 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Рецепти', 'Record': 'запис', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', 'Record %(id)s updated': 'Запис %(id)s змінено', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': 'запису не існує', 'Record ID': 'Ід.запису', 'Record id': 'ід. запису', 'Record Updated': 'Запис змінено', 'Register': 'Реєстрація', 'Registration identifier': 'Реєстраційний ідентифікатор', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': 'Реєстраційний ключ', +'Registration needs verification': 'Registration needs verification', 'Registration successful': 'Реєстрація пройшла успішно', 'Remember me (for 30 days)': "Запам'ятати мене (на 30 днів)", 'Request reset password': 'Запит на зміну пароля', @@ -215,6 +249,7 @@ 'Semantic': 'Семантика', 'Services': 'Сервіс', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'Size of cache:': 'Розмір кешу:', 'state': 'стан', 'Statistics': 'Статистика', @@ -228,33 +263,51 @@ 'The output of the file is a dictionary that was rendered by the view %s': 'Результат функції - словник пар (назва=значення) було відображено з допомогою відображення (view) %s', 'The Views': 'Відображення (Views)', 'This App': 'Цей додаток', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', 'This email already has an account': 'Вказана адреса ел.пошти вже зареєстрована', 'Time in Cache (h:m:s)': 'Час знаходження в кеші (h:m:s)', 'Timestamp': 'Відмітка часу', 'too short': 'Занадто короткий', 'Traceback': 'Traceback', 'Twitter': 'Твіттер', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'unable to parse csv file': 'не вдається розібрати csv-файл', +'Unable to send email': 'Unable to send email', 'Update:': 'Оновити:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Для створення складних запитів використовуйте (...)&(...) замість AND, (...)|(...) замість OR, та ~(...) замість NOT.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': 'Користувач %(id)s увійшов', 'User %(id)s Logged-out': 'Користувач %(id)s вийшов', 'User %(id)s Password changed': 'Користувач %(id)s змінив свій пароль', 'User %(id)s Password reset': 'Користувач %(id)s скинув пароль', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', 'User %(id)s Profile updated': 'Параметри користувача %(id)s змінено', 'User %(id)s Registered': 'Користувач %(id)s зареєструвався', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': 'Ід.користувача', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'value already in database or empty': 'значення вже в базі даних або порожнє', 'Verify Password': 'Повторити пароль', 'Videos': 'Відео', 'View': 'Відображення (View)', 'Welcome': 'Ласкаво просимо', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome to web2py!': 'Ласкаво просимо до web2py!', 'Which called the function %s located in the file %s': 'Управління передалось функції %s, яка розташована у файлі %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Працюємо...', 'You are successfully running web2py': 'Ви успішно запустили web2py', 'You can modify this application and adapt it to your needs': 'Ви можете модифікувати цей додаток і адаптувати його до своїх потреб', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'Ви відвідали наступну адресу: %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/zh-cn.py b/applications/welcome/languages/zh-cn.py old mode 100755 new mode 100644 index 916384d5..1c20626e --- a/applications/welcome/languages/zh-cn.py +++ b/applications/welcome/languages/zh-cn.py @@ -14,7 +14,19 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': '新版 web2py 已推出', 'A new version of web2py is available: %s': '新版 web2py 已推出: %s', 'about': '关于', @@ -30,7 +42,9 @@ 'Ajax Recipes': 'Ajax Recipes', 'An error occured, please %s the page': 'An error occured, please %s the page', 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'API Example': 'API Example', 'appadmin is disabled because insecure channel': '管理界面在非安全通道下被禁用', +'Apply changes': 'Apply changes', 'Are you sure you want to delete file "%s"?': '确定要删除文件"%s"?', 'Are you sure you want to delete this object?': '确定要删除该对象么?', 'Are you sure you want to uninstall application "%s"': '确定要删除应用程序 "%s"', @@ -39,6 +53,7 @@ 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': '注意: 因为在测试模式不保证多线程安全性,所以不可同时执行多个测试案例', 'ATTENTION: you cannot edit the running application!': '注意:不可编辑正在执行的应用程序!', 'Authentication': '验证', +'Authentication code': 'Authentication code', 'Available Databases and Tables': '可提供的数据库和数据表', 'Buy this book': '购买本书', "Buy web2py's book": "Buy web2py's book", @@ -51,11 +66,13 @@ 'Cannot compile: there are errors in your app. Debug it, correct errors and try again.': '编译失败:应用程序有错误,请排除错误后再尝试编译.', 'Change Password': '修改密码', 'change password': '修改密码', +'Change password': 'Change password', 'Check to delete': '打勾以示删除', 'Check to delete:': '打勾以示删除:', 'Clear CACHE?': 'Clear CACHE?', 'Clear DISK': 'Clear DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Client IP': '客户端网址(IP)', 'Community': 'Community', 'Components and Plugins': 'Components and Plugins', @@ -106,6 +123,9 @@ 'Editing file': '编辑文件', 'Editing file "%s"': '编辑文件"%s"', 'Email and SMS': 'Email and SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'enter an integer between %(min)g and %(max)g': 'enter an integer between %(min)g and %(max)g', 'Error logs for "%(app)s"': '"%(app)s"的错误记录', 'Errors': 'Errors', @@ -115,9 +135,14 @@ 'Forgot username?': '忘记用户名?', 'Forms and Validators': 'Forms and Validators', 'Free Applications': 'Free Applications', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': '沒有 doctests 的函数会显示 [passed].', 'Graph Model': 'Graph Model', +'Grid Example': 'Grid Example', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': '群组编号', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Groups': 'Groups', 'Hello World': 'Hello World', 'Helping web2py': 'Helping web2py', @@ -126,18 +151,28 @@ 'How did you get here?': 'How did you get here?', 'import': 'import', 'Import/Export': '导入/导出', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Index': '索引', 'insert new': '插入新纪录', 'insert new %s': '插入新纪录 %s', 'Installed applications': '已安裝应用程序', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': '內部状态', 'Introduction': 'Introduction', 'Invalid action': '非法操作(action)', 'Invalid email': '不符合电子邮件格式', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', +'Invalid password': 'Invalid password', 'Invalid Query': '无效的查询请求', 'invalid request': '网络要求无效', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Is Active': 'Is Active', 'Key': 'Key', +'Key verified': 'Key verified', 'Language files (static strings) updated': '语言文件已更新', 'Languages': '各国语言', 'Last name': '姓', @@ -148,13 +183,17 @@ 'License for': '软件授权', 'Live Chat': 'Live Chat', 'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'login': '登录', 'Login': '登录', +'Login disabled by administrator': 'Login disabled by administrator', 'Login to the Administrative Interface': '登录到管理员界面', 'logout': '登出', 'Logout': '登出', 'Lost Password': '忘记密码', 'Lost password?': '忘记密码?', +'Lost your password?': 'Lost your password?', 'Main Menu': '主菜单', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', @@ -167,6 +206,7 @@ 'Modules': '程序模块', 'My Sites': 'My Sites', 'Name': '名字', +'New password': 'New password', 'New Record': '新记录', 'new record inserted': '已插入新记录', 'next %s rows': 'next %s rows', @@ -175,6 +215,7 @@ 'No databases in this application': '该应用程序不含数据库', 'Number of entries: **%s**': 'Number of entries: **%s**', 'Object or table name': 'Object or table name', +'Old password': 'Old password', 'Online book': 'Online book', 'Online examples': '点击进入在线例子', 'or import from csv file': '或导入CSV文件', @@ -184,15 +225,21 @@ 'Other Recipes': 'Other Recipes', 'Overview': '概览', 'Password': '密码', +'Password changed': 'Password changed', "Password fields don't match": '密码不匹配', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Peeking at file': '选择文件', 'Permission': 'Permission', 'Permissions': 'Permissions', +'please input your password again': 'please input your password again', 'Plugins': 'Plugins', 'Powered by': '基于下列技术构建:', 'Preface': 'Preface', 'previous %s rows': 'previous %s rows', 'previous 100 rows': '往前 100 笔', +'Profile': 'Profile', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'pygraphviz library not found', 'Python': 'Python', 'Query:': '查询:', @@ -203,15 +250,26 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Recipes', 'Record': '记录', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': '记录不存在', 'Record ID': '记录编号', 'Record id': '记录编号', +'Record Updated': 'Record Updated', 'Register': '注册', 'register': '注册', 'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': '注册密钥', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'reload': 'reload', 'Remember me (for 30 days)': '记住我(30 天)', +'Request reset password': 'Request reset password', 'Reset Password key': '重置密码', 'Resolve Conflict file': '解决冲突文件', 'Role': '角色', @@ -223,6 +281,7 @@ 'Semantic': 'Semantic', 'Services': 'Services', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'Size of cache:': 'Size of cache:', 'state': '状态', 'Static files': '静态文件', @@ -246,36 +305,59 @@ 'There are no translators, only default language is supported': '沒有对应的语言文件,仅支持原始语言', 'There are no views': '沒有视图', 'This App': '该应用', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'This is the %(filename)s template': '这是%(filename)s文件的模板(template)', 'Ticket': '问题清单', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', 'Timestamp': '时间戳', 'Traceback': 'Traceback', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Unable to check for upgrades': '查询新版本失败', 'Unable to download': '无法下载', 'Unable to download app': '无法下载应用程序', 'unable to parse csv file': '无法解析CSV文件', +'Unable to send email': 'Unable to send email', 'Update:': '更新:', 'Upload existing application': '上传已有应用程序', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': '使用下列方式可得到更复杂的条件表达式, (...)&(...) 代表必须都满足, (...)|(...) 代表其一, ~(...)则代表否.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': '用户 %(id)s 已登录', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', 'User %(id)s Registered': '用户 %(id)s 已注册', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': '用户编号', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Verify Password': '验证密码', 'Videos': '视频', 'View': '查看', 'Views': '视图', 'Welcome': '欢迎', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': '欢迎 %s', 'Welcome to web2py': '欢迎使用 web2py', 'Welcome to web2py!': '欢迎使用 web2py!', 'Which called the function %s located in the file %s': 'Which called the function %s located in the file %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Working...', 'YES': '是', 'You are successfully running web2py': '您已成功运行 web2py', 'You can modify this application and adapt it to your needs': '请根据您的需要修改本程序', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'You visited the url %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/zh-tw.py b/applications/welcome/languages/zh-tw.py old mode 100755 new mode 100644 index 2b64c75d..de25451e --- a/applications/welcome/languages/zh-tw.py +++ b/applications/welcome/languages/zh-tw.py @@ -14,7 +14,19 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': '新版的 web2py 已發行', 'A new version of web2py is available: %s': '新版的 web2py 已發行: %s', 'about': '關於', @@ -30,7 +42,9 @@ 'Ajax Recipes': 'Ajax Recipes', 'An error occured, please %s the page': 'An error occured, please %s the page', 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'API Example': 'API Example', 'appadmin is disabled because insecure channel': '因為來自非安全通道,管理介面關閉', +'Apply changes': 'Apply changes', 'Are you sure you want to delete file "%s"?': '確定要刪除檔案"%s"?', 'Are you sure you want to delete this object?': 'Are you sure you want to delete this object?', 'Are you sure you want to uninstall application "%s"': '確定要移除應用程式 "%s"', @@ -39,6 +53,7 @@ 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': '注意: 因為在測試模式不保證多執行緒安全性,也就是說不可以同時執行多個測試案例', 'ATTENTION: you cannot edit the running application!': '注意:不可編輯正在執行的應用程式!', 'Authentication': '驗證', +'Authentication code': 'Authentication code', 'Available Databases and Tables': '可提供的資料庫和資料表', 'Buy this book': 'Buy this book', "Buy web2py's book": "Buy web2py's book", @@ -51,11 +66,13 @@ 'Cannot compile: there are errors in your app. Debug it, correct errors and try again.': '無法編譯:應用程式中含有錯誤,請除錯後再試一次.', 'Change Password': '變更密碼', 'change password': '變更密碼', +'Change password': 'Change password', 'Check to delete': '打勾代表刪除', 'Check to delete:': '點選以示刪除:', 'Clear CACHE?': 'Clear CACHE?', 'Clear DISK': 'Clear DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Client IP': '客戶端網址(IP)', 'Community': 'Community', 'Components and Plugins': 'Components and Plugins', @@ -106,6 +123,9 @@ 'Editing file': '編輯檔案', 'Editing file "%s"': '編輯檔案"%s"', 'Email and SMS': 'Email and SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'enter an integer between %(min)g and %(max)g': 'enter an integer between %(min)g and %(max)g', 'Error logs for "%(app)s"': '"%(app)s"的錯誤紀錄', 'Errors': 'Errors', @@ -115,9 +135,14 @@ 'Forgot username?': 'Forgot username?', 'Forms and Validators': 'Forms and Validators', 'Free Applications': 'Free Applications', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': '沒有 doctests 的函式會顯示 [passed].', 'Graph Model': 'Graph Model', +'Grid Example': 'Grid Example', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': '群組編號', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Groups': 'Groups', 'Hello World': '嗨! 世界', 'Helping web2py': 'Helping web2py', @@ -126,18 +151,28 @@ 'How did you get here?': 'How did you get here?', 'import': 'import', 'Import/Export': '匯入/匯出', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Index': '索引', 'insert new': '插入新資料', 'insert new %s': '插入新資料 %s', 'Installed applications': '已安裝應用程式', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': '內部狀態', 'Introduction': 'Introduction', 'Invalid action': '不合法的動作(action)', 'Invalid email': '不合法的電子郵件', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', +'Invalid password': 'Invalid password', 'Invalid Query': '不合法的查詢', 'invalid request': '不合法的網路要求(request)', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Is Active': 'Is Active', 'Key': 'Key', +'Key verified': 'Key verified', 'Language files (static strings) updated': '語言檔已更新', 'Languages': '各國語言', 'Last name': '姓', @@ -148,13 +183,17 @@ 'License for': '軟體版權為', 'Live Chat': 'Live Chat', 'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'login': '登入', 'Login': '登入', +'Login disabled by administrator': 'Login disabled by administrator', 'Login to the Administrative Interface': '登入到管理員介面', 'logout': '登出', 'Logout': '登出', 'Lost Password': '密碼遺忘', 'Lost password?': 'Lost password?', +'Lost your password?': 'Lost your password?', 'Main Menu': '主選單', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', @@ -167,6 +206,7 @@ 'Modules': '程式模組', 'My Sites': 'My Sites', 'Name': '名字', +'New password': 'New password', 'New Record': '新紀錄', 'new record inserted': '已插入新紀錄', 'next %s rows': 'next %s rows', @@ -175,6 +215,7 @@ 'No databases in this application': '這應用程式不含資料庫', 'Number of entries: **%s**': 'Number of entries: **%s**', 'Object or table name': 'Object or table name', +'Old password': 'Old password', 'Online book': 'Online book', 'Online examples': '點此處進入線上範例', 'or import from csv file': '或是從逗號分隔檔(CSV)匯入', @@ -184,15 +225,21 @@ 'Other Recipes': 'Other Recipes', 'Overview': 'Overview', 'Password': '密碼', +'Password changed': 'Password changed', "Password fields don't match": '密碼欄不匹配', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Peeking at file': '選擇檔案', 'Permission': 'Permission', 'Permissions': 'Permissions', +'please input your password again': 'please input your password again', 'Plugins': 'Plugins', 'Powered by': '基於以下技術構建:', 'Preface': 'Preface', 'previous %s rows': 'previous %s rows', 'previous 100 rows': '往前 100 筆', +'Profile': 'Profile', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'pygraphviz library not found', 'Python': 'Python', 'Query:': '查詢:', @@ -203,15 +250,26 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Recipes', 'Record': '紀錄', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': '紀錄不存在', 'Record ID': '紀錄編號', 'Record id': '紀錄編號', +'Record Updated': 'Record Updated', 'Register': '註冊', 'register': '註冊', 'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': '註冊金鑰', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'reload': 'reload', 'Remember me (for 30 days)': '記住我(30 天)', +'Request reset password': 'Request reset password', 'Reset Password key': '重設密碼', 'Resolve Conflict file': '解決衝突檔案', 'Role': '角色', @@ -223,6 +281,7 @@ 'Semantic': 'Semantic', 'Services': 'Services', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'Size of cache:': 'Size of cache:', 'state': '狀態', 'Static files': '靜態檔案', @@ -246,36 +305,59 @@ 'There are no translators, only default language is supported': '沒有翻譯檔,只支援原始語言', 'There are no views': '沒有視圖', 'This App': 'This App', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'This is the %(filename)s template': '這是%(filename)s檔案的樣板(template)', 'Ticket': '問題單', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', 'Timestamp': '時間標記', 'Traceback': 'Traceback', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Unable to check for upgrades': '無法做升級檢查', 'Unable to download': '無法下載', 'Unable to download app': '無法下載應用程式', 'unable to parse csv file': '無法解析逗號分隔檔(csv)', +'Unable to send email': 'Unable to send email', 'Update:': '更新:', 'Upload existing application': '更新存在的應用程式', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': '使用下列方式來組合更複雜的條件式, (...)&(...) 代表同時存在的條件, (...)|(...) 代表擇一的條件, ~(...)則代表反向條件.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': '使用者 %(id)s 已登入', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', 'User %(id)s Registered': '使用者 %(id)s 已註冊', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': '使用者編號', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Verify Password': '驗證密碼', 'Videos': 'Videos', 'View': '視圖', 'Views': '視圖', 'Welcome': 'Welcome', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': '歡迎 %s', 'Welcome to web2py': '歡迎使用 web2py', 'Welcome to web2py!': 'Welcome to web2py!', 'Which called the function %s located in the file %s': 'Which called the function %s located in the file %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Working...', 'YES': '是', 'You are successfully running web2py': 'You are successfully running web2py', 'You can modify this application and adapt it to your needs': 'You can modify this application and adapt it to your needs', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'You visited the url %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/languages/zh.py b/applications/welcome/languages/zh.py old mode 100755 new mode 100644 index 97940322..76e8d1f8 --- a/applications/welcome/languages/zh.py +++ b/applications/welcome/languages/zh.py @@ -14,7 +14,19 @@ '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', '?': '?', +'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)', +'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}', +'@markmin\x01**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '**not available** (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)': '``**not available**``:red (requires the Python [[Pympler https://pypi.python.org/pypi/Pympler popup]] library)', +'@markmin\x01An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', +'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})', +'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', +'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)', +'A new password was emailed to you': 'A new password was emailed to you', 'A new version of web2py is available': '新版的 web2py 已發行', 'A new version of web2py is available: %s': '新版的 web2py 已發行: %s', 'about': '關於', @@ -29,7 +41,9 @@ 'Administrator Password:': '管理員密碼:', 'Ajax Recipes': 'Ajax Recipes', 'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page', +'API Example': 'API Example', 'appadmin is disabled because insecure channel': '因為來自非安全通道,管理介面關閉', +'Apply changes': 'Apply changes', 'Are you sure you want to delete file "%s"?': '確定要刪除檔案"%s"?', 'Are you sure you want to delete this object?': 'Are you sure you want to delete this object?', 'Are you sure you want to uninstall application "%s"': '確定要移除應用程式 "%s"', @@ -38,6 +52,7 @@ 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': '注意: 因為在測試模式不保證多執行緒安全性,也就是說不可以同時執行多個測試案例', 'ATTENTION: you cannot edit the running application!': '注意:不可編輯正在執行的應用程式!', 'Authentication': '驗證', +'Authentication code': 'Authentication code', 'Available Databases and Tables': '可提供的資料庫和資料表', 'Buy this book': 'Buy this book', "Buy web2py's book": "Buy web2py's book", @@ -50,11 +65,13 @@ 'Cannot compile: there are errors in your app. Debug it, correct errors and try again.': '無法編譯:應用程式中含有錯誤,請除錯後再試一次.', 'Change Password': '變更密碼', 'change password': '變更密碼', +'Change password': 'Change password', 'Check to delete': '打勾代表刪除', 'Check to delete:': '點選以示刪除:', 'Clear CACHE?': 'Clear CACHE?', 'Clear DISK': 'Clear DISK', 'Clear RAM': 'Clear RAM', +'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password', 'Client IP': '客戶端網址(IP)', 'Community': 'Community', 'Components and Plugins': 'Components and Plugins', @@ -103,6 +120,9 @@ 'Editing file': '編輯檔案', 'Editing file "%s"': '編輯檔案"%s"', 'Email and SMS': 'Email and SMS', +'Email sent': 'Email sent', +'Email verification': 'Email verification', +'Email verified': 'Email verified', 'Error logs for "%(app)s"': '"%(app)s"的錯誤紀錄', 'Errors': 'Errors', 'export as csv file': '以逗號分隔檔(csv)格式匯出', @@ -110,9 +130,14 @@ 'First name': '名', 'Forms and Validators': 'Forms and Validators', 'Free Applications': 'Free Applications', +'Function disabled': 'Function disabled', 'Functions with no doctests will result in [passed] tests.': '沒有 doctests 的函式會顯示 [passed].', 'Graph Model': 'Graph Model', +'Grid Example': 'Grid Example', +'Group %(group_id)s created': 'Group %(group_id)s created', +'Group %(group_id)s deleted': 'Group %(group_id)s deleted', 'Group ID': '群組編號', +'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Groups': 'Groups', 'Hello World': '嗨! 世界', 'Helping web2py': 'Helping web2py', @@ -121,17 +146,27 @@ 'How did you get here?': 'How did you get here?', 'import': 'import', 'Import/Export': '匯入/匯出', +'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.', 'Index': '索引', 'insert new': '插入新資料', 'insert new %s': '插入新資料 %s', 'Installed applications': '已安裝應用程式', +'Insufficient privileges': 'Insufficient privileges', 'Internal State': '內部狀態', 'Introduction': 'Introduction', 'Invalid action': '不合法的動作(action)', 'Invalid email': '不合法的電子郵件', +'Invalid key': 'Invalid key', +'Invalid login': 'Invalid login', +'Invalid password': 'Invalid password', 'Invalid Query': '不合法的查詢', 'invalid request': '不合法的網路要求(request)', +'Invalid reset password': 'Invalid reset password', +'Invalid user': 'Invalid user', +'Invalid username': 'Invalid username', +'Invitation to join %(site)s': 'Invitation to join %(site)s', 'Key': 'Key', +'Key verified': 'Key verified', 'Language files (static strings) updated': '語言檔已更新', 'Languages': '各國語言', 'Last name': '姓', @@ -142,12 +177,16 @@ 'License for': '軟體版權為', 'Live Chat': 'Live Chat', 'Log In': 'Log In', +'Logged in': 'Logged in', +'Logged out': 'Logged out', 'login': '登入', 'Login': '登入', +'Login disabled by administrator': 'Login disabled by administrator', 'Login to the Administrative Interface': '登入到管理員介面', 'logout': '登出', 'Logout': '登出', 'Lost Password': '密碼遺忘', +'Lost your password?': 'Lost your password?', 'Main Menu': '主選單', 'Manage %(action)s': 'Manage %(action)s', 'Manage Access Control': 'Manage Access Control', @@ -158,6 +197,7 @@ 'Modules': '程式模組', 'My Sites': 'My Sites', 'Name': '名字', +'New password': 'New password', 'New Record': '新紀錄', 'new record inserted': '已插入新紀錄', 'next %s rows': 'next %s rows', @@ -165,6 +205,8 @@ 'NO': '否', 'No databases in this application': '這應用程式不含資料庫', 'Number of entries: **%s**': 'Number of entries: **%s**', +'Object or table name': 'Object or table name', +'Old password': 'Old password', 'Online book': 'Online book', 'Online examples': '點此處進入線上範例', 'or import from csv file': '或是從逗號分隔檔(CSV)匯入', @@ -174,15 +216,21 @@ 'Other Recipes': 'Other Recipes', 'Overview': 'Overview', 'Password': '密碼', +'Password changed': 'Password changed', "Password fields don't match": '密碼欄不匹配', +'Password reset': 'Password reset', +'Password retrieve': 'Password retrieve', 'Peeking at file': '選擇檔案', 'Permission': 'Permission', 'Permissions': 'Permissions', +'please input your password again': 'please input your password again', 'Plugins': 'Plugins', 'Powered by': '基於以下技術構建:', 'Preface': 'Preface', 'previous %s rows': 'previous %s rows', 'previous 100 rows': '往前 100 筆', +'Profile': 'Profile', +'Profile updated': 'Profile updated', 'pygraphviz library not found': 'pygraphviz library not found', 'Python': 'Python', 'Query:': '查詢:', @@ -193,13 +241,25 @@ 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.', 'Recipes': 'Recipes', 'Record': '紀錄', +'Record %(id)s created': 'Record %(id)s created', +'Record %(id)s deleted': 'Record %(id)s deleted', +'Record %(id)s read': 'Record %(id)s read', +'Record %(id)s updated': 'Record %(id)s updated', +'Record Created': 'Record Created', +'Record Deleted': 'Record Deleted', 'record does not exist': '紀錄不存在', 'Record ID': '紀錄編號', 'Record id': '紀錄編號', +'Record Updated': 'Record Updated', 'Register': '註冊', 'register': '註冊', +'Registration identifier': 'Registration identifier', +'Registration is pending approval': 'Registration is pending approval', 'Registration key': '註冊金鑰', +'Registration needs verification': 'Registration needs verification', +'Registration successful': 'Registration successful', 'Remember me (for 30 days)': '記住我(30 天)', +'Request reset password': 'Request reset password', 'Reset Password key': '重設密碼', 'Resolve Conflict file': '解決衝突檔案', 'Role': '角色', @@ -211,6 +271,7 @@ 'Semantic': 'Semantic', 'Services': 'Services', 'Sign Up': 'Sign Up', +'Sign up': 'Sign up', 'Size of cache:': 'Size of cache:', 'state': '狀態', 'Static files': '靜態檔案', @@ -234,35 +295,58 @@ 'There are no translators, only default language is supported': '沒有翻譯檔,只支援原始語言', 'There are no views': '沒有視圖', 'This App': 'This App', +'This code was emailed to you and is required for login.': 'This code was emailed to you and is required for login.', +'This email already has an account': 'This email already has an account', 'This is the %(filename)s template': '這是%(filename)s檔案的樣板(template)', 'Ticket': '問題單', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', 'Timestamp': '時間標記', 'Traceback': 'Traceback', 'Twitter': 'Twitter', +'Two-step Login Authentication Code': 'Two-step Login Authentication Code', 'Unable to check for upgrades': '無法做升級檢查', 'Unable to download': '無法下載', 'Unable to download app': '無法下載應用程式', 'unable to parse csv file': '無法解析逗號分隔檔(csv)', +'Unable to send email': 'Unable to send email', 'Update:': '更新:', 'Upload existing application': '更新存在的應用程式', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': '使用下列方式來組合更複雜的條件式, (...)&(...) 代表同時存在的條件, (...)|(...) 代表擇一的條件, ~(...)則代表反向條件.', 'User': 'User', +'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s', 'User %(id)s Logged-in': '使用者 %(id)s 已登入', +'User %(id)s Logged-out': 'User %(id)s Logged-out', +'User %(id)s Password changed': 'User %(id)s Password changed', +'User %(id)s Password reset': 'User %(id)s Password reset', +'User %(id)s Password retrieved': 'User %(id)s Password retrieved', +'User %(id)s Profile updated': 'User %(id)s Profile updated', 'User %(id)s Registered': '使用者 %(id)s 已註冊', +'User %(id)s Username retrieved': 'User %(id)s Username retrieved', +'User %(id)s Verification email sent': 'User %(id)s Verification email sent', +'User %(id)s verified registration key': 'User %(id)s verified registration key', 'User ID': '使用者編號', +'Username': 'Username', +'Username already taken': 'Username already taken', +'Username retrieve': 'Username retrieve', 'Users': 'Users', 'Verify Password': '驗證密碼', 'Videos': 'Videos', 'View': '視圖', 'Views': '視圖', +'Welcome %(username)s! Click on the link %(link)s to verify your email': 'Welcome %(username)s! Click on the link %(link)s to verify your email', 'Welcome %s': '歡迎 %s', 'Welcome to web2py': '歡迎使用 web2py', 'Welcome to web2py!': 'Welcome to web2py!', 'Which called the function %s located in the file %s': 'Which called the function %s located in the file %s', +'Wiki Example': 'Wiki Example', 'Working...': 'Working...', 'YES': '是', 'You are successfully running web2py': 'You are successfully running web2py', 'You can modify this application and adapt it to your needs': 'You can modify this application and adapt it to your needs', +'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process', 'You visited the url %s': 'You visited the url %s', +'Your password is: %(password)s': 'Your password is: %(password)s', +'Your temporary login code is {0}': 'Your temporary login code is {0}', +'Your username is: %(username)s': 'Your username is: %(username)s', +'Your username was emailed to you': 'Your username was emailed to you', } diff --git a/applications/welcome/static/css/web2py-bootstrap4.css b/applications/welcome/static/css/web2py-bootstrap4.css index 2a8348c9..7bcb6ea7 100644 --- a/applications/welcome/static/css/web2py-bootstrap4.css +++ b/applications/welcome/static/css/web2py-bootstrap4.css @@ -1,4 +1,3 @@ - label, th { font-weigth: bold; white-space: nowrap; @@ -20,6 +19,9 @@ div.w2p_flash { margin: 0 0 20px; padding: 15px 35px 15px 15px; } +.nav-item a { + white-space: nowrap; +} div.w2p_flash.alert:hover { opacity: 1; } diff --git a/applications/welcome/static/js/web2py-bootstrap4.js b/applications/welcome/static/js/web2py-bootstrap4.js index 91f3d8ff..6d42fb4b 100644 --- a/applications/welcome/static/js/web2py-bootstrap4.js +++ b/applications/welcome/static/js/web2py-bootstrap4.js @@ -52,7 +52,7 @@ }); } var ul = this; - $(ul).find(":text").addClass('form-control').wrap("
").after('
').keypress(function(e) { + $(ul).find(":text").addClass('form-control').wrap("
").after('
 
').keypress(function(e) { return (e.which == 13) ? pe(ul, e) : true; }).next().click(function(e) { pe(ul, e); diff --git a/applications/welcome/views/default/index.html b/applications/welcome/views/default/index.html index 9107d644..61a53cbf 100644 --- a/applications/welcome/views/default/index.html +++ b/applications/welcome/views/default/index.html @@ -1,7 +1,7 @@ {{extend 'layout.html'}} {{block header}} -
+

/{{=request.application}}/{{=request.controller}}/{{=request.function}}

diff --git a/applications/welcome/views/generic.csv b/applications/welcome/views/generic.csv new file mode 100644 index 00000000..a76bf0be --- /dev/null +++ b/applications/welcome/views/generic.csv @@ -0,0 +1,17 @@ +{{"""Usage: + + def controller(): + return {"": db().select(db.thing.ALL)} + +And then visit that controller with a .csv extention name +""" +}}{{if len(response._vars)==1:}}{{ +# Not yet find a Python 2/3 compatible StringIO pattern, +# we avoid this solution http://web2py.com/books/default/chapter/29/10/services#CSV +# Here we buffer the entire csv file instead (it is your controller's job to limit the volume anyway), +# based on: http://web2py.com/books/default/chapter/29/06/the-database-abstraction-layer#CSV-one-Table-at-a-time- + +content = response._vars[next(iter(response._vars))] +response.headers['Content-Type'] = 'application/vnd.ms-excel' +response.write(str(content), escape=False) +}}{{pass}} diff --git a/applications/welcome/views/layout.html b/applications/welcome/views/layout.html index 5643dfdf..d26d052e 100644 --- a/applications/welcome/views/layout.html +++ b/applications/welcome/views/layout.html @@ -35,7 +35,7 @@
{{=response.flash or ''}}
-
.*?)\.(?P.*?)\..*').match(name) + items = re.match(REGEX_UPLOAD_PATTERN, name) if not items: raise HTTP(404) - (t, f) = (items.group('table'), items.group('field')) + t = items.group('table'); f = items.group('field') try: field = db[t][f] except (AttributeError, KeyError): @@ -801,6 +804,8 @@ class Session(Storage): - session_filename """ + REGEX_SESSION_FILE = r'^(?:[\w-]+/)?[\w.-]+$' + def connect(self, request=None, response=None, @@ -836,7 +841,6 @@ class Session(Storage): compression_level(int): 0-9, sets zlib compression on the data before the encryption """ - from gluon.dal import Field request = request or current.request response = response or current.response masterapp = masterapp or request.application @@ -892,7 +896,7 @@ class Session(Storage): response.session_file = None # check if the session_id points to a valid sesion filename if response.session_id: - if not regex_session_id.match(response.session_id): + if not re.match(self.REGEX_SESSION_FILE, response.session_id): response.session_id = None else: response.session_filename = \ @@ -927,7 +931,7 @@ class Session(Storage): elif response.session_storage_type == 'db': if global_settings.db_sessions is not True: global_settings.db_sessions.add(masterapp) - # if had a session on file alreday, close it (yes, can happen) + # if had a session on file already, close it (yes, can happen) if response.session_file: self._close(response) # if on GAE tickets go also in DB @@ -939,7 +943,7 @@ class Session(Storage): table_migrate = False tname = tablename + '_' + masterapp table = db.get(tname, None) - # Field = db.Field + Field = db.Field if table is None: db.define_table( tname, @@ -969,7 +973,7 @@ class Session(Storage): if row: # rows[0].update_record(locked=True) # Unpickle the data - session_data = pickle.loads(row[b'session_data']) + session_data = pickle.loads(row['session_data']) self.update(session_data) response.session_new = False else: @@ -1051,7 +1055,7 @@ class Session(Storage): if record_id.isdigit() and long(record_id) > 0: new_unique_key = web2py_uuid() row = table(record_id) - if row and row[b'unique_key'] == to_bytes(unique_key): + if row and row['unique_key'] == unique_key: table._db(table.id == record_id).update(unique_key=new_unique_key) else: record_id = None @@ -1075,6 +1079,16 @@ class Session(Storage): scookies['HttpOnly'] = True if self._secure: scookies['secure'] = True + if self._same_site is None: + # Using SameSite Lax Mode is the default + # You actually have to call session.samesite(False) if you really + # dont want the extra protection provided by the SameSite header + self._same_site = 'Lax' + if self._same_site: + if 'samesite' not in Cookie.Morsel._reserved: + # Python version 3.7 and lower needs this + Cookie.Morsel._reserved['samesite'] = 'SameSite' + scookies['samesite'] = self._same_site def clear_session_cookies(self): request = current.request @@ -1153,6 +1167,9 @@ class Session(Storage): def secure(self): self._secure = True + def samesite(self, mode='Lax'): + self._same_site = mode + def forget(self, response=None): self._close(response) self._forget = True @@ -1180,7 +1197,7 @@ class Session(Storage): def _unchanged(self, response): if response.session_new: - internal = ['_last_timestamp', '_secure', '_start_timestamp'] + internal = ['_last_timestamp', '_secure', '_start_timestamp', '_same_site'] for item in self.keys(): if item not in internal: return False diff --git a/gluon/highlight.py b/gluon/highlight.py index b8cae776..d7482654 100644 --- a/gluon/highlight.py +++ b/gluon/highlight.py @@ -6,14 +6,31 @@ | Copyrighted by Massimo Di Pierro | License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) """ -from __future__ import print_function -from gluon._compat import xrange -from gluon.utils import local_html_escape + +from pydal._compat import xrange +from yatl.sanitizer import xmlescape import re __all__ = ['highlight'] +class all_styles(object): + """ + Custom non-data descriptor for lazy initialization of + Highlighter.all_styles class attribute. + + see: + https://docs.python.org/2/reference/datamodel.html#implementing-descriptors + or + https://docs.python.org/3/reference/datamodel.html#implementing-descriptors + """ + + def __get__(self, instance, owner): + val = _get_all_styles(owner) + setattr(owner, 'all_styles', val) + return val + + class Highlighter(object): """Does syntax highlighting. @@ -63,7 +80,7 @@ class Highlighter(object): Callback for C specific highlighting. """ - value = local_html_escape(match.group(), quote=False) + value = xmlescape(match.group(), quote=False) self.change_style(token, style) self.output.append(value) @@ -77,7 +94,7 @@ class Highlighter(object): Callback for python specific highlighting. """ - value = local_html_escape(match.group(), quote=False) + value = xmlescape(match.group(), quote=False) if token == 'MULTILINESTRING': self.change_style(token, style) self.output.append(value) @@ -114,90 +131,14 @@ class Highlighter(object): Callback for HTML specific highlighting. """ - value = local_html_escape(match.group(), quote=False) + value = xmlescape(match.group(), quote=False) self.change_style(token, style) self.output.append(value) if token == 'GOTOPYTHON': return 'PYTHON' return None - all_styles = { - 'C': (c_tokenizer, ( - ('COMMENT', re.compile(r'//.*\r?\n'), - 'color: green; font-style: italic'), - ('MULTILINECOMMENT', re.compile(r'/\*.*?\*/', re.DOTALL), - 'color: green; font-style: italic'), - ('PREPROCESSOR', re.compile(r'\s*#.*?[^\\]\s*\n', - re.DOTALL), 'color: magenta; font-style: italic'), - ('PUNC', re.compile(r'[-+*!&|^~/%\=<>\[\]{}(),.:]'), - 'font-weight: bold'), - ('NUMBER', - re.compile(r'0x[0-9a-fA-F]+|[+-]?\d+(\.\d+)?([eE][+-]\d+)?|\d+'), - 'color: red'), - ('KEYWORD', re.compile(r'(sizeof|int|long|short|char|void|' - + r'signed|unsigned|float|double|' - + r'goto|break|return|continue|asm|' - + r'case|default|if|else|switch|while|for|do|' - + r'struct|union|enum|typedef|' - + r'static|register|auto|volatile|extern|const)(?![a-zA-Z0-9_])'), - 'color:#185369; font-weight: bold'), - ('CPPKEYWORD', - re.compile(r'(class|private|protected|public|template|new|delete|' - + r'this|friend|using|inline|export|bool|throw|try|catch|' - + r'operator|typeid|virtual)(?![a-zA-Z0-9_])'), - 'color: blue; font-weight: bold'), - ('STRING', re.compile(r'r?u?\'(.*?)(?\[\]{}(),.:]'), - 'font-weight: bold'), - ('NUMBER', - re.compile(r'0x[0-9a-fA-F]+|[+-]?\d+(\.\d+)?([eE][+-]\d+)?|\d+' - ), 'color: red'), - ('KEYWORD', - re.compile(r'(def|class|break|continue|del|exec|finally|pass|' - + r'print|raise|return|try|except|global|assert|lambda|' - + r'yield|for|while|if|elif|else|and|in|is|not|or|import|' - + r'from|True|False)(?![a-zA-Z0-9_])'), - 'color:#185369; font-weight: bold'), - ('WEB2PY', - re.compile(r'(request|response|session|cache|redirect|local_import|HTTP|TR|XML|URL|BEAUTIFY|A|BODY|BR|B|CAT|CENTER|CODE|COL|COLGROUP|DIV|EM|EMBED|FIELDSET|LEGEND|FORM|H1|H2|H3|H4|H5|H6|IFRAME|HEAD|HR|HTML|I|IMG|INPUT|LABEL|LI|LINK|MARKMIN|MENU|META|OBJECT|OL|ON|OPTION|P|PRE|SCRIPT|SELECT|SPAN|STYLE|TABLE|THEAD|TBODY|TFOOT|TAG|TD|TEXTAREA|TH|TITLE|TT|T|UL|XHTML|IS_SLUG|IS_STRONG|IS_LOWER|IS_UPPER|IS_ALPHANUMERIC|IS_DATETIME|IS_DATETIME_IN_RANGE|IS_DATE|IS_DATE_IN_RANGE|IS_DECIMAL_IN_RANGE|IS_EMAIL|IS_EXPR|IS_FLOAT_IN_RANGE|IS_IMAGE|IS_INT_IN_RANGE|IS_IN_SET|IS_IPV4|IS_LIST_OF|IS_LENGTH|IS_MATCH|IS_EQUAL_TO|IS_EMPTY_OR|IS_NULL_OR|IS_NOT_EMPTY|IS_TIME|IS_UPLOAD_FILENAME|IS_URL|CLEANUP|CRYPT|IS_IN_DB|IS_NOT_IN_DB|DAL|Field|SQLFORM|SQLTABLE|xmlescape|embed64)(?![a-zA-Z0-9_])' - ), 'link:%(link)s;text-decoration:None;color:#FF5C1F;'), - ('MAGIC', re.compile(r'self|None'), - 'color:#185369; font-weight: bold'), - ('MULTILINESTRING', re.compile(r'r?u?(\'\'\'|""")'), - 'color: #FF9966'), - ('STRING', re.compile(r'r?u?\'(.*?)(?]*-->|'), - 'color: green; font-style: italic'), - ('XMLCRAP', re.compile(r']*>'), - 'color: blue; font-style: italic'), - ('SCRIPT', re.compile(r'', re.IGNORECASE + + re.DOTALL), 'color: black'), + ('TAG', re.compile(r''), + 'color: darkred; font-weight: bold'), + )), + } + + def highlight( code, language, @@ -286,13 +307,13 @@ color: #A0A0A0; 'WEB2PY']: code = Highlighter(language, link, styles).highlight(code) else: - code = local_html_escape(code, quote=False) + code = xmlescape(code, quote=False) lines = code.split('\n') if counter is None: linenumbers = [''] * len(lines) elif isinstance(counter, str): - linenumbers = [local_html_escape(counter, quote=False)] * len(lines) + linenumbers = [xmlescape(counter, quote=False)] * len(lines) else: linenumbers = [str(i + counter) + '.' for i in xrange(len(lines))] @@ -325,7 +346,7 @@ color: #A0A0A0; fa = ' '.join([key[1:].lower() for (key, value) in items if key[:1] == '_' and value is None] + ['%s="%s"' % (key[1:].lower(), str(value).replace('"', "'")) - for (key, value) in attributes.items() if key[:1] + for (key, value) in items if key[:1] == '_' and value]) if fa: fa = ' ' + fa diff --git a/gluon/html.py b/gluon/html.py index 4adf287c..6642cfc5 100644 --- a/gluon/html.py +++ b/gluon/html.py @@ -9,7 +9,6 @@ Template helpers -------------------------------------------- """ -from __future__ import print_function import cgi import os @@ -18,25 +17,50 @@ import copy import types import urllib import base64 -from gluon import sanitizer, decoder import itertools -from gluon._compat import reduce, pickle, copyreg, HTMLParser, name2codepoint, iteritems, unichr, unicodeT, \ +from pydal._compat import PY2, reduce, pickle, copyreg, HTMLParser, name2codepoint, iteritems, unichr, unicodeT, \ urllib_quote, to_bytes, to_native, to_unicode, basestring, urlencode, implements_bool, text_type, long -from gluon.utils import local_html_escape +from yatl import sanitizer import marshal +from gluon import decoder from gluon.storage import Storage -from gluon.utils import web2py_uuid, simple_hash, compare +from gluon.utils import web2py_uuid, compare from gluon.highlight import highlight +from gluon.validators import simple_hash +def local_html_escape(data, quote=False): + """ + Works with bytes. + Replace special characters "&", "<" and ">" to HTML-safe sequences. + If the optional flag quote is true (the default), the quotation mark + characters, both double quote (") and single quote (') characters are also + translated. + """ + if PY2: + import cgi + data = cgi.escape(data, quote) + return data.replace("'", "'") if quote else data + else: + import html + if isinstance(data, str): + return html.escape(data, quote=quote) + data = data.replace(b"&", b"&") # Must be done first! + data = data.replace(b"<", b"<") + data = data.replace(b">", b">") + if quote: + data = data.replace(b'"', b""") + data = data.replace(b'\'', b"'") + return data + regex_crlf = re.compile('\r|\n') join = ''.join # name2codepoint is incomplete respect to xhtml (and xml): 'apos' is missing. -entitydefs = dict(map(lambda k_v: (k_v[0], unichr(k_v[1]).encode('utf-8')), iteritems(name2codepoint))) -entitydefs.setdefault('apos', u"'".encode('utf-8')) +entitydefs = dict([(k_v[0], to_bytes(unichr(k_v[1]))) for k_v in iteritems(name2codepoint)]) +entitydefs.setdefault('apos', to_bytes("'")) __all__ = [ diff --git a/gluon/languages.py b/gluon/languages.py index 07dbbac9..9ed60f9c 100644 --- a/gluon/languages.py +++ b/gluon/languages.py @@ -19,12 +19,11 @@ import logging from cgi import escape from threading import RLock -from gluon.utils import local_html_escape - -from gluon._compat import copyreg, PY2, maketrans, iterkeys, unicodeT, to_unicode, to_bytes, iteritems, to_native, pjoin - +from pydal._compat import copyreg, PY2, maketrans, iterkeys, unicodeT, to_unicode, to_bytes, iteritems, to_native, pjoin from pydal.contrib.portalocker import read_locked, LockedFile +from yatl.sanitizer import xmlescape + from gluon.fileutils import listdir from gluon.cfs import getcfs from gluon.html import XML, xmlescape @@ -311,7 +310,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 +324,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 to_unicode(x, 'utf-8').lower() def write_dict(filename, contents): @@ -393,6 +392,12 @@ class lazyT(object): def __eq__(self, other): return str(self) == str(other) + def __lt__(self, other): + return str(self) < str(other) + + def __gt__(self, other): + return str(self) > str(other) + def __ne__(self, other): return str(self) != str(other) @@ -428,7 +433,7 @@ class lazyT(object): return len(str(self)) def xml(self): - return str(self) if self.M else local_html_escape(str(self), quote=False) + return str(self) if self.M else xmlescape(str(self), quote=False) def encode(self, *a, **b): if PY2 and a[0] != 'utf8': @@ -452,12 +457,12 @@ class lazyT(object): def pickle_lazyT(c): - return str, (c.xml(),) + return str, (to_native(c.xml()),) copyreg.pickle(lazyT, pickle_lazyT) -class translator(object): +class TranslatorFactory(object): """ This class is instantiated by gluon.compileapp.build_environment as the T object @@ -742,8 +747,8 @@ class translator(object): try: otherT = self.otherTs[index] except KeyError: - otherT = self.otherTs[index] = translator(self.langpath, - self.http_accept_language) + otherT = self.otherTs[index] = TranslatorFactory(self.langpath, + self.http_accept_language) if language: otherT.force(language) return otherT @@ -936,8 +941,8 @@ class translator(object): 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) diff --git a/gluon/main.py b/gluon/main.py index 9867c87b..8f5fac1b 100644 --- a/gluon/main.py +++ b/gluon/main.py @@ -1,4 +1,3 @@ -#!/bin/env python # -*- coding: utf-8 -*- """ @@ -9,10 +8,9 @@ The gluon wsgi application --------------------------- """ -from __future__ import print_function if False: - import import_all # DO NOT REMOVE PART OF FREEZE PROCESS + from . import import_all # DO NOT REMOVE PART OF FREEZE PROCESS import gc import os @@ -26,13 +24,13 @@ import socket import random import string -from gluon._compat import Cookie, urllib2 +from gluon._compat import Cookie, urllib_quote # from thread import allocate_lock -from gluon.fileutils import abspath, write_file +from gluon.fileutils import abspath, read_file, write_file, create_missing_folders, create_missing_app_folders, \ + add_path_first from gluon.settings import global_settings -from gluon.utils import web2py_uuid -from gluon.admin import add_path_first, create_missing_folders, create_missing_app_folders +from gluon.utils import web2py_uuid, unlocalised_http_header_date from gluon.globals import current # Remarks: @@ -56,7 +54,6 @@ web2py_path = global_settings.applications_parent # backward compatibility create_missing_folders() # set up logging for subsequent imports -import logging import logging.config # This needed to prevent exception on Python 2.5: @@ -100,13 +97,9 @@ requests = 0 # gc timer # Security Checks: validate URL and session_id here, # accept_language is validated in languages -# pattern used to validate client address -regex_client = re.compile('[\w\-:]+(\.[\w\-]+)*\.?') # ## to account for IPV6 - try: - version_info = open(pjoin(global_settings.gluon_parent, 'VERSION'), 'r') - raw_version_string = version_info.read().split()[-1].strip() - version_info.close() + version_info = read_file(pjoin(global_settings.gluon_parent, 'VERSION')) + raw_version_string = version_info.split()[-1].strip() global_settings.web2py_version = raw_version_string web2py_version = global_settings.web2py_version except: @@ -123,6 +116,9 @@ load_routes() HTTPS_SCHEMES = set(('https', 'HTTPS')) +# pattern used to match client IP address +REGEX_CLIENT = re.compile(r'[\w:]+(\.\w+)*') + def get_client(env): """ Guesses the client address from the environment variables @@ -131,12 +127,12 @@ def get_client(env): if all fails, assume '127.0.0.1' or '::1' (running locally) """ eget = env.get - g = regex_client.search(eget('http_x_forwarded_for', '')) - client = (g.group() or '').split(',')[0] if g else None + m = REGEX_CLIENT.search(eget('http_x_forwarded_for', '')) + client = m and m.group() if client in (None, '', 'unknown'): - g = regex_client.search(eget('remote_addr', '')) - if g: - client = g.group() + m = REGEX_CLIENT.search(eget('remote_addr', '')) + if m: + client = m.group() elif env.http_host.startswith('['): # IPv6 client = '::1' else: @@ -199,8 +195,7 @@ def serve_controller(request, response, session): ('Content-Type', contenttype('.' + request.extension)), ('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'), - ('Expires', time.strftime('%a, %d %b %Y %H:%M:%S GMT', - time.gmtime())), + ('Expires', unlocalised_http_header_date(time.gmtime())), ('Pragma', 'no-cache')] for key, value in default_headers: response.headers.setdefault(key, value) @@ -250,7 +245,7 @@ class LazyWSGI(object): def app(environ, start_response): data = f() start_response(self.response.status, - self.response.headers.items()) + list(self.response.headers.items())) if isinstance(data, list): return data return [data] @@ -355,11 +350,10 @@ def wsgibase(environ, responder): local_hosts = global_settings.local_hosts client = get_client(env) x_req_with = str(env.http_x_requested_with).lower() - cmd_opts = global_settings.cmd_options request.update( client=client, - folder=abspath('applications', app) + os.sep, + folder=abspath('applications', app), ajax=x_req_with == 'xmlhttprequest', cid=env.http_web2py_component_element, is_local=(env.remote_addr in local_hosts and client == env.remote_addr), @@ -487,10 +481,10 @@ def wsgibase(environ, responder): if request.ajax: if response.flash: http_response.headers['web2py-component-flash'] = \ - urllib2.quote(xmlescape(response.flash).replace(b'\n', b'')) + urllib_quote(xmlescape(response.flash).replace(b'\n', b'')) if response.js: http_response.headers['web2py-component-command'] = \ - urllib2.quote(response.js.replace('\n', '')) + urllib_quote(response.js.replace('\n', '')) # ################################################## # store cookies in headers @@ -560,8 +554,12 @@ def wsgibase(environ, responder): http_response, request, environ, ticket) if not http_response: return wsgibase(new_environ, responder) + if global_settings.web2py_crontype == 'soft': - newcron.softcron(global_settings.applications_parent).start() + cmd_opts = global_settings.cmd_options + newcron.softcron(global_settings.applications_parent, + apps=cmd_opts and cmd_opts.crontabs) + return http_response.to(responder, env=env) @@ -714,13 +712,12 @@ class HttpServer(object): if interfaces: # if interfaces is specified, it must be tested for rocket parameter correctness # not necessarily completely tested (e.g. content of tuples or ip-format) - import types if isinstance(interfaces, list): for i in interfaces: if not isinstance(i, tuple): - raise "Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/" + raise AttributeError("Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/") else: - raise "Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/" + raise AttributeError("Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/") if path: # if a path is specified change the global variables so that web2py @@ -767,8 +764,8 @@ class HttpServer(object): app_info=app_info, min_threads=min_threads, max_threads=max_threads, - queue_size=int(request_queue_size), - timeout=int(timeout), + queue_size=request_queue_size, + timeout=timeout, handle_signals=False, ) @@ -788,7 +785,11 @@ class HttpServer(object): """ stop cron and the web server """ - newcron.stopcron() + if global_settings.web2py_crontype == 'soft': + try: + newcron.stopcron() + except: + pass self.server.stop(stoplogging) try: os.unlink(self.pid_filename) diff --git a/gluon/messageboxhandler.py b/gluon/messageboxhandler.py index 50aa68cd..70079cdf 100644 --- a/gluon/messageboxhandler.py +++ b/gluon/messageboxhandler.py @@ -1,10 +1,14 @@ import logging import os +import sys try: - import Tkinter -except: - Tkinter = None + if sys.version_info[0] == 2: + import Tkinter as tkinter + else: + import tkinter +except ImportError: + tkinter = None class MessageBoxHandler(logging.Handler): @@ -12,15 +16,15 @@ class MessageBoxHandler(logging.Handler): logging.Handler.__init__(self) def emit(self, record): - if Tkinter: + if tkinter: msg = self.format(record) - root = Tkinter.Tk() + root = tkinter.Tk() root.wm_title("web2py logger message") - text = Tkinter.Text() + text = tkinter.Text() text["height"] = 12 text.insert(0.1, msg) text.pack() - button = Tkinter.Button(root, text="OK", command=root.destroy) + button = tkinter.Button(root, text="OK", command=root.destroy) button.pack() root.mainloop() @@ -30,6 +34,6 @@ class NotifySendHandler(logging.Handler): logging.Handler.__init__(self) def emit(self, record): - if Tkinter: + if tkinter: msg = self.format(record) os.system("notify-send '%s'" % msg) diff --git a/gluon/myregex.py b/gluon/myregex.py deleted file mode 100644 index 5c3a7c99..00000000 --- a/gluon/myregex.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -| This file is part of the web2py Web Framework -| Copyrighted by Massimo Di Pierro -| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) - -Useful regexes ---------------- -""" - -import re - -# pattern to find defined tables - -regex_tables = re.compile( - """^[\w]+\.define_table\(\s*[\'\"](?P\w+)[\'\"]""", - flags=re.M) - -# pattern to find exposed functions in controller - -regex_expose = re.compile( - '^def\s+(?P_?[a-zA-Z0-9]\w*)\( *\)\s*:', - flags=re.M) - -regex_longcomments = re.compile('(""".*?"""|'+"'''.*?''')", re.DOTALL) - -regex_include = re.compile( - '(?P\{\{\s*include\s+[\'"](?P[^\'"]*)[\'"]\s*\}\})') - -regex_extend = re.compile( - '^\s*(?P\{\{\s*extend\s+[\'"](?P[^\'"]+)[\'"]\s*\}\})', re.MULTILINE) diff --git a/gluon/newcron.py b/gluon/newcron.py index d53ecbe5..42bfb68f 100644 --- a/gluon/newcron.py +++ b/gluon/newcron.py @@ -1,43 +1,52 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- +# vim: set ts=4 sw=4 et ai: """ | This file is part of the web2py Web Framework | Created by Attila Csipa | Modified by Massimo Di Pierro +| Worker, SoftWorker and SimplePool added by Paolo Pastori | License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) Cron-style interface """ -import sys -import os import threading -import logging +import os +from logging import getLogger import time import sched +import sys import re -import datetime -import platform from functools import reduce -try: - import cPickle as pickle -except: - import pickle -from gluon.settings import global_settings +import datetime +import shlex + from gluon import fileutils -from gluon._compat import to_bytes +from gluon._compat import to_bytes, pickle from pydal.contrib import portalocker -logger = logging.getLogger("web2py.cron") -_cron_stopping = False -_cron_subprocs = [] +logger_name = 'web2py.cron' + + +_stopping = False + +def reset(): + global _stopping + _stopping = False + + +_subprocs_lock = threading.RLock() +_subprocs = [] + +def subprocess_count(): + with _subprocs_lock: + return len(_subprocs) def absolute_path_link(path): """ Returns an absolute path for the destination of a symlink - """ if os.path.islink(path): link = os.readlink(path) @@ -50,66 +59,56 @@ def absolute_path_link(path): def stopcron(): """Graceful shutdown of cron""" - global _cron_stopping - _cron_stopping = True - while _cron_subprocs: - proc = _cron_subprocs.pop() + global _stopping + _stopping = True + while subprocess_count(): + with _subprocs_lock: + proc = _subprocs.pop() if proc.poll() is None: try: proc.terminate() - except: - import traceback - traceback.print_exc() + except Exception: + getLogger(logger_name).exception('error in stopcron') -class extcron(threading.Thread): - - def __init__(self, applications_parent, apps=None): - threading.Thread.__init__(self) - self.setDaemon(False) - self.path = applications_parent - self.apps = apps - # crondance(self.path, 'external', startup=True, apps=self.apps) - - def run(self): - if not _cron_stopping: - logger.debug('external cron invocation') - crondance(self.path, 'external', startup=False, apps=self.apps) +def extcron(applications_parent, apps=None): + getLogger(logger_name).debug('external cron invocation') + crondance(applications_parent, 'external', startup=False, apps=apps) class hardcron(threading.Thread): - def __init__(self, applications_parent): + def __init__(self, applications_parent, apps=None): threading.Thread.__init__(self) self.setDaemon(True) self.path = applications_parent - crondance(self.path, 'hard', startup=True) + self.apps = apps + # processing of '@reboot' entries in crontab (startup=True) + getLogger(logger_name).info('hard cron bootstrap') + crondance(self.path, 'hard', startup=True, apps=self.apps) def launch(self): - if not _cron_stopping: - logger.debug('hard cron invocation') - crondance(self.path, 'hard', startup=False) + if not _stopping: + self.logger.debug('hard cron invocation') + crondance(self.path, 'hard', startup=False, apps=self.apps) def run(self): + self.logger = getLogger(logger_name) + self.logger.info('hard cron daemon started') s = sched.scheduler(time.time, time.sleep) - logger.info('Hard cron daemon started') - while not _cron_stopping: + while not _stopping: now = time.time() s.enter(60 - now % 60, 1, self.launch, ()) s.run() -class softcron(threading.Thread): - - def __init__(self, applications_parent): - threading.Thread.__init__(self) - self.path = applications_parent - # crondance(self.path, 'soft', startup=True) - - def run(self): - if not _cron_stopping: - logger.debug('soft cron invocation') - crondance(self.path, 'soft', startup=False) +def softcron(applications_parent, apps=None): + logger = getLogger(logger_name) + try: + if not _dancer((applications_parent, apps)): + logger.warning('no thread available for soft crondance') + except Exception: + logger.exception('error executing soft crondance') class Token(object): @@ -120,6 +119,7 @@ class Token(object): fileutils.write_file(self.path, to_bytes(''), 'wb') self.master = None self.now = time.time() + self.logger = getLogger(logger_name) def acquire(self, startup=False): """ @@ -131,29 +131,30 @@ class Token(object): stop == 0 if job started but did not yet complete if a cron job started within less than 60 seconds, acquire returns None if a cron job started before 60 seconds and did not stop, - a warning is issue "Stale cron.master detected" + a warning is issued ("Stale cron.master detected") """ if sys.platform == 'win32': locktime = 59.5 else: locktime = 59.99 if portalocker.LOCK_EX is None: - logger.warning('WEB2PY CRON: Disabled because no file locking') + self.logger.warning('cron disabled because no file locking') return None self.master = fileutils.open_file(self.path, 'rb+') + ret = None try: - ret = None portalocker.lock(self.master, portalocker.LOCK_EX) try: (start, stop) = pickle.load(self.master) except: - (start, stop) = (0, 1) + start = 0 + stop = 1 if startup or self.now - start > locktime: ret = self.now if not stop: # this happens if previous cron job longer than 1 minute - logger.warning('WEB2PY CRON: Stale cron.master detected') - logger.debug('WEB2PY CRON: Acquiring lock') + self.logger.warning('stale cron.master detected') + self.logger.debug('acquiring lock') self.master.seek(0) pickle.dump((self.now, 0), self.master) self.master.flush() @@ -171,7 +172,7 @@ class Token(object): ret = self.master.closed if not self.master.closed: portalocker.lock(self.master, portalocker.LOCK_EX) - logger.debug('WEB2PY CRON: Releasing cron lock') + self.logger.debug('releasing cron lock') self.master.seek(0) (start, stop) = pickle.load(self.master) if start == self.now: # if this is my lock @@ -195,8 +196,7 @@ def rangetolist(s, period='min'): s = s.replace('*', '1-12', 1) elif period == 'dow': s = s.replace('*', '0-6', 1) - m = re.compile(r'(\d+)-(\d+)/(\d+)') - match = m.match(s) + match = re.match(r'(\d+)-(\d+)/(\d+)', s) if match: for i in range(int(match.group(1)), int(match.group(2)) + 1): if i % int(match.group(3)) == 0: @@ -245,142 +245,247 @@ def parsecronline(line): return task -class cronlauncher(threading.Thread): +class Worker(threading.Thread): - def __init__(self, cmd, shell=True): + def __init__(self, pool): threading.Thread.__init__(self) - if platform.system() == 'Windows': - shell = False - self.cmd = cmd - self.shell = shell + self.setDaemon(True) + self.pool = pool + self.run_lock = threading.Lock() + self.run_lock.acquire() + self.payload = None def run(self): - import subprocess - global _cron_subprocs - if isinstance(self.cmd, (list, tuple)): - cmd = self.cmd - else: - cmd = self.cmd.split() - proc = subprocess.Popen(cmd, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - shell=self.shell) - _cron_subprocs.append(proc) - (stdoutdata, stderrdata) = proc.communicate() - try: - _cron_subprocs.remove(proc) - except ValueError: - pass - if proc.returncode != 0: - logger.warning( - 'WEB2PY CRON Call returned code %s:\n%s' % - (proc.returncode, stdoutdata + stderrdata)) - else: - logger.debug('WEB2PY CRON Call returned success:\n%s' - % stdoutdata) + logger = getLogger(logger_name) + logger.info('Worker %s: started', self.name) + while True: + try: + with self.run_lock: # waiting for run_lock.release() + cmd = ' '.join(self.payload) + logger.debug('Worker %s: now calling %r', self.name, cmd) + import subprocess + proc = subprocess.Popen(self.payload, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + with _subprocs_lock: + _subprocs.append(proc) + stdoutdata, stderrdata = proc.communicate() + try: + with _subprocs_lock: + _subprocs.remove(proc) + except ValueError: + pass + if proc.returncode != 0: + logger.warning('Worker %s: %r call returned code %s:\n%s\n%s', + self.name, cmd, proc.returncode, stdoutdata, stderrdata) + else: + logger.debug('Worker %s: %r call returned success:\n%s', + self.name, cmd, stdoutdata) + finally: + self.run_lock.acquire() + self.pool.stop(self) -def crondance(applications_parent, ctype='soft', startup=False, apps=None): +class SoftWorker(threading.Thread): + + def __init__(self, pool): + threading.Thread.__init__(self) + self.setDaemon(True) + self.pool = pool + self.run_lock = threading.Lock() + self.run_lock.acquire() + self.payload = None + + def run(self): + logger = getLogger(logger_name) + logger.info('SoftWorker %s: started', self.name) + while True: + try: + with self.run_lock: # waiting for run_lock.release() + getLogger(logger_name).debug('soft cron invocation') + applications_parent, apps = self.payload + crondance(applications_parent, 'soft', startup=False, apps=apps) + finally: + self.run_lock.acquire() + self.pool.stop(self) + + +class SimplePool(object): + """ + Very simple thread pool, + (re)uses a maximum number of threads to launch cron tasks. + + Pool size can be incremented after initialization, + this allows delayed configuration of a global instance + for the case you do not want to use lazy initialization. + """ + + def __init__(self, size, worker_cls=Worker): + """ + Create the pool setting initial size. + + Notice that no thread is created until the instance is called. + """ + self.size = size + self.worker_cls = worker_cls + self.lock = threading.RLock() + self.idle = list() + self.running = set() + + def grow(self, size): + if size and size > self.size: + self.size = size + + def start(self, t): + with self.lock: + try: + self.idle.remove(t) + except ValueError: + pass + self.running.add(t) + + def stop(self, t): + with self.lock: + self.idle.append(t) + try: + self.running.remove(t) + except KeyError: + pass + + def __call__(self, payload): + """ + Pass payload to a thread for immediate execution. + + Returns a boolean indicating if a thread is available. + """ + with self.lock: + if len(self.running) == self.size: + # no worker available + return False + idle_num = len(self.idle) + if idle_num: + # use an existing (idle) thread + t = self.idle.pop(0) + else: + # create a new thread + t = self.worker_cls(self) + self.start(t) + t.payload = payload + t.run_lock.release() + if not idle_num: + t.start() + return True + + +_dancer = SimplePool(5, worker_cls=SoftWorker) + +def dancer_size(size): + _dancer.grow(size) + +_launcher = SimplePool(5) + +def launcher_size(size): + _launcher.grow(size) + +def crondance(applications_parent, ctype='hard', startup=False, apps=None): + """ + Does the periodic job of cron service: read the crontab(s) and launch + the various commands. + """ apppath = os.path.join(applications_parent, 'applications') - cron_path = os.path.join(applications_parent) - token = Token(cron_path) + token = Token(applications_parent) cronmaster = token.acquire(startup=startup) if not cronmaster: return - now_s = time.localtime() - checks = (('min', now_s.tm_min), - ('hr', now_s.tm_hour), - ('mon', now_s.tm_mon), - ('dom', now_s.tm_mday), - ('dow', (now_s.tm_wday + 1) % 7)) + try: + now_s = time.localtime() + checks = (('min', now_s.tm_min), + ('hr', now_s.tm_hour), + ('mon', now_s.tm_mon), + ('dom', now_s.tm_mday), + ('dow', (now_s.tm_wday + 1) % 7)) + logger = getLogger(logger_name) - if apps is None: - apps = [x for x in os.listdir(apppath) - if os.path.isdir(os.path.join(apppath, x))] + if not apps: + apps = [x for x in os.listdir(apppath) + if os.path.isdir(os.path.join(apppath, x))] - full_apath_links = set() + full_apath_links = set() - for app in apps: - if _cron_stopping: - break - apath = os.path.join(apppath, app) - - # if app is a symbolic link to other app, skip it - full_apath_link = absolute_path_link(apath) - if full_apath_link in full_apath_links: - continue + if sys.executable.lower().endswith('pythonservice.exe'): + _python_exe = os.path.join(sys.exec_prefix, 'python.exe') else: - full_apath_links.add(full_apath_link) + _python_exe = sys.executable + base_commands = [_python_exe] + w2p_path = fileutils.abspath('web2py.py', gluon=True) + if os.path.exists(w2p_path): + base_commands.append(w2p_path) + base_commands.extend(('--cron_job', '--no_banner', '--no_gui', '--plain')) - cronpath = os.path.join(apath, 'cron') - crontab = os.path.join(cronpath, 'crontab') - if not os.path.exists(crontab): - continue - try: - cronlines = fileutils.readlines_file(crontab, 'rt') - lines = [x.strip() for x in cronlines if x.strip( - ) and not x.strip().startswith('#')] - tasks = [parsecronline(cline) for cline in lines] - except Exception as e: - logger.error('WEB2PY CRON: crontab read error %s' % e) - continue - - for task in tasks: - if _cron_stopping: + for app in apps: + if _stopping: break - if sys.executable.lower().endswith('pythonservice.exe'): - _python_exe = os.path.join(sys.exec_prefix, 'python.exe') - else: - _python_exe = sys.executable - commands = [_python_exe] - w2p_path = fileutils.abspath('web2py.py', gluon=True) - if os.path.exists(w2p_path): - commands.append(w2p_path) - if applications_parent != global_settings.gluon_parent: - commands.extend(('-f', applications_parent)) - citems = [(k in task and not v in task[k]) for k, v in checks] - task_min = task.get('min', []) - if not task: - continue - elif not startup and task_min == [-1]: - continue - elif task_min != [-1] and reduce(lambda a, b: a or b, citems): - continue - logger.info('WEB2PY CRON (%s): %s executing %s in %s at %s' - % (ctype, app, task.get('cmd'), - os.getcwd(), datetime.datetime.now())) - action, command, models = False, task['cmd'], '' - if command.startswith('**'): - (action, models, command) = (True, '', command[2:]) - elif command.startswith('*'): - (action, models, command) = (True, '-M', command[1:]) - else: - action = False + apath = os.path.join(apppath, app) - if action and command.endswith('.py'): - commands.extend(('-J', # cron job - models, # import models? - '-S', app, # app name - '-a', '""', # password - '-R', command)) # command - elif action: - commands.extend(('-J', # cron job - models, # import models? - '-S', app + '/' + command, # app name - '-a', '""')) # password + # if app is a symbolic link to other app, skip it + full_apath_link = absolute_path_link(apath) + if full_apath_link in full_apath_links: + continue else: - commands = command - - # from python docs: - # You do not need shell=True to run a batch file or - # console-based executable. - shell = False + full_apath_links.add(full_apath_link) + cronpath = os.path.join(apath, 'cron') + crontab = os.path.join(cronpath, 'crontab') + if not os.path.exists(crontab): + continue try: - cronlauncher(commands, shell=shell).start() + cronlines = [line.strip() for line in fileutils.readlines_file(crontab, 'rt')] + lines = [line for line in cronlines if line and not line.startswith('#')] + tasks = [parsecronline(cline) for cline in lines] except Exception as e: - logger.warning( - 'WEB2PY CRON: Execution error for %s: %s' - % (task.get('cmd'), e)) - token.release() + logger.error('crontab read error %s', e) + continue + + for task in tasks: + if _stopping: + break + if not task: + continue + task_min = task.get('min', []) + if not startup and task_min == [-1]: + continue + citems = [(k in task and not v in task[k]) for k, v in checks] + if task_min != [-1] and reduce(lambda a, b: a or b, citems): + continue + + logger.info('%s cron: %s executing %r in %s at %s', + ctype, app, task.get('cmd'), + os.getcwd(), datetime.datetime.now()) + action = models = False + command = task['cmd'] + if command.startswith('**'): + action = True + command = command[2:] + elif command.startswith('*'): + action = models = True + command = command[1:] + + if action: + commands = base_commands[:] + if command.endswith('.py'): + commands.extend(('-S', app, '-R', command)) + else: + commands.extend(('-S', app + '/' + command)) + if models: + commands.append('-M') + else: + commands = shlex.split(command) + + try: + if not _launcher(commands): + logger.warning('no thread available, cannot execute %r', task['cmd']) + except Exception: + logger.exception('error executing %r', task['cmd']) + finally: + token.release() diff --git a/gluon/packages/dal b/gluon/packages/dal index 70929a6d..54191338 160000 --- a/gluon/packages/dal +++ b/gluon/packages/dal @@ -1 +1 @@ -Subproject commit 70929a6dc03e6296c34944d2d232f257b78337d7 +Subproject commit 541913385cb731f2802b3f1b6f744093bb3abdf3 diff --git a/gluon/packages/yatl b/gluon/packages/yatl new file mode 160000 index 00000000..2eb050b8 --- /dev/null +++ b/gluon/packages/yatl @@ -0,0 +1 @@ +Subproject commit 2eb050b8e251813e905ecbabba839ca578901a3a diff --git a/gluon/recfile.py b/gluon/recfile.py old mode 100755 new mode 100644 diff --git a/gluon/restricted.py b/gluon/restricted.py index 115470e3..a06312fd 100644 --- a/gluon/restricted.py +++ b/gluon/restricted.py @@ -167,7 +167,7 @@ class RestrictedError(Exception): ticket_storage = TicketStorage(db=request.tickets_db) ticket_storage.store(request, request.uuid.split('/', 1)[1], d) cmd_opts = global_settings.cmd_options - if cmd_opts and cmd_opts.print_errors: + if cmd_opts and cmd_opts.errors_to_console: logger.error(self.traceback) return request.uuid except: diff --git a/gluon/rewrite.py b/gluon/rewrite.py index 31e1bd16..48c1535b 100644 --- a/gluon/rewrite.py +++ b/gluon/rewrite.py @@ -15,7 +15,6 @@ routes.py supports two styles of URL rewriting, depending on whether 'routers' i Refer to router.example.py and routes.example.py for additional documentation. """ -from __future__ import print_function import os import re @@ -205,7 +204,7 @@ def url_out(request, environ, application, controller, function, if host is True or (host is None and (scheme or port is not None)): host = request.env.http_host if not scheme or scheme is True: - scheme = request.env.get('wsgi_url_scheme', 'http').lower() if request else 'http' + scheme = 'https' if request and request.is_https else 'http' if host: host_port = host if not port else host.split(':', 1)[0] + ':%s' % port url = '%s://%s%s' % (scheme, host_port, url) diff --git a/gluon/rocket.py b/gluon/rocket.py index d76da94e..69192347 100644 --- a/gluon/rocket.py +++ b/gluon/rocket.py @@ -5,14 +5,14 @@ # Modified by Massimo Di Pierro # Import System Modules -from __future__ import print_function + import sys import errno import socket import logging import platform -from gluon._compat import iteritems, to_bytes, StringIO -from gluon._compat import urllib_unquote, to_native +from gluon._compat import iteritems, to_bytes, to_unicode, StringIO +from gluon._compat import urllib_unquote, to_native, PY2 # Define Constants VERSION = '1.2.6' @@ -32,7 +32,7 @@ DEFAULTS = dict(LISTEN_QUEUE_SIZE=DEFAULT_LISTEN_QUEUE_SIZE, MIN_THREADS=DEFAULT_MIN_THREADS, MAX_THREADS=DEFAULT_MAX_THREADS) -PY3K = sys.version_info[0] > 2 +PY3K = not PY2 class NullHandler(logging.Handler): @@ -40,39 +40,8 @@ class NullHandler(logging.Handler): def emit(self, record): pass -if PY3K: - def b(val): - """ Convert string/unicode/bytes literals into bytes. This allows for - the same code to run on Python 2.x and 3.x. """ - if isinstance(val, str): - return val.encode() - else: - return val - - def u(val, encoding="us-ascii"): - """ Convert bytes into string/unicode. This allows for the - same code to run on Python 2.x and 3.x. """ - if isinstance(val, bytes): - return val.decode(encoding) - else: - return val - -else: - def b(val): - """ Convert string/unicode/bytes literals into bytes. This allows for - the same code to run on Python 2.x and 3.x. """ - if isinstance(val, unicode): - return val.encode() - else: - return val - - def u(val, encoding="us-ascii"): - """ Convert bytes into string/unicode. This allows for the - same code to run on Python 2.x and 3.x. """ - if isinstance(val, str): - return val.decode(encoding) - else: - return val +b = to_bytes +u = to_unicode # Import Package Modules # package imports removed in monolithic build @@ -613,9 +582,9 @@ import socket import logging import traceback from threading import Lock -try: +if PY3K: from queue import Queue -except ImportError: +else: from Queue import Queue # Import Package Modules diff --git a/gluon/rocket.py.footer b/gluon/rocket.py.footer index 7d76ea4b..91ba75e5 100644 --- a/gluon/rocket.py.footer +++ b/gluon/rocket.py.footer @@ -1,4 +1,3 @@ -from __future__ import print_function # The following code is not part of Rocket but was added to # web2py for testing purposes. diff --git a/gluon/sanitizer.py b/gluon/sanitizer.py index b98b7477..25b36a34 100644 --- a/gluon/sanitizer.py +++ b/gluon/sanitizer.py @@ -1,219 +1 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -| From http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496942 -| Submitter: Josh Goldfoot (other recipes) -| Last Updated: 2006/08/05 -| Version: 1.0 - -Cross-site scripting (XSS) defense ------------------------------------ -""" - -from gluon._compat import HTMLParser, urlparse, entitydefs, basestring -from gluon.utils import local_html_escape -from formatter import AbstractFormatter -from xml.sax.saxutils import quoteattr - -__all__ = ['sanitize'] - - -def xssescape(text): - """Gets rid of < and > and & and, for good measure, :""" - - return local_html_escape(text, quote=True).replace(':', ':') - - -class XssCleaner(HTMLParser): - - def __init__( - self, - permitted_tags=[ - 'a', - 'b', - 'blockquote', - 'br/', - 'i', - 'li', - 'ol', - 'ul', - 'p', - 'cite', - 'code', - 'pre', - 'img/', - ], - allowed_attributes={'a': ['href', 'title'], 'img': ['src', 'alt' - ], 'blockquote': ['type']}, - strip_disallowed=False - ): - - HTMLParser.__init__(self) - self.result = '' - self.open_tags = [] - self.permitted_tags = [i for i in permitted_tags if i[-1] != '/'] - self.requires_no_close = [i[:-1] for i in permitted_tags - if i[-1] == '/'] - self.permitted_tags += self.requires_no_close - self.allowed_attributes = allowed_attributes - - # The only schemes allowed in URLs (for href and src attributes). - # Adding "javascript" or "vbscript" to this list would not be smart. - - self.allowed_schemes = ['http', 'https', 'ftp', 'mailto'] - - #to strip or escape disallowed tags? - self.strip_disallowed = strip_disallowed - # there might be data after final closing tag, that is to be ignored - self.in_disallowed = [False] - - def handle_data(self, data): - if data and not self.in_disallowed[-1]: - self.result += xssescape(data) - - def handle_charref(self, ref): - if self.in_disallowed[-1]: - return - elif len(ref) < 7 and (ref.isdigit() or ref == 'x27'): # x27 is a special case for apostrophe - self.result += '&#%s;' % ref - else: - self.result += xssescape('&#%s' % ref) - - def handle_entityref(self, ref): - if self.in_disallowed[-1]: - return - elif ref in entitydefs: - self.result += '&%s;' % ref - else: - self.result += xssescape('&%s' % ref) - - def handle_comment(self, comment): - if self.in_disallowed[-1]: - return - elif comment: - self.result += xssescape('' % comment) - - def handle_starttag( - self, - tag, - attrs - ): - if tag not in self.permitted_tags: - self.in_disallowed.append(True) - if (not self.strip_disallowed): - self.result += xssescape('<%s>' % tag) - else: - self.in_disallowed.append(False) - bt = '<' + tag - if tag in self.allowed_attributes: - attrs = dict(attrs) - self.allowed_attributes_here = [x for x in - self.allowed_attributes[tag] if x in attrs - and len(attrs[x]) > 0] - for attribute in self.allowed_attributes_here: - if attribute in ['href', 'src', 'background']: - if self.url_is_acceptable(attrs[attribute]): - bt += ' %s="%s"' % (attribute, - attrs[attribute]) - else: - bt += ' %s=%s' % (xssescape(attribute), - quoteattr(attrs[attribute])) - # deal with without href and without src - if bt == '" % tag, "<%s />" % tag) - if not escape: - self.strip_disallowed = True - self.result = '' - self.feed(rawstring) - for endtag in self.open_tags: - if endtag not in self.requires_no_close: - self.result += '' % endtag - return self.result - - def xtags(self): - """ - Returns a printable string informing the user which tags are allowed - """ - - tg = '' - for x in sorted(self.permitted_tags): - tg += '<' + x - if x in self.allowed_attributes: - for y in self.allowed_attributes[x]: - tg += ' %s=""' % y - tg += '> ' - return xssescape(tg.strip()) - - -def sanitize(text, permitted_tags=[ - 'a', - 'b', - 'blockquote', - 'br/', - 'i', - 'li', - 'ol', - 'ul', - 'p', - 'cite', - 'code', - 'pre', - 'img/', - 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', - 'table', 'tbody', 'thead', 'tfoot', 'tr', 'td', 'div', - 'strong', 'span', -], - allowed_attributes={ - 'a': ['href', 'title'], - 'img': ['src', 'alt'], - 'blockquote': ['type'], - 'td': ['colspan'], - }, - escape=True): - if not isinstance(text, basestring): - return str(text) - return XssCleaner(permitted_tags=permitted_tags, - allowed_attributes=allowed_attributes).strip(text, escape) +from yatl.sanitizer import sanitize diff --git a/gluon/scheduler.py b/gluon/scheduler.py index 79d22c0f..4ca449c1 100644 --- a/gluon/scheduler.py +++ b/gluon/scheduler.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# vim: set ts=4 sw=4 et ai: """ | This file is part of the web2py Web Framework | Copyrighted by Massimo Di Pierro @@ -8,46 +9,47 @@ Background processes made simple --------------------------------- """ + from __future__ import print_function -import os -import re -import time -import multiprocessing -import sys -import threading -import traceback -import signal import socket -import datetime +import os import logging -import optparse -import tempfile import types from functools import reduce +import datetime +import re +import sys from json import loads, dumps +import tempfile +import traceback +import threading +import multiprocessing +import time +import signal + from gluon import DAL, Field, IS_NOT_EMPTY, IS_IN_SET, IS_NOT_IN_DB, IS_EMPTY_OR from gluon import IS_INT_IN_RANGE, IS_DATETIME, IS_IN_DB from gluon.utils import web2py_uuid -from gluon._compat import Queue, long, iteritems, PY2 +from gluon._compat import Queue, long, iteritems, PY2, to_bytes, string_types, integer_types from gluon.storage import Storage USAGE = """ ## Example -For any existing app +For any existing application myapp -Create File: app/models/scheduler.py ====== +Create File: myapp/models/scheduler.py ====== from gluon.scheduler import Scheduler -def demo1(*args,**vars): +def demo1(*args, **vars): print('you passed args=%s and vars=%s' % (args, vars)) return 'done!' def demo2(): 1/0 -scheduler = Scheduler(db,dict(demo1=demo1,demo2=demo2)) +scheduler = Scheduler(db, dict(demo1=demo1, demo2=demo2)) ## run worker nodes with: cd web2py @@ -63,21 +65,16 @@ python scheduler.py -h http://127.0.0.1:8000/myapp/appadmin/insert/db/scheduler_task ## monitor scheduled jobs -http://127.0.0.1:8000/myapp/appadmin/select/db?query=db.scheduler_task.id>0 +http://127.0.0.1:8000/myapp/appadmin/select/db?query=db.scheduler_task.id ## view completed jobs -http://127.0.0.1:8000/myapp/appadmin/select/db?query=db.scheduler_run.id>0 +http://127.0.0.1:8000/myapp/appadmin/select/db?query=db.scheduler_run.id ## view workers -http://127.0.0.1:8000/myapp/appadmin/select/db?query=db.scheduler_worker.id>0 +http://127.0.0.1:8000/myapp/appadmin/select/db?query=db.scheduler_worker.id """ -path = os.getcwd() - -if 'WEB2PY_PATH' not in os.environ: - os.environ['WEB2PY_PATH'] = path - IDENTIFIER = "%s#%s" % (socket.gethostname(), os.getpid()) logger = logging.getLogger('web2py.scheduler.%s' % IDENTIFIER) @@ -100,6 +97,7 @@ SECONDS = 1 HEARTBEAT = 3 * SECONDS MAXHIBERNATION = 10 CLEAROUT = '!clear!' +RESULTINFILE = 'result_in_file:' CALLABLETYPES = (types.LambdaType, types.FunctionType, types.BuiltinFunctionType, @@ -167,7 +165,7 @@ class JobGraph(object): if job_name: q = sd.job_name == job_name else: - q = sd.id > 0 + q = sd.id edges = db(q).select() nested_dict = {} @@ -195,7 +193,7 @@ class JobGraph(object): assert not nested_dict, "A cyclic dependency exists amongst %r" % nested_dict db.commit() return rtn - except: + except Exception: db.rollback() return None @@ -209,7 +207,6 @@ class CronParser(object): @staticmethod def _rangetolist(s, period='min'): - retval = [] if s.startswith('*'): if period == 'min': s = s.replace('*', '0-59', 1) @@ -221,21 +218,21 @@ class CronParser(object): s = s.replace('*', '1-12', 1) elif period == 'dow': s = s.replace('*', '0-6', 1) - m = re.compile(r'(\d+)-(\d+)/(\d+)') - match = m.match(s) + match = re.match(r'(\d+)-(\d+)/(\d+)', s) if match: - min_, max_ = int(match.group(1)), int(match.group(2)) + 1 + max_ = int(match.group(2)) + 1 step_ = int(match.group(3)) else: - m = re.compile(r'(\d+)/(\d+)') - ranges_max = {'min': 59, 'hr': 23, 'mon': 12, 'dom': 31, 'dow': 7} - match = m.match(s) + match = re.match(r'(\d+)/(\d+)', s) if match: - min_, max_ = int(match.group(1)), ranges_max[period] + 1 + ranges_max = dict(min=59, hr=23, mon=12, dom=31, dow=7) + max_ = ranges_max[period] + 1 step_ = int(match.group(2)) if match: - for i in range(min_, max_, step_): - retval.append(i) + min_ = int(match.group(1)) + retval = list(range(min_, max_, step_)) + else: + retval = [] return retval @staticmethod @@ -279,9 +276,9 @@ class CronParser(object): 'fri': 5, 'sat': 6} monthsofyear = {'jan': 1, 'feb': 2, 'mar': 3, 'apr': 4, 'may': 5, 'jun': 6, 'jul': 7, 'aug': 8, 'sep': 9, 'oct': 10, - 'nov': 11, 'dec': 12, 'l': 'l'} - for (s, i) in zip(params[:5], ['min', 'hr', 'dom', 'mon', 'dow']): - if s not in [None, '*']: + 'nov': 11, 'dec': 12} + for (s, i) in zip(params, ('min', 'hr', 'dom', 'mon', 'dow')): + if s != '*': task[i] = [] vals = s.split(',') for val in vals: @@ -294,13 +291,13 @@ class CronParser(object): if isnum: val = '%s/1' % val else: - val = '-'.join([str(refdict[v]) + val = '-'.join([str(refdict.get(v, '')) for v in val.split('-')]) - if val != '-1' and '-' in val and '/' not in val: + if '-' in val and '/' not in val: val = '%s/1' % val if '/' in val: task[i] += self._rangetolist(val, i) - elif val.isdigit() or val == '-1': + elif val.isdigit(): task[i].append(int(val)) elif i in ('dow', 'mon'): if val in refdict: @@ -324,7 +321,12 @@ class CronParser(object): @staticmethod def _get_next_dom(sched, task): if task['dom'] == ['l']: - last_feb = 29 if sched.year % 4 == 0 else 28 + # instead of calendar.isleap + try: + last_feb = 29 + datetime.date(sched.year, 2, last_feb) + except ValueError: + last_feb = 28 lastdayofmonth = [ 31, last_feb, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ] @@ -369,7 +371,7 @@ class CronParser(object): sched = self._get_next_mon(sched, task) return sched.replace(hour=0, minute=0) - def get_next(self): + def next(self): """Get next date according to specs.""" if not self.task: self._parse() @@ -405,42 +407,41 @@ class CronParser(object): """Support iteration.""" return self - __next__ = next = get_next + __next__ = next -# the two functions below deal with simplejson decoding as unicode, esp for the dict decode -# and subsequent usage as function Keyword arguments unicode variable names won't work! + +# the two functions below deal with simplejson decoding as unicode, +# esp for the dict decode and subsequent usage as function Keyword arguments +# unicode variable names won't work! # borrowed from http://stackoverflow.com/questions/956867/ - def _decode_list(lst): if not PY2: return lst newlist = [] for i in lst: - if isinstance(i, unicode): - i = i.encode('utf-8') + if isinstance(i, string_types): + i = to_bytes(i) elif isinstance(i, list): i = _decode_list(i) newlist.append(i) return newlist - def _decode_dict(dct): if not PY2: return dct newdict = {} for k, v in iteritems(dct): - if isinstance(k, unicode): - k = k.encode('utf-8') - if isinstance(v, unicode): - v = v.encode('utf-8') + k = to_bytes(k) + if isinstance(v, string_types): + v = to_bytes(v) elif isinstance(v, list): v = _decode_list(v) newdict[k] = v return newdict -def executor(queue, task, out): +def executor(retq, task, outq): """The function used to execute tasks in the background process.""" logger.debug(' task started') @@ -450,50 +451,55 @@ def executor(queue, task, out): def __init__(self, out_queue): self.out_queue = out_queue self.stdout = sys.stdout + self.written = False sys.stdout = self - def __del__(self): + def close(self): sys.stdout = self.stdout + if self.written: + # see "Joining processes that use queues" section in + # https://docs.python.org/2/library/multiprocessing.html#programming-guidelines + # https://docs.python.org/3/library/multiprocessing.html#programming-guidelines + self.out_queue.cancel_join_thread() def flush(self): pass def write(self, data): self.out_queue.put(data) + self.written = True W2P_TASK = Storage({ 'id': task.task_id, 'uuid': task.uuid, 'run_id': task.run_id }) - stdout = LogOutput(out) + stdout = LogOutput(outq) try: if task.app: - os.chdir(os.environ['WEB2PY_PATH']) from gluon.shell import env, parse_path_info from gluon import current - level = logging.getLogger().getEffectiveLevel() - logging.getLogger().setLevel(logging.WARN) - # Get controller-specific subdirectory if task.app is of - # form 'app/controller' + ## FIXME: why temporarily change the log level of the root logger? + #level = logging.getLogger().getEffectiveLevel() + #logging.getLogger().setLevel(logging.WARN) + # support for task.app like 'app/controller' (a, c, f) = parse_path_info(task.app) _env = env(a=a, c=c, import_models=True, extra_request={'is_scheduler': True}) - logging.getLogger().setLevel(level) + #logging.getLogger().setLevel(level) f = task.function functions = current._scheduler.tasks - if not functions: + if functions: + _function = functions.get(f) + else: # look into env _function = _env.get(f) - else: - _function = functions.get(f) if not isinstance(_function, CALLABLETYPES): raise NameError( "name '%s' not found in scheduler's environment" % f) # Inject W2P_TASK into environment _env.update({'W2P_TASK': W2P_TASK}) # Inject W2P_TASK into current - from gluon import current current.W2P_TASK = W2P_TASK globals().update(_env) args = _decode_list(loads(task.args)) @@ -508,162 +514,15 @@ def executor(queue, task, out): fd, temp_path = tempfile.mkstemp(suffix='.w2p_sched') with os.fdopen(fd, 'w') as f: f.write(result) - result = 'w2p_special:%s' % temp_path - queue.put(TaskReport('COMPLETED', result=result)) - except BaseException as e: + result = RESULTINFILE + temp_path + retq.put(TaskReport('COMPLETED', result=result)) + except: tb = traceback.format_exc() - queue.put(TaskReport('FAILED', tb=tb)) - del stdout + retq.put(TaskReport('FAILED', tb=tb)) + finally: + stdout.close() -class MetaScheduler(threading.Thread): - """Base class documenting scheduler's base methods.""" - - def __init__(self): - threading.Thread.__init__(self) - self.process = None # the background process - self.have_heartbeat = True # set to False to kill - self.empty_runs = 0 - - def local_async(self, task): - """Start the background process. - - Args: - task : a `Task` object - - Returns: - tuple: containing:: - - ('ok',result,output) - ('error',exception,None) - ('timeout',None,None) - ('terminated',None,None) - - """ - db = self.db - sr = db.scheduler_run - out = multiprocessing.Queue() - queue = multiprocessing.Queue(maxsize=1) - p = multiprocessing.Process(target=executor, args=(queue, task, out)) - self.process = p - logger.debug(' task starting') - p.start() - - task_output = "" - tout = "" - - try: - if task.sync_output > 0: - run_timeout = task.sync_output - else: - run_timeout = task.timeout - - start = time.time() - - while p.is_alive() and (not task.timeout or time.time() - start < task.timeout): - if tout: - try: - logger.debug(' partial output saved') - db(sr.id == task.run_id).update(run_output=task_output) - db.commit() - except: - pass - p.join(timeout=run_timeout) - tout = "" - while not out.empty(): - tout += out.get() - if tout: - logger.debug(' partial output: "%s"', str(tout)) - if CLEAROUT in tout: - task_output = tout[ - tout.rfind(CLEAROUT) + len(CLEAROUT):] - else: - task_output += tout - except: - p.terminate() - p.join() - logger.debug(' task stopped by general exception') - tr = TaskReport(STOPPED) - else: - if p.is_alive(): - p.terminate() - logger.debug(' task timeout') - try: - # we try to get a traceback here - tr = queue.get(timeout=2) - tr.status = TIMEOUT - tr.output = task_output - except Queue.Empty: - tr = TaskReport(TIMEOUT) - elif queue.empty(): - logger.debug(' task stopped') - tr = TaskReport(STOPPED) - else: - logger.debug(' task completed or failed') - tr = queue.get() - result = tr.result - if result and result.startswith('w2p_special'): - temp_path = result.replace('w2p_special:', '', 1) - with open(temp_path) as f: - tr.result = f.read() - os.unlink(temp_path) - tr.output = task_output - return tr - - def die(self): - """Forces termination of the worker process along with any running - task""" - logger.info('die!') - self.have_heartbeat = False - self.terminate_process() - - def give_up(self): - """Waits for any running task to be executed, then exits the worker - process""" - logger.info('Giving up as soon as possible!') - self.have_heartbeat = False - - def terminate_process(self): - """Terminate any running tasks (internal use only)""" - try: - self.process.terminate() - except: - pass # no process to terminate - - def run(self): - """This is executed by the main thread to send heartbeats""" - counter = 0 - while self.have_heartbeat: - self.send_heartbeat(counter) - counter += 1 - - def start_heartbeats(self): - self.start() - - def send_heartbeat(self, counter): - raise NotImplementedError - - def pop_task(self): - """Fetches a task ready to be executed""" - raise NotImplementedError - - def report_task(self, task, task_report): - """Creates a task report""" - raise NotImplementedError - - def sleep(self): - raise NotImplementedError - - def loop(self): - """Main loop, fetching tasks and starting executor's background - processes""" - raise NotImplementedError - - -TASK_STATUS = (QUEUED, RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED, EXPIRED) -RUN_STATUS = (RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED) -WORKER_STATUS = (ACTIVE, PICK, DISABLED, TERMINATE, KILL, STOP_TASK) - class IS_CRONLINE(object): """ Validates cronline @@ -674,9 +533,9 @@ class IS_CRONLINE(object): def __call__(self, value): recur = CronParser(value, datetime.datetime.now()) try: - recur.get_next() + recur.next() return (value, None) - except (KeyError, ValueError) as e: + except ValueError as e: if not self.error_message: return (value, e) return (value, self.error_message) @@ -707,7 +566,12 @@ class TYPE(object): return (value, current.T('Not of type: %s') % self.myclass) -class Scheduler(MetaScheduler): +TASK_STATUS = (QUEUED, RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED, EXPIRED) +RUN_STATUS = (RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED) +WORKER_STATUS = (ACTIVE, PICK, DISABLED, TERMINATE, KILL, STOP_TASK) + + +class Scheduler(threading.Thread): """Scheduler object Args: @@ -739,7 +603,12 @@ class Scheduler(MetaScheduler): worker_name=None, group_names=None, heartbeat=HEARTBEAT, max_empty_runs=0, discard_results=False, utc_time=False): - MetaScheduler.__init__(self) + threading.Thread.__init__(self) + self.setDaemon(True) + self.process = None # the background process + self.process_queues = (None, None) + self.have_heartbeat = True # set to False to kill + self.empty_runs = 0 self.db = db self.db_thread = None @@ -753,6 +622,7 @@ class Scheduler(MetaScheduler): self.do_assign_tasks = False self.greedy = False self.utc_time = utc_time + self.w_stats_lock = threading.RLock() self.w_stats = Storage( dict( status=RUNNING, @@ -770,6 +640,151 @@ class Scheduler(MetaScheduler): self.define_tables(db, migrate=migrate) + def execute(self, task): + """Start the background process. + + Args: + task : a `Task` object + + Returns: + a `TaskReport` object + """ + outq = multiprocessing.Queue() + retq = multiprocessing.Queue(maxsize=1) + self.process = p = \ + multiprocessing.Process(target=executor, args=(retq, task, outq)) + self.process_queues = (retq, outq) + + logger.debug(' task starting') + p.start() + start = time.time() + + if task.sync_output > 0: + run_timeout = task.sync_output + else: + run_timeout = task.timeout + task_output = tout = '' + try: + while p.is_alive() and (not task.timeout or + time.time() - start < task.timeout): + # NOTE: try always to empty the out queue before + # the child process is joined, + # see "Joining processes that use queues" section in + # https://docs.python.org/2/library/multiprocessing.html#programming-guidelines + # https://docs.python.org/3/library/multiprocessing.html#programming-guidelines + while True: + try: + tout += outq.get(timeout=2) + except Queue.Empty: + break + if tout: + logger.debug(' partial output: "%s"', tout) + if CLEAROUT in tout: + task_output = tout[ + tout.rfind(CLEAROUT) + len(CLEAROUT):] + else: + task_output += tout + try: + db = self.db + db(db.scheduler_run.id == task.run_id).update(run_output=task_output) + db.commit() + tout = '' + logger.debug(' partial output saved') + except Exception: + logger.exception(' error while saving partial output') + task_output = task_output[:-len(tout)] + p.join(timeout=run_timeout) + except: + logger.exception(' task stopped by general exception') + self.terminate_process() + tr = TaskReport(STOPPED) + else: + if p.is_alive(): + logger.debug(' task timeout') + self.terminate_process(flush_ret=False) + try: + # we try to get a traceback here + tr = retq.get(timeout=2) # NOTE: risky after terminate + tr.status = TIMEOUT + tr.output = task_output + except Queue.Empty: + tr = TaskReport(TIMEOUT) + else: + try: + tr = retq.get_nowait() + except Queue.Empty: + logger.debug(' task stopped') + tr = TaskReport(STOPPED) + else: + logger.debug(' task completed or failed') + result = tr.result + if result and result.startswith(RESULTINFILE): + temp_path = result.replace(RESULTINFILE, '', 1) + with open(temp_path) as f: + tr.result = f.read() + os.unlink(temp_path) + tr.output = task_output + return tr + + _terminate_process_lock = threading.RLock() + + def terminate_process(self, flush_out=True, flush_ret=True): + """Terminate any running tasks (internal use only)""" + if self.process is not None: + # must synchronize since we are called by main and heartbeat thread + with self._terminate_process_lock: + if flush_out: + queue = self.process_queues[1] + while not queue.empty(): # NOTE: empty() is not reliable + try: + queue.get_nowait() + except Queue.Empty: + pass + if flush_ret: + queue = self.process_queues[0] + while not queue.empty(): + try: + queue.get_nowait() + except Queue.Empty: + pass + logger.debug('terminating process') + try: + # NOTE: terminate should not be called when using shared + # resources, see "Avoid terminating processes" + # section in + # https://docs.python.org/2/library/multiprocessing.html#programming-guidelines + # https://docs.python.org/3/library/multiprocessing.html#programming-guidelines + self.process.terminate() + # NOTE: calling join after a terminate is risky, + # as explained in "Avoid terminating processes" + # section this can lead to a deadlock + self.process.join() + finally: + self.process = None + + def die(self): + """Forces termination of the worker process along with any running + task""" + logger.info('die!') + self.have_heartbeat = False + self.terminate_process() + + def give_up(self): + """Waits for any running task to be executed, then exits the worker + process""" + logger.info('Giving up as soon as possible!') + self.have_heartbeat = False + + def run(self): + """This is executed by the heartbeat thread""" + counter = 0 + while self.have_heartbeat: + self.send_heartbeat(counter) + counter += 1 + + def start_heartbeats(self): + self.start() + def __get_migrate(self, tablename, migrate=True): if migrate is False: return False @@ -804,6 +819,7 @@ class Scheduler(MetaScheduler): Field('group_name', default='main'), Field('status', requires=IS_IN_SET(TASK_STATUS), default=QUEUED, writable=False), + Field('broadcast', 'boolean', default=False), Field('function_name', requires=IS_IN_SET(sorted(self.tasks.keys())) if self.tasks else DEFAULT), @@ -902,58 +918,49 @@ class Scheduler(MetaScheduler): try: self.start_heartbeats() while self.have_heartbeat: - if self.w_stats.status == DISABLED: + with self.w_stats_lock: + is_disabled = self.w_stats.status == DISABLED + if is_disabled: logger.debug('Someone stopped me, sleeping until better' ' times come (%s)', self.w_stats.sleep) self.sleep() continue logger.debug('looping...') + if self.is_a_ticker and self.do_assign_tasks: + # I'm a ticker, and 5 loops passed without + # reassigning tasks, let's do that + self.wrapped_assign_tasks() task = self.wrapped_pop_task() if task: - self.w_stats.empty_runs = 0 - self.w_stats.status = RUNNING - self.w_stats.total += 1 - self.wrapped_report_task(task, self.local_async(task)) - if not self.w_stats.status == DISABLED: - self.w_stats.status = ACTIVE + with self.w_stats_lock: + self.w_stats.empty_runs = 0 + self.w_stats.status = RUNNING + self.w_stats.total += 1 + self.wrapped_report_task(task, self.execute(task)) + with self.w_stats_lock: + if not self.w_stats.status == DISABLED: + self.w_stats.status = ACTIVE else: - self.w_stats.empty_runs += 1 + with self.w_stats_lock: + self.w_stats.empty_runs += 1 + if self.max_empty_runs != 0: + logger.debug('empty runs %s/%s', + self.w_stats.empty_runs, + self.max_empty_runs) + if self.w_stats.empty_runs >= self.max_empty_runs: + logger.info( + 'empty runs limit reached, killing myself') + self.die() + if self.is_a_ticker and self.greedy: + # there could be other tasks ready to be assigned + logger.info('TICKER: greedy loop') + self.wrapped_assign_tasks() logger.debug('sleeping...') - if self.max_empty_runs != 0: - logger.debug('empty runs %s/%s', - self.w_stats.empty_runs, - self.max_empty_runs) - if self.w_stats.empty_runs >= self.max_empty_runs: - logger.info( - 'empty runs limit reached, killing myself') - self.die() self.sleep() except (KeyboardInterrupt, SystemExit): logger.info('catched') self.die() - def wrapped_assign_tasks(self, db): - """Commodity function to call `assign_tasks` and trap exceptions. - - If an exception is raised, assume it happened because of database - contention and retries `assign_task` after 0.5 seconds - """ - logger.debug('Assigning tasks...') - db.commit() # db.commit() only for Mysql - x = 0 - while x < 10: - try: - self.assign_tasks(db) - db.commit() - logger.debug('Tasks assigned...') - break - except: - self.w_stats.errors += 1 - db.rollback() - logger.error('TICKER: error assigning tasks (%s)', x) - x += 1 - time.sleep(0.5) - def wrapped_pop_task(self): """Commodity function to call `pop_task` and trap exceptions. @@ -961,30 +968,21 @@ class Scheduler(MetaScheduler): contention and retries `pop_task` after 0.5 seconds """ db = self.db - db.commit() # another nifty db.commit() only for Mysql - x = 0 - while x < 10: + db.commit() # for MySQL only; FIXME: Niphlod, still needed? could avoid when not MySQL? + for x in range(10): try: - rtn = self.pop_task(db) - return rtn - break - except: + return self.pop_task() + except Exception: + logger.exception(' error popping tasks') self.w_stats.errors += 1 db.rollback() - logger.error(' error popping tasks') - x += 1 time.sleep(0.5) - def pop_task(self, db): + def pop_task(self): """Grab a task ready to be executed from the queue.""" now = self.now() - st = self.db.scheduler_task - if self.is_a_ticker and self.do_assign_tasks: - # I'm a ticker, and 5 loops passed without reassigning tasks, - # let's do that and loop again - self.wrapped_assign_tasks(db) - return None - # ready to process something + db = self.db + st = db.scheduler_task grabbed = db( (st.assigned_worker_name == self.worker_name) & (st.status == ASSIGNED) @@ -992,22 +990,18 @@ class Scheduler(MetaScheduler): task = grabbed.select(limitby=(0, 1), orderby=st.next_run_time).first() if task: + # none will touch my task! task.update_record(status=RUNNING, last_run_time=now) - # noone will touch my task! db.commit() logger.debug(' work to do %s', task.id) else: - if self.is_a_ticker and self.greedy: - # there are other tasks ready to be assigned - logger.info('TICKER: greedy loop') - self.wrapped_assign_tasks(db) - else: - logger.info('nothing to do') + logger.info('nothing to do') return None - times_run = task.times_run + 1 + if task.cronline: - cron_recur = CronParser(task.cronline, now.replace(second=0)) - next_run_time = cron_recur.get_next() + cron_recur = CronParser(task.cronline, + now.replace(second=0, microsecond=0)) + next_run_time = cron_recur.next() elif not task.prevent_drift: next_run_time = task.last_run_time + datetime.timedelta( seconds=task.period @@ -1017,9 +1011,10 @@ class Scheduler(MetaScheduler): # see #1191 next_run_time = task.start_time secondspassed = (now - next_run_time).total_seconds() - steps = secondspassed // task.period + 1 - next_run_time += datetime.timedelta(seconds=task.period * steps) + times = secondspassed // task.period + 1 + next_run_time += datetime.timedelta(seconds=task.period * times) + times_run = task.times_run + 1 if times_run < task.repeats or task.repeats == 0: # need to run (repeating task) run_again = True @@ -1027,7 +1022,7 @@ class Scheduler(MetaScheduler): # no need to run again run_again = False run_id = 0 - while True and not self.discard_results: + while not self.discard_results: # FIXME: forever? logger.debug(' new scheduler_run record') try: run_id = db.scheduler_run.insert( @@ -1037,9 +1032,10 @@ class Scheduler(MetaScheduler): worker_name=self.worker_name) db.commit() break - except: - time.sleep(0.5) + except Exception: + logger.exception(' error inserting scheduler_run') db.rollback() + time.sleep(0.5) logger.info('new task %(id)s "%(task_name)s"' ' %(application_name)s.%(function_name)s' % task) return Task( @@ -1066,15 +1062,14 @@ class Scheduler(MetaScheduler): contention and retries `pop_task` after 0.5 seconds """ db = self.db - while True: + while True: # FIXME: forever? try: self.report_task(task, task_report) db.commit() break - except: - self.w_stats.errors += 1 + except Exception: + logger.exception(' error storing result') db.rollback() - logger.error(' error storing result') time.sleep(0.5) def report_task(self, task, task_report): @@ -1082,8 +1077,8 @@ class Scheduler(MetaScheduler): Deals with logic for repeating tasks. """ - db = self.db now = self.now() + db = self.db st = db.scheduler_task sr = db.scheduler_run if not self.discard_results: @@ -1104,11 +1099,11 @@ class Scheduler(MetaScheduler): db(sr.id == task.run_id).delete() # if there is a stop_time and the following run would exceed it is_expired = (task.stop_time and - task.next_run_time > task.stop_time and - True or False) + task.next_run_time > task.stop_time or + False) status = (task.run_again and is_expired and EXPIRED or - task.run_again and not is_expired and - QUEUED or COMPLETED) + task.run_again and not is_expired and QUEUED or + COMPLETED) if task_report.status == COMPLETED: d = dict(status=status, next_run_time=task.next_run_time, @@ -1117,7 +1112,7 @@ class Scheduler(MetaScheduler): ) db(st.id == task.task_id).update(**d) if status == COMPLETED: - self.update_dependencies(db, task.task_id) + self.update_dependencies(task.task_id) else: st_mapping = {'FAILED': 'FAILED', 'TIMEOUT': 'TIMEOUT', @@ -1133,16 +1128,18 @@ class Scheduler(MetaScheduler): ) logger.info('task completed (%s)', task_report.status) - def update_dependencies(self, db, task_id): + def update_dependencies(self, task_id): """Unblock execution paths for Jobs.""" + db = self.db db(db.scheduler_task_deps.task_child == task_id).update(can_visit=True) def adj_hibernation(self): """Used to increase the "sleep" interval for DISABLED workers.""" - if self.w_stats.status == DISABLED: - wk_st = self.w_stats.sleep - hibernation = wk_st + HEARTBEAT if wk_st < MAXHIBERNATION else MAXHIBERNATION - self.w_stats.sleep = hibernation + with self.w_stats_lock: + if self.w_stats.status == DISABLED: + wk_st = self.w_stats.sleep + hibernation = wk_st + HEARTBEAT if wk_st < MAXHIBERNATION else MAXHIBERNATION + self.w_stats.sleep = hibernation def send_heartbeat(self, counter): """Coordination among available workers. @@ -1155,58 +1152,68 @@ class Scheduler(MetaScheduler): - does "housecleaning" for dead workers - triggers tasks assignment to workers """ + if self.db_thread: + # BKR 20180612 check if connection still works + try: + self.db_thread(self.db_thread.scheduler_worker).count() + except self.db_thread._adapter.connection.OperationalError: + # if not -> throw away self.db_thread and force reconnect + self.db_thread = None + if not self.db_thread: logger.debug('thread building own DAL object') self.db_thread = DAL( self.db._uri, folder=self.db._adapter.folder, decode_credentials=True) self.define_tables(self.db_thread, migrate=False) + try: - db = self.db_thread - sw, st = db.scheduler_worker, db.scheduler_task now = self.now() + db = self.db_thread + sw = db.scheduler_worker + st = db.scheduler_task # record heartbeat - mybackedstatus = db(sw.worker_name == self.worker_name).select().first() - if not mybackedstatus: - sw.insert(status=ACTIVE, worker_name=self.worker_name, - first_heartbeat=now, last_heartbeat=now, - group_names=self.group_names, - worker_stats=self.w_stats) - self.w_stats.status = ACTIVE - self.w_stats.sleep = self.heartbeat - mybackedstatus = ACTIVE - else: - mybackedstatus = mybackedstatus.status - if mybackedstatus == DISABLED: - # keep sleeping - self.w_stats.status = DISABLED - logger.debug('........recording heartbeat (%s)', - self.w_stats.status) - db(sw.worker_name == self.worker_name).update( - last_heartbeat=now, - worker_stats=self.w_stats) - elif mybackedstatus == TERMINATE: - self.w_stats.status = TERMINATE - logger.debug("Waiting to terminate the current task") - self.give_up() - elif mybackedstatus == KILL: - self.w_stats.status = KILL - self.die() - return + row = db(sw.worker_name == self.worker_name).select().first() + with self.w_stats_lock: + if not row: + sw.insert(status=ACTIVE, worker_name=self.worker_name, + first_heartbeat=now, last_heartbeat=now, + group_names=self.group_names, + worker_stats=self.w_stats) + self.w_stats.status = ACTIVE + self.w_stats.sleep = self.heartbeat + backed_status = ACTIVE else: - if mybackedstatus == STOP_TASK: - logger.info('Asked to kill the current task') - self.terminate_process() - logger.debug('........recording heartbeat (%s)', - self.w_stats.status) - db(sw.worker_name == self.worker_name).update( - last_heartbeat=now, status=ACTIVE, - worker_stats=self.w_stats) - self.w_stats.sleep = self.heartbeat # re-activating the process - if self.w_stats.status != RUNNING: - self.w_stats.status = ACTIVE + backed_status = row.status + if backed_status == DISABLED: + # keep sleeping + self.w_stats.status = DISABLED + logger.debug('........recording heartbeat (DISABLED)') + db(sw.worker_name == self.worker_name).update( + last_heartbeat=now, + worker_stats=self.w_stats) + elif backed_status == TERMINATE: + self.w_stats.status = TERMINATE + logger.debug("Waiting to terminate the current task") + self.give_up() + elif backed_status == KILL: + self.w_stats.status = KILL + self.die() + return + else: + if backed_status == STOP_TASK: + logger.info('Asked to kill the current task') + self.terminate_process() + logger.debug('........recording heartbeat (%s)', + self.w_stats.status) + db(sw.worker_name == self.worker_name).update( + last_heartbeat=now, status=ACTIVE, + worker_stats=self.w_stats) + self.w_stats.sleep = self.heartbeat # re-activating the process + if self.w_stats.status != RUNNING: + self.w_stats.status = ACTIVE self.do_assign_tasks = False - if counter % 5 == 0 or mybackedstatus == PICK: + if counter % 5 == 0 or backed_status == PICK: try: # delete dead workers expiration = now - datetime.timedelta( @@ -1228,14 +1235,16 @@ class Scheduler(MetaScheduler): try: self.is_a_ticker = self.being_a_ticker() except: - logger.error('Error coordinating TICKER') - if self.w_stats.status == ACTIVE: - self.do_assign_tasks = True + logger.exception('Error coordinating TICKER') + with self.w_stats_lock: + if self.w_stats.status == ACTIVE: + self.do_assign_tasks = True except: - logger.error('Error cleaning up') + logger.exception('Error cleaning up') + db.commit() except: - logger.error('Error retrieving status') + logger.exception('Error retrieving status') db.rollback() self.adj_hibernation() self.sleep() @@ -1253,7 +1262,8 @@ class Scheduler(MetaScheduler): (sw.worker_name != my_name) & (sw.status == ACTIVE) ).select(sw.is_ticker, sw.worker_name) ticker = all_active.find(lambda row: row.is_ticker is True).first() - not_busy = self.w_stats.status == ACTIVE + with self.w_stats_lock: + not_busy = self.w_stats.status == ACTIVE if not ticker: # if no other tickers are around if not_busy: @@ -1275,14 +1285,38 @@ class Scheduler(MetaScheduler): "%s is a ticker, I'm a poor worker" % ticker.worker_name) return False - def assign_tasks(self, db): + def wrapped_assign_tasks(self): + """Commodity function to call `assign_tasks` and trap exceptions. + + If an exception is raised, assume it happened because of database + contention and retries `assign_task` after 0.5 seconds + """ + logger.debug('Assigning tasks...') + db = self.db + db.commit() # for MySQL only; FIXME: Niphlod, still needed? could avoid when not MySQL? + for x in range(10): + try: + self.assign_tasks() + db.commit() + logger.debug('Tasks assigned...') + break + except Exception: + logger.exception('TICKER: error assigning tasks') + self.w_stats.errors += 1 + db.rollback() + time.sleep(0.5) + + def assign_tasks(self): """Assign task to workers, that can then pop them from the queue. Deals with group_name(s) logic, in order to assign linearly tasks to available workers for those groups """ - sw, st, sd = db.scheduler_worker, db.scheduler_task, db.scheduler_task_deps now = self.now() + db = self.db + sw = db.scheduler_worker + st = db.scheduler_task + sd = db.scheduler_task_deps all_workers = db(sw.status == ACTIVE).select() # build workers as dict of groups wkgroups = {} @@ -1316,7 +1350,6 @@ class Scheduler(MetaScheduler): (st.status.belongs((QUEUED, ASSIGNED))) & ( (sd.id == None) | (st.id.belongs(deps_with_no_deps)) - ) )._select(st.id, distinct=True, left=sd.on( (st.id == sd.task_parent) & @@ -1348,44 +1381,72 @@ class Scheduler(MetaScheduler): # let's freeze it up db.commit() - x = 0 + tnum = 0 for group in wkgroups.keys(): tasks = all_available(st.group_name == group).select( limitby=(0, limit), orderby=st.next_run_time) # let's break up the queue evenly among workers for task in tasks: - x += 1 + tnum += 1 gname = task.group_name ws = wkgroups.get(gname) if ws: - counter = 0 - myw = 0 - for i, w in enumerate(ws['workers']): - if w['c'] < counter: - myw = i - counter = w['c'] - assigned_wn = wkgroups[gname]['workers'][myw]['name'] - d = dict( - status=ASSIGNED, - assigned_worker_name=assigned_wn - ) - db( - (st.id == task.id) & - (st.status.belongs((QUEUED, ASSIGNED))) - ).update(**d) - wkgroups[gname]['workers'][myw]['c'] += 1 - db.commit() + if task.broadcast: + for worker in ws['workers']: + new_task = db.scheduler_task.insert( + application_name = task.application_name, + task_name = task.task_name, + group_name = task.group_name, + status = ASSIGNED, + broadcast = False, + function_name = task.function_name, + args = task.args, + start_time = now, + repeats = 1, + retry_failed = task.retry_failed, + sync_output = task.sync_output, + assigned_worker_name = worker['name']) + if task.period: + next_run_time = now+datetime.timedelta(seconds=task.period) + else: + # must be cronline + cron_recur = CronParser(task.cronline, + now.replace(second=0, microsecond=0)) + next_run_time = cron_recur.next() + db(st.id == task.id).update(times_run=task.times_run+1, + next_run_time=next_run_time, + last_run_time=now) + db.commit() + else: + counter = 0 + myw = 0 + for i, w in enumerate(ws['workers']): + if w['c'] < counter: + myw = i + counter = w['c'] + assigned_wn = wkgroups[gname]['workers'][myw]['name'] + d = dict( + status=ASSIGNED, + assigned_worker_name=assigned_wn + ) + db( + (st.id == task.id) & + (st.status.belongs((QUEUED, ASSIGNED))) + ).update(**d) + wkgroups[gname]['workers'][myw]['c'] += 1 + db.commit() # I didn't report tasks but I'm working nonetheless!!!! - if x > 0: - self.w_stats.empty_runs = 0 - self.w_stats.queue = x - self.w_stats.distribution = wkgroups - self.w_stats.workers = len(all_workers) + with self.w_stats_lock: + if tnum > 0: + self.w_stats.empty_runs = 0 + self.w_stats.queue = tnum + self.w_stats.distribution = wkgroups + self.w_stats.workers = len(all_workers) # I'll be greedy only if tasks assigned are equal to the limit # (meaning there could be others ready to be assigned) - self.greedy = x >= limit + self.greedy = tnum >= limit logger.info('TICKER: workers are %s', len(all_workers)) - logger.info('TICKER: tasks are %s', x) + logger.info('TICKER: tasks are %s', tnum) def sleep(self): """Calculate the number of seconds to sleep.""" @@ -1395,27 +1456,28 @@ class Scheduler(MetaScheduler): def set_worker_status(self, group_names=None, action=ACTIVE, exclude=None, limit=None, worker_name=None): """Internal function to set worker's status.""" - ws = self.db.scheduler_worker + db = self.db + ws = db.scheduler_worker if not group_names: group_names = self.group_names elif isinstance(group_names, str): group_names = [group_names] if worker_name: - self.db(ws.worker_name == worker_name).update(status=action) + db(ws.worker_name == worker_name).update(status=action) return exclusion = exclude and exclude.append(action) or [action] if not limit: for group in group_names: - self.db( + db( (ws.group_names.contains(group)) & (~ws.status.belongs(exclusion)) ).update(status=action) else: for group in group_names: - workers = self.db((ws.group_names.contains(group)) & - (~ws.status.belongs(exclusion)) - )._select(ws.id, limitby=(0, limit)) - self.db(ws.id.belongs(workers)).update(status=action) + workers = db((ws.group_names.contains(group)) & + (~ws.status.belongs(exclusion)) + )._select(ws.id, limitby=(0, limit)) + db(ws.id.belongs(workers)).update(status=action) def disable(self, group_names=None, limit=None, worker_name=None): """Set DISABLED on the workers processing `group_names` tasks. @@ -1496,18 +1558,19 @@ class Scheduler(MetaScheduler): if cronline: try: start_time = kwargs.get('start_time', self.now) - next_run_time = CronParser(cronline, start_time).get_next() + next_run_time = CronParser(cronline, start_time).next() kwargs.update(start_time=start_time, next_run_time=next_run_time) - except: + except Exception: pass if 'start_time' in kwargs and 'next_run_time' not in kwargs: kwargs.update(next_run_time=kwargs['start_time']) - rtn = self.db.scheduler_task.validate_and_insert(**kwargs) + db = self.db + rtn = db.scheduler_task.validate_and_insert(**kwargs) if not rtn.errors: rtn.uuid = tuuid if immediate: - self.db( - (self.db.scheduler_worker.is_ticker == True) + db( + (db.scheduler_worker.is_ticker == True) ).update(status=PICK) else: rtn.uuid = None @@ -1536,8 +1599,10 @@ class Scheduler(MetaScheduler): """ from pydal.objects import Query - sr, st = self.db.scheduler_run, self.db.scheduler_task - if isinstance(ref, (int, long)): + db = self.db + sr = db.scheduler_run + st = db.scheduler_task + if isinstance(ref, integer_types): q = st.id == ref elif isinstance(ref, str): q = st.uuid == ref @@ -1553,7 +1618,7 @@ class Scheduler(MetaScheduler): fields = st.ALL, sr.ALL left = sr.on(sr.task_id == st.id) orderby = ~st.id | ~sr.id - row = self.db(q).select( + row = db(q).select( *fields, **dict(orderby=orderby, left=left, @@ -1587,24 +1652,26 @@ class Scheduler(MetaScheduler): Note: Experimental """ - st, sw = self.db.scheduler_task, self.db.scheduler_worker - if isinstance(ref, (int, long)): + db = self.db + st = db.scheduler_task + sw = db.scheduler_worker + if isinstance(ref, integer_types): q = st.id == ref elif isinstance(ref, str): q = st.uuid == ref else: raise SyntaxError( "You can retrieve results only by id or uuid") - task = self.db(q).select(st.id, st.status, st.assigned_worker_name) + task = db(q).select(st.id, st.status, st.assigned_worker_name) task = task.first() rtn = None if not task: return rtn if task.status == 'RUNNING': q = sw.worker_name == task.assigned_worker_name - rtn = self.db(q).update(status=STOP_TASK) + rtn = db(q).update(status=STOP_TASK) elif task.status == 'QUEUED': - rtn = self.db(q).update( + rtn = db(q).update( stop_time=self.now(), enabled=False, status=STOPPED) @@ -1620,7 +1687,7 @@ class Scheduler(MetaScheduler): if only_ticker: workers = db(db.scheduler_worker.is_ticker == True).select() else: - workers = db(db.scheduler_worker.id > 0).select() + workers = db(db.scheduler_worker.id).select() all_workers = {} for row in workers: all_workers[row.worker_name] = Storage( @@ -1641,6 +1708,7 @@ def main(): python gluon/scheduler.py """ + import optparse parser = optparse.OptionParser() parser.add_option( "-w", "--worker_name", dest="worker_name", default=None, @@ -1688,7 +1756,7 @@ def main(): sys.path.append(path) print('importing tasks...') tasks = __import__(filename, globals(), locals(), [], -1).tasks - print('tasks found: ' + ', '.join(tasks.keys())) + print('tasks found: ' + ', '.join(list(tasks.keys()))) else: tasks = {} group_names = [x.strip() for x in options.group_names.split(',')] diff --git a/gluon/serializers.py b/gluon/serializers.py index 032cff81..a72bc216 100644 --- a/gluon/serializers.py +++ b/gluon/serializers.py @@ -10,7 +10,7 @@ from gluon.html import TAG, XmlComponent, xmlescape from gluon.languages import lazyT import gluon.contrib.rss2 as rss2 import json as json_parser -from gluon._compat import long, to_native, unicodeT +from gluon._compat import long, to_native, unicodeT, integer_types have_yaml = True try: @@ -79,7 +79,7 @@ def custom_json(o): datetime.datetime, datetime.time)): return o.isoformat()[:19].replace('T', ' ') - elif isinstance(o, (int, long)): + elif isinstance(o, integer_types): return int(o) elif isinstance(o, decimal.Decimal): return str(o) @@ -119,13 +119,43 @@ def xml(value, encoding='UTF-8', key='document', quote=True): return ('' % encoding) + str(xml_rec(value, key, quote)) -def json(value, default=custom_json, indent=None, sort_keys=False): - value = json_parser.dumps(value, default=default, sort_keys=sort_keys, indent=indent) - # replace JavaScript incompatible spacing - # http://timelessrepo.com/json-isnt-a-javascript-subset - # PY3 FIXME - # return value.replace(ur'\u2028', '\\u2028').replace(ur'\2029', '\\u2029') - return value +class JSONEncoderForHTML(json_parser.JSONEncoder): + """An encoder that produces JSON safe to embed in HTML. + To embed JSON content in, say, a script tag on a web page, the + characters &, < and > should be escaped. They cannot be escaped + with the usual entities (e.g. &) because they are not expanded + within ''') self.assertEqual(SCRIPT().xml(), b'') - self.assertEqual(SCRIPT(';').xml() + DIV().xml(), + self.assertEqual(SCRIPT(';').xml() + DIV().xml(), b'
') def test_STYLE(self): diff --git a/gluon/tests/test_is_url.py b/gluon/tests/test_is_url.py deleted file mode 100644 index 51a204d1..00000000 --- a/gluon/tests/test_is_url.py +++ /dev/null @@ -1,692 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Unit tests for IS_URL() -""" - -import unittest - -from gluon.validators import IS_URL, IS_HTTP_URL, IS_GENERIC_URL -from gluon.validators import unicode_to_ascii_authority - - -class TestIsUrl(unittest.TestCase): - - def testModeHttp(self): - - # defaults to mode='http' - - x = IS_URL() - self.assertEqual(x('http://google.ca'), ('http://google.ca', - None)) - self.assertEqual(x('google.ca'), ('http://google.ca', None)) - self.assertEqual(x('google.ca:80'), ('http://google.ca:80', - None)) - self.assertEqual(x('unreal.blargg'), ('unreal.blargg', - 'Enter a valid URL')) - self.assertEqual(x('google..ca'), ('google..ca', 'Enter a valid URL')) - self.assertEqual( - x('google.ca..'), ('google.ca..', 'Enter a valid URL')) - - # explicit use of 'http' mode - - x = IS_URL(mode='http') - self.assertEqual(x('http://google.ca'), ('http://google.ca', - None)) - self.assertEqual(x('google.ca'), ('http://google.ca', None)) - self.assertEqual(x('google.ca:80'), ('http://google.ca:80', - None)) - self.assertEqual(x('unreal.blargg'), ('unreal.blargg', - 'Enter a valid URL')) - - # prepends 'https' instead of 'http' - - x = IS_URL(mode='http', prepend_scheme='https') - self.assertEqual(x('http://google.ca'), ('http://google.ca', - None)) - self.assertEqual(x('google.ca'), ('https://google.ca', None)) - self.assertEqual(x('google.ca:80'), ('https://google.ca:80', - None)) - self.assertEqual(x('unreal.blargg'), ('unreal.blargg', - 'Enter a valid URL')) - - # prepending disabled - - x = IS_URL(prepend_scheme=None) - self.assertEqual(x('http://google.ca'), ('http://google.ca', - None)) - self.assertEqual(x('google.ca'), ('google.ca', None)) - self.assertEqual(x('google.ca:80'), ('google.ca:80', None)) - self.assertEqual(x('unreal.blargg'), ('unreal.blargg', - 'Enter a valid URL')) - - # custom allowed_schemes - - x = IS_URL(mode='http', allowed_schemes=[None, 'http']) - self.assertEqual(x('http://google.ca'), ('http://google.ca', - None)) - self.assertEqual(x('https://google.ca'), ('https://google.ca', - 'Enter a valid URL')) - self.assertEqual(x('google.ca'), ('http://google.ca', None)) - self.assertEqual(x('google.ca:80'), ('http://google.ca:80', - None)) - self.assertEqual(x('unreal.blargg'), ('unreal.blargg', - 'Enter a valid URL')) - - # custom allowed_schemes, excluding None - - x = IS_URL(allowed_schemes=['http']) - self.assertEqual(x('http://google.ca'), ('http://google.ca', - None)) - self.assertEqual(x('https://google.ca'), ('https://google.ca', - 'Enter a valid URL')) - self.assertEqual(x('google.ca'), ('google.ca', 'Enter a valid URL')) - self.assertEqual(x('google.ca:80'), ('google.ca:80', - 'Enter a valid URL')) - self.assertEqual(x('unreal.blargg'), ('unreal.blargg', - 'Enter a valid URL')) - - # custom allowed_schemes and prepend_scheme - - x = IS_URL(allowed_schemes=[None, 'https'], - prepend_scheme='https') - self.assertEqual(x('http://google.ca'), ('http://google.ca', - 'Enter a valid URL')) - self.assertEqual(x('https://google.ca'), ('https://google.ca', - None)) - self.assertEqual(x('google.ca'), ('https://google.ca', None)) - self.assertEqual(x('google.ca:80'), ('https://google.ca:80', - None)) - self.assertEqual(x('unreal.blargg'), ('unreal.blargg', - 'Enter a valid URL')) - - # Now any URL requiring prepending will fail, but prepending is still - # enabled! - - x = IS_URL(allowed_schemes=['http']) - self.assertEqual(x('google.ca'), ('google.ca', 'Enter a valid URL')) - - def testModeGeneric(self): - - # 'generic' mode - - x = IS_URL(mode='generic') - self.assertEqual(x('http://google.ca'), ('http://google.ca', None)) - self.assertEqual(x('google.ca'), ('google.ca', None)) - self.assertEqual(x('google.ca:80'), ('http://google.ca:80', None)) - self.assertEqual(x('blargg://unreal'), ('blargg://unreal', - 'Enter a valid URL')) - - # 'generic' mode with custom allowed_schemes that still includes - # 'http' (the default for prepend_scheme) - - x = IS_URL(mode='generic', allowed_schemes=['http', 'blargg']) - self.assertEqual(x('http://google.ca'), ('http://google.ca', - None)) - self.assertEqual(x('ftp://google.ca'), ('ftp://google.ca', - 'Enter a valid URL')) - self.assertEqual(x('google.ca'), ('google.ca', 'Enter a valid URL')) - self.assertEqual(x('google.ca:80'), ('google.ca:80', - 'Enter a valid URL')) - self.assertEqual(x('blargg://unreal'), ('blargg://unreal', - None)) - - # 'generic' mode with overriden prepend_scheme - - x = IS_URL(mode='generic', prepend_scheme='ftp') - self.assertEqual(x('http://google.ca'), ('http://google.ca', - None)) - self.assertEqual(x('ftp://google.ca'), ('ftp://google.ca', - None)) - self.assertEqual(x('google.ca'), ('google.ca', None)) - self.assertEqual(x('google.ca:80'), ('ftp://google.ca:80', - None)) - self.assertEqual(x('blargg://unreal'), ('blargg://unreal', - 'Enter a valid URL')) - - # 'generic' mode with overriden allowed_schemes and prepend_scheme - - x = IS_URL(mode='generic', allowed_schemes=[None, 'ftp', 'ftps' - ], prepend_scheme='ftp') - self.assertEqual(x('http://google.ca'), ('http://google.ca', - 'Enter a valid URL')) - self.assertEqual(x('google.ca'), ('google.ca', None)) - self.assertEqual(x('ftp://google.ca'), ('ftp://google.ca', - None)) - self.assertEqual(x('google.ca:80'), ('ftp://google.ca:80', - None)) - self.assertEqual(x('blargg://unreal'), ('blargg://unreal', - 'Enter a valid URL')) - - # Now any URL requiring prepending will fail, but prepending is still - # enabled! - - x = IS_URL(mode='generic', allowed_schemes=['http']) - self.assertEqual(x('google.ca'), ('google.ca', 'Enter a valid URL')) - - def testExceptionalUse(self): - - # mode must be in set ['http', 'generic'] - - try: - x = IS_URL(mode='ftp') - x('http://www.google.ca') - except Exception as e: - if str(e) != "invalid mode 'ftp' in IS_URL": - self.fail('Wrong exception: ' + str(e)) - else: - self.fail("Accepted invalid mode: 'ftp'") - - # allowed_schemes in 'http' mode must be in set [None, 'http', 'https'] - - try: - x = IS_URL(allowed_schemes=[None, 'ftp', 'ftps'], - prepend_scheme='ftp') - x('http://www.benn.ca') # we can only reasonably know about the - # error at calling time - except Exception as e: - if str(e)\ - != "allowed_scheme value 'ftp' is not in [None, 'http', 'https']": - self.fail('Wrong exception: ' + str(e)) - else: - self.fail( - "Accepted invalid allowed_schemes: [None, 'ftp', 'ftps']") - - # prepend_scheme's value must be in allowed_schemes (default for 'http' - # mode is [None, 'http', 'https']) - - try: - x = IS_URL(prepend_scheme='ftp') - x('http://www.benn.ca') # we can only reasonably know about the - # error at calling time - except Exception as e: - if str(e)\ - != "prepend_scheme='ftp' is not in allowed_schemes=[None, 'http', 'https']": - self.fail('Wrong exception: ' + str(e)) - else: - self.fail("Accepted invalid prepend_scheme: 'ftp'") - - # custom allowed_schemes that excludes 'http', so prepend_scheme must be - # specified! - - try: - x = IS_URL(allowed_schemes=[None, 'https']) - except Exception as e: - if str(e)\ - != "prepend_scheme='http' is not in allowed_schemes=[None, 'https']": - self.fail('Wrong exception: ' + str(e)) - else: - self.fail("Accepted invalid prepend_scheme: 'http'") - - # prepend_scheme must be in allowed_schemes - - try: - x = IS_URL(allowed_schemes=[None, 'http'], - prepend_scheme='https') - except Exception as e: - if str(e)\ - != "prepend_scheme='https' is not in allowed_schemes=[None, 'http']": - self.fail('Wrong exception: ' + str(e)) - else: - self.fail("Accepted invalid prepend_scheme: 'https'") - - # prepend_scheme's value (default is 'http') must be in allowed_schemes - - try: - x = IS_URL(mode='generic', allowed_schemes=[None, 'ftp', - 'ftps']) - except Exception as e: - if str(e)\ - != "prepend_scheme='http' is not in allowed_schemes=[None, 'ftp', 'ftps']": - self.fail('Wrong exception: ' + str(e)) - else: - self.fail("Accepted invalid prepend_scheme: 'http'") - - # prepend_scheme's value must be in allowed_schemes, which by default - # is all schemes that really exist - - try: - x = IS_URL(mode='generic', prepend_scheme='blargg') - x('http://www.google.ca') - # we can only reasonably know about the error at calling time - except Exception as e: - if not str(e).startswith( - "prepend_scheme='blargg' is not in allowed_schemes="): - self.fail('Wrong exception: ' + str(e)) - else: - self.fail("Accepted invalid prepend_scheme: 'blargg'") - - # prepend_scheme's value must be in allowed_schemes - - try: - x = IS_URL(mode='generic', allowed_schemes=[None, 'http'], - prepend_scheme='blargg') - except Exception as e: - if str(e)\ - != "prepend_scheme='blargg' is not in allowed_schemes=[None, 'http']": - self.fail('Wrong exception: ' + str(e)) - else: - self.fail("Accepted invalid prepend_scheme: 'blargg'") - - # Not inluding None in the allowed_schemes essentially disabled - # prepending, so even though - # prepend_scheme has the invalid value 'http', we don't care! - - x = IS_URL(allowed_schemes=['https'], prepend_scheme='https') - self.assertEqual(x('google.ca'), ('google.ca', 'Enter a valid URL')) - - # Not inluding None in the allowed_schemes essentially disabled prepending, so even though - # prepend_scheme has the invalid value 'http', we don't care! - - x = IS_URL(mode='generic', allowed_schemes=['https'], - prepend_scheme='https') - self.assertEqual(x('google.ca'), ('google.ca', 'Enter a valid URL')) - - -# ############################################################################## - - -class TestIsGenericUrl(unittest.TestCase): - - x = IS_GENERIC_URL() - - def testInvalidUrls(self): - urlsToCheckA = [] - for i in list(range(0, 32)) + [127]: - - # Control characters are disallowed in any part of a URL - - urlsToCheckA.append('http://www.benn' + chr(i) + '.ca') - - urlsToCheckB = [ - None, - '', - 'http://www.no spaces allowed.com', - 'http://www.benn.ca/no spaces allowed/', - 'http://www.benn.ca/angle_bracket/', - 'http://www.benn.ca/invalid%character', - 'http://www.benn.ca/illegal%%20use', - 'http://www.benn.ca/illegaluse%', - 'http://www.benn.ca/illegaluse%0', - 'http://www.benn.ca/illegaluse%x', - 'http://www.benn.ca/ill%egaluse%x', - 'http://www.benn.ca/double"quote/', - 'http://www.curly{brace.com', - 'http://www.benn.ca/curly}brace/', - 'http://www.benn.ca/or|symbol/', - 'http://www.benn.ca/back\slash', - 'http://www.benn.ca/the^carat', - 'http://left[bracket.me', - 'http://www.benn.ca/right]bracket', - 'http://www.benn.ca/angle`quote', - '-ttp://www.benn.ca', - '+ttp://www.benn.ca', - '.ttp://www.benn.ca', - '9ttp://www.benn.ca', - 'ht;tp://www.benn.ca', - 'ht@tp://www.benn.ca', - 'ht&tp://www.benn.ca', - 'ht=tp://www.benn.ca', - 'ht$tp://www.benn.ca', - 'ht,tp://www.benn.ca', - 'ht:tp://www.benn.ca', - 'htp://invalid_scheme.com', - ] - - failures = [] - - for url in urlsToCheckA + urlsToCheckB: - if self.x(url)[1] is None: - failures.append('Incorrectly accepted: ' + str(url)) - - if len(failures) > 0: - self.fail(failures) - - def testValidUrls(self): - urlsToCheck = [ - 'ftp://ftp.is.co.za/rfc/rfc1808.txt', - 'gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles', - 'http://www.math.uio.no/faq/compression-faq/part1.html', - 'mailto:mduerst@ifi.unizh.ch', - 'news:comp.infosystems.www.servers.unix', - 'telnet://melvyl.ucop.edu/', - 'hTTp://www.benn.ca', - '%66%74%70://ftp.is.co.za/rfc/rfc1808.txt', - '%46%74%70://ftp.is.co.za/rfc/rfc1808.txt', - '/faq/compression-faq/part1.html', - 'google.com', - 'www.google.com:8080', - '128.127.123.250:8080', - 'blargg:ping', - 'http://www.benn.ca', - 'http://benn.ca', - 'http://amazon.com/books/', - 'https://amazon.com/movies', - 'rtsp://idontknowthisprotocol', - 'HTTP://allcaps.com', - 'http://localhost', - 'http://localhost#fragment', - 'http://localhost/hello', - 'http://localhost/hello?query=True', - 'http://localhost/hello/', - 'http://localhost:8080', - 'http://localhost:8080/', - 'http://localhost:8080/hello', - 'http://localhost:8080/hello/', - 'file:///C:/Documents%20and%20Settings/Jonathan/Desktop/view.py', - ] - - failures = [] - - for url in urlsToCheck: - if self.x(url)[1] is not None: - failures.append('Incorrectly rejected: ' + str(url)) - - if len(failures) > 0: - self.fail(failures) - - def testPrepending(self): - # Does not prepend scheme for abbreviated domains - self.assertEqual(self.x('google.ca'), ('google.ca', None)) - - # Does not prepend scheme for abbreviated domains - self.assertEqual(self.x('google.ca:8080'), ('google.ca:8080', None)) - - # Does not prepend when scheme already exists - self.assertEqual(self.x('https://google.ca'), - ('https://google.ca', None)) - - # Does not prepend if None type is not specified in allowed_scheme, - # because a scheme is required - - y = IS_GENERIC_URL(allowed_schemes=['http', 'blargg'], - prepend_scheme='http') - self.assertEqual(y('google.ca'), ('google.ca', 'Enter a valid URL')) - - -# ############################################################################## - - -class TestIsHttpUrl(unittest.TestCase): - - x = IS_HTTP_URL() - - def testInvalidUrls(self): - urlsToCheck = [ - None, - '', - 'http://invalid' + chr(2) + '.com', - 'htp://invalid_scheme.com', - 'blargg://invalid_scheme.com', - 'http://-123.com', - 'http://abcd-.ca', - 'http://-abc123-.me', - 'http://www.dom&ain.com/', - 'http://www.dom=ain.com/', - 'http://www.benn.ca&', - 'http://%62%65%6E%6E%2E%63%61/path', - 'http://.domain.com', - 'http://.domain.com./path', - 'http://domain..com', - 'http://domain...at..com', - 'http://domain.com..', - 'http://domain.com../path', - 'http://domain.3m', - 'http://domain.-3m', - 'http://domain.3m-', - 'http://domain.-3m-', - 'http://domain.co&m', - 'http://domain.m3456', - 'http://domain.m-3/path#fragment', - 'http://domain.m---k/path?query=value', - 'http://23.32..', - 'http://23..32.56.0', - 'http://38997.222.999', - 'http://23.32.56.99.', - 'http://.23.32.56.99', - 'http://.23.32.56.99.', - 'http://w127.123.0.256:8080', - 'http://23.32.56.99:abcd', - 'http://23.32.56.99:23cd', - 'http://google.com:cd22', - 'http://23.32:1300.56.99', - 'http://www.yahoo:1600.com', - 'path/segment/without/starting/slash', - 'http://www.math.uio.no;param=3', - '://ABC.com:/%7esmith/home.html', - ] - - failures = [] - - for url in urlsToCheck: - if self.x(url)[1] is None: - failures.append('Incorrectly accepted: ' + str(url)) - - if len(failures) > 0: - self.fail(failures) - - def testValidUrls(self): - - urlsToCheck = [ - 'http://abc.com:80/~smith/home.html', - 'http://ABC.com/%7Esmith/home.html', - 'http://ABC.com:/%7esmith/home.html', - 'http://www.math.uio.no/faq/compression-faq/part1.html', - '//google.ca/faq/compression-faq/part1.html', - '//google.ca/faq;param=3', - '//google.ca/faq/index.html?query=5', - '//google.ca/faq/index.html;param=value?query=5', - '/faq/compression-faq/part1.html', - '/faq;param=3', - '/faq/index.html?query=5', - '/faq/index.html;param=value?query=5', - 'google.com', - 'benn.ca/init/default', - 'benn.ca/init;param=value/default?query=value', - 'http://host-name---with-dashes.me', - 'http://www.host-name---with-dashes.me', - 'http://a.com', - 'http://a.3.com', - 'http://a.bl-ck.com', - 'http://bl-e.b.com', - 'http://host123with456numbers.ca', - 'http://1234567890.com.', - 'http://1234567890.com./path', - 'http://google.com./path', - 'http://domain.xn--d1acj3b', - 'http://127.123.0.256', - 'http://127.123.0.256/document/drawer', - '127.123.0.256/document/', - '156.212.123.100', - 'http://www.google.com:180200', - 'http://www.google.com:8080/path', - 'http://www.google.com:8080', - '//www.google.com:8080', - 'www.google.com:8080', - 'http://127.123.0.256:8080/path', - '//127.123.0.256:8080', - '127.123.0.256:8080', - 'http://example.me??query=value?', - 'http://a.com', - 'http://3.com', - 'http://www.benn.ca', - 'http://benn.ca', - 'http://amazon.com/books/', - 'https://amazon.com/movies', - 'hTTp://allcaps.com', - 'http://localhost', - 'HTTPS://localhost.', - 'http://localhost#fragment', - 'http://localhost/hello;param=value', - 'http://localhost/hello;param=value/hi;param2=value2;param3=value3', - 'http://localhost/hello?query=True', - 'http://www.benn.ca/hello;param=value/hi;param2=value2;param3=value3/index.html?query=3', - 'http://localhost/hello/?query=1500&five=6', - 'http://localhost:8080', - 'http://localhost:8080/', - 'http://localhost:8080/hello', - 'http://localhost:8080/hello%20world/', - 'http://www.a.3.be-nn.5.ca', - 'http://www.amazon.COM', - ] - - failures = [] - - for url in urlsToCheck: - if self.x(url)[1] is not None: - failures.append('Incorrectly rejected: ' + str(url)) - - if len(failures) > 0: - self.fail(failures) - - def testPrepending(self): - # prepends scheme for abbreviated domains - self.assertEqual(self.x('google.ca'), ('http://google.ca', None)) - - # prepends scheme for abbreviated domains - self.assertEqual(self.x('google.ca:8080'), - ('http://google.ca:8080', None)) - - # does not prepend when scheme already exists - self.assertEqual(self.x('https://google.ca'), - ('https://google.ca', None)) - - y = IS_HTTP_URL( - prepend_scheme='https', allowed_schemes=[None, 'https']) - self.assertEqual(y('google.ca'), ( - 'https://google.ca', None)) # prepends https if asked - - z = IS_HTTP_URL(prepend_scheme=None) - self.assertEqual(z('google.ca:8080'), ('google.ca:8080', - None)) # prepending disabled - - try: - IS_HTTP_URL(prepend_scheme='mailto') - except Exception as e: - if str(e)\ - != "prepend_scheme='mailto' is not in allowed_schemes=[None, 'http', 'https']": - self.fail('Wrong exception: ' + str(e)) - else: - self.fail("Got invalid prepend_scheme: 'mailto'") - - # Does not prepend if None type is not specified in allowed_scheme, because a scheme is required - - a = IS_HTTP_URL(allowed_schemes=['http']) - self.assertEqual(a('google.ca'), ('google.ca', 'Enter a valid URL')) - self.assertEqual(a('google.ca:80'), ('google.ca:80', - 'Enter a valid URL')) - - -class TestUnicode(unittest.TestCase): - x = IS_URL() - y = IS_URL(allowed_schemes=['https'], prepend_scheme='https') - #excludes the option for abbreviated URLs with no scheme - z = IS_URL(prepend_scheme=None) - # disables prepending the scheme in the return value - - def testUnicodeToAsciiUrl(self): - self.assertEqual(unicode_to_ascii_authority(u'www.Alliancefran\xe7aise.nu'), 'www.xn--alliancefranaise-npb.nu') - self.assertEqual( - unicode_to_ascii_authority(u'www.benn.ca'), 'www.benn.ca') - self.assertRaises(UnicodeError, unicode_to_ascii_authority, - u'\u4e2d' * 1000) # label is too long - - def testValidUrls(self): - self.assertEqual(self.x(u'www.Alliancefrancaise.nu'), ( - 'http://www.Alliancefrancaise.nu', None)) - self.assertEqual(self.x(u'www.Alliancefran\xe7aise.nu'), ( - 'http://www.xn--alliancefranaise-npb.nu', None)) - self.assertEqual(self.x(u'www.Alliancefran\xe7aise.nu:8080'), ( - 'http://www.xn--alliancefranaise-npb.nu:8080', None)) - self.assertEqual(self.x(u'http://www.Alliancefran\xe7aise.nu'), - ('http://www.xn--alliancefranaise-npb.nu', None)) - self.assertEqual(self.x(u'http://www.Alliancefran\xe7aise.nu/parnaise/blue'), ('http://www.xn--alliancefranaise-npb.nu/parnaise/blue', None)) - self.assertEqual(self.x(u'http://www.Alliancefran\xe7aise.nu/parnaise/blue#fragment'), ('http://www.xn--alliancefranaise-npb.nu/parnaise/blue#fragment', None)) - self.assertEqual(self.x(u'http://www.Alliancefran\xe7aise.nu/parnaise/blue?query=value#fragment'), ('http://www.xn--alliancefranaise-npb.nu/parnaise/blue?query=value#fragment', None)) - self.assertEqual(self.x(u'http://www.Alliancefran\xe7aise.nu:8080/parnaise/blue?query=value#fragment'), ('http://www.xn--alliancefranaise-npb.nu:8080/parnaise/blue?query=value#fragment', None)) - self.assertEqual(self.x(u'www.Alliancefran\xe7aise.nu/parnaise/blue?query=value#fragment'), ('http://www.xn--alliancefranaise-npb.nu/parnaise/blue?query=value#fragment', None)) - self.assertEqual(self.x( - u'http://\u4e2d\u4fd4.com'), ('http://xn--fiq13b.com', None)) - self.assertEqual(self.x(u'http://\u4e2d\u4fd4.com/\u4e86'), - ('http://xn--fiq13b.com/%4e%86', None)) - self.assertEqual(self.x(u'http://\u4e2d\u4fd4.com/\u4e86?query=\u4e86'), ('http://xn--fiq13b.com/%4e%86?query=%4e%86', None)) - self.assertEqual(self.x(u'http://\u4e2d\u4fd4.com/\u4e86?query=\u4e86#fragment'), ('http://xn--fiq13b.com/%4e%86?query=%4e%86#fragment', None)) - self.assertEqual(self.x(u'http://\u4e2d\u4fd4.com?query=\u4e86#fragment'), ('http://xn--fiq13b.com?query=%4e%86#fragment', None)) - self.assertEqual( - self.x(u'http://B\xfccher.ch'), ('http://xn--bcher-kva.ch', None)) - self.assertEqual(self.x(u'http://\xe4\xf6\xfc\xdf.com'), ( - 'http://xn--ss-uia6e4a.com', None)) - self.assertEqual(self.x( - u'http://visegr\xe1d.com'), ('http://xn--visegrd-mwa.com', None)) - self.assertEqual(self.x(u'http://h\xe1zipatika.com'), ( - 'http://xn--hzipatika-01a.com', None)) - self.assertEqual(self.x(u'http://www.\xe7ukurova.com'), ( - 'http://www.xn--ukurova-txa.com', None)) - self.assertEqual(self.x(u'http://nixier\xf6hre.nixieclock-tube.com'), ('http://xn--nixierhre-57a.nixieclock-tube.com', None)) - self.assertEqual(self.x(u'google.ca.'), ('http://google.ca.', None)) - - self.assertEqual( - self.y(u'https://google.ca'), ('https://google.ca', None)) - self.assertEqual(self.y( - u'https://\u4e2d\u4fd4.com'), ('https://xn--fiq13b.com', None)) - - self.assertEqual(self.z(u'google.ca'), ('google.ca', None)) - - def testInvalidUrls(self): - self.assertEqual( - self.x(u'://ABC.com'), (u'://ABC.com', 'Enter a valid URL')) - self.assertEqual(self.x(u'http://\u4e2d\u4fd4.dne'), ( - u'http://\u4e2d\u4fd4.dne', 'Enter a valid URL')) - self.assertEqual(self.x(u'https://google.dne'), ( - u'https://google.dne', 'Enter a valid URL')) - self.assertEqual(self.x(u'https://google..ca'), ( - u'https://google..ca', 'Enter a valid URL')) - self.assertEqual( - self.x(u'google..ca'), (u'google..ca', 'Enter a valid URL')) - self.assertEqual(self.x(u'http://' + u'\u4e2d' * 1000 + u'.com'), ( - u'http://' + u'\u4e2d' * 1000 + u'.com', 'Enter a valid URL')) - - self.assertEqual(self.x(u'http://google.com#fragment_\u4e86'), ( - u'http://google.com#fragment_\u4e86', 'Enter a valid URL')) - self.assertEqual(self.x(u'http\u4e86://google.com'), ( - u'http\u4e86://google.com', 'Enter a valid URL')) - self.assertEqual(self.x(u'http\u4e86://google.com#fragment_\u4e86'), ( - u'http\u4e86://google.com#fragment_\u4e86', 'Enter a valid URL')) - - self.assertEqual(self.y(u'http://\u4e2d\u4fd4.com/\u4e86'), ( - u'http://\u4e2d\u4fd4.com/\u4e86', 'Enter a valid URL')) - #self.assertEqual(self.y(u'google.ca'), (u'google.ca', 'Enter a valid URL')) - - self.assertEqual(self.z(u'invalid.domain..com'), ( - u'invalid.domain..com', 'Enter a valid URL')) - self.assertEqual(self.z(u'invalid.\u4e2d\u4fd4.blargg'), ( - u'invalid.\u4e2d\u4fd4.blargg', 'Enter a valid URL')) - -# ############################################################################## - - -class TestSimple(unittest.TestCase): - - def test_IS_URL(self): - rtn = IS_URL()('abc.com') - self.assertEqual(rtn, ('http://abc.com', None)) - rtn = IS_URL(mode='generic')('abc.com') - self.assertEqual(rtn, ('abc.com', None)) - rtn = IS_URL(allowed_schemes=['https'], prepend_scheme='https')('https://abc.com') - self.assertEqual(rtn, ('https://abc.com', None)) - rtn = IS_URL(prepend_scheme='https')('abc.com') - self.assertEqual(rtn, ('https://abc.com', None)) - rtn = IS_URL(mode='generic', allowed_schemes=['ftps', 'https'], prepend_scheme='https')('https://abc.com') - self.assertEqual(rtn, ('https://abc.com', None)) - rtn = IS_URL(mode='generic', allowed_schemes=['ftps', 'https', None], prepend_scheme='https')('abc.com') - self.assertEqual(rtn, ('abc.com', None)) - # regression test for issue 773 - rtn = IS_URL()('domain.ninja') - self.assertEqual(rtn, ('http://domain.ninja', None)) - # addition of allowed_tlds - rtn = IS_URL(allowed_tlds=['com', 'net', 'org'])('domain.ninja') - self.assertEqual(rtn, ('domain.ninja', 'Enter a valid URL')) - # mode = 'generic' doesn't consider allowed_tlds - rtn = IS_URL(mode='generic', allowed_tlds=['com', 'net', 'org'])('domain.ninja') - self.assertEqual(rtn, ('domain.ninja', None)) diff --git a/gluon/tests/test_languages.py b/gluon/tests/test_languages.py index cfa07cb8..ea33efd8 100644 --- a/gluon/tests/test_languages.py +++ b/gluon/tests/test_languages.py @@ -88,14 +88,14 @@ class TestTranslations(unittest.TestCase): pass def test_plain(self): - T = languages.translator(self.langpath, self.http_accept_language) + T = languages.TranslatorFactory(self.langpath, self.http_accept_language) self.assertEqual(str(T('Hello World')), 'Hello World') self.assertEqual(str(T('Hello World## comment')), 'Hello World') self.assertEqual(str(T.M('**Hello World**')), 'Hello World') - # sub_tuple testing + # sub_tuple testing self.assertEqual(str(T('%s %%{shop}', 1)), '1 shop') self.assertEqual(str(T('%s %%{shop}', 2)), @@ -192,7 +192,7 @@ class TestTranslations(unittest.TestCase): '2') self.assertEqual(str(T('%i%%{?st?[0]}', 0)), '0') - # sub_dict testing + # sub_dict testing self.assertEqual(str(T('%(key)s %%{is(key)}', dict(key=1))), '1 is') self.assertEqual(str(T('%(key)i %%{is(key)}', dict(key=2))), @@ -340,7 +340,7 @@ class TestMessages(unittest.TestCase): pass def test_decode(self): - T = languages.translator(self.langpath, self.http_accept_language) + T = languages.TranslatorFactory(self.langpath, self.http_accept_language) messages = Messages(T) messages.update({'email_sent':'Email sent', 'test': "ä"}) self.assertEqual(to_unicode(messages.email_sent, 'utf-8'), 'Email sent') @@ -359,7 +359,7 @@ class TestHTMLTag(unittest.TestCase): pass def test_decode(self): - T = languages.translator(self.langpath, self.http_accept_language) + T = languages.TranslatorFactory(self.langpath, self.http_accept_language) elem = SPAN(T("Complete")) self.assertEqual(elem.flatten(), "Complete") elem = SPAN(T("Cannot be empty", language="ru")) diff --git a/gluon/tests/test_recfile.py b/gluon/tests/test_recfile.py index 03e36382..ae05216f 100644 --- a/gluon/tests/test_recfile.py +++ b/gluon/tests/test_recfile.py @@ -71,4 +71,3 @@ class TestRecfile(unittest.TestCase): self.assertFalse(recfile.exists(filename)) self.assertRaises(IOError, recfile.remove, filename) self.assertRaises(IOError, recfile.open, filename, "r") - diff --git a/gluon/tests/test_rocket.py b/gluon/tests/test_rocket.py index a394e895..5519e96d 100644 --- a/gluon/tests/test_rocket.py +++ b/gluon/tests/test_rocket.py @@ -1,3 +1,3 @@ -# TODO : I think we should continue to use pathoc (http://pathod.net/docs/pathoc) for tests but integrate the call in +# TODO : I think we should continue to use pathoc (http://pathod.net/docs/pathoc) for tests but integrate the call in # gluon/tests so they run automatically. No need to make our own tests. # ref: https://groups.google.com/d/msg/web2py-developers/Cjye8_hXZk8/AXbftS3sCgAJ diff --git a/gluon/tests/test_router.py b/gluon/tests/test_router.py index 132f8a04..fc1cd9dd 100644 --- a/gluon/tests/test_router.py +++ b/gluon/tests/test_router.py @@ -1,86 +1,76 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """Unit tests for rewrite.py routers option""" -from __future__ import print_function + import os -import unittest import tempfile import logging +import unittest +import shutil -from gluon.rewrite import load, filter_url, filter_err, get_effective_router, map_url_out +from gluon.rewrite import (load, filter_url, filter_err, + get_effective_router, map_url_out) from gluon.html import URL -from gluon.fileutils import abspath from gluon.settings import global_settings from gluon.http import HTTP from gluon.storage import Storage from gluon._compat import to_bytes, PY2 -logger = None -oldcwd = None -root = None -def norm_root(root): - return root.replace('/', os.sep) +logger = None + +old_root = root = None def setUpModule(): - def make_apptree(): + def make_apptree(root): "build a temporary applications tree" - # applications/ - os.mkdir(abspath('applications')) - - # applications/app/ + # applications/ + os.mkdir(os.path.join(root, 'applications')) + # applications/app/ for app in ('admin', 'examples', 'welcome'): - os.mkdir(abspath('applications', app)) + os.mkdir(os.path.join(root, 'applications', app)) # applications/app/(controllers, static) for subdir in ('controllers', 'static'): - os.mkdir(abspath('applications', app, subdir)) - - # applications/admin/controllers/*.py + os.mkdir(os.path.join(root, 'applications', app, subdir)) + # applications/admin/controllers/*.py + base = os.path.join(root, 'applications', 'admin', 'controllers') for ctr in ('appadmin', 'default', 'gae', 'mercurial', 'shell', 'wizard'): - open(abspath('applications', 'admin', - 'controllers', '%s.py' % ctr), 'w').close() - # applications/examples/controllers/*.py + open(os.path.join(base, '%s.py' % ctr), 'w').close() + # applications/examples/controllers/*.py + base = os.path.join(root, 'applications', 'examples', 'controllers') for ctr in ('ajax_examples', 'appadmin', 'default', 'global', 'spreadsheet'): - open(abspath('applications', 'examples', - 'controllers', '%s.py' % ctr), 'w').close() - # applications/welcome/controllers/*.py - # (include controller that collides with another app) + open(os.path.join(base, '%s.py' % ctr), 'w').close() + # applications/welcome/controllers/*.py + # (include controller that collides with another app) + base = os.path.join(root, 'applications', 'welcome', 'controllers') for ctr in ('appadmin', 'default', 'other', 'admin'): - open(abspath('applications', 'welcome', - 'controllers', '%s.py' % ctr), 'w').close() - - # create an app-specific routes.py for examples app - routes = open(abspath('applications', 'examples', 'routes.py'), 'w') - routes.write("routers=dict(examples=dict(default_function='exdef'))") - routes.close() - - # create language files for examples app + open(os.path.join(base, '%s.py' % ctr), 'w').close() + # create an app-specific routes.py for examples app + routes = os.path.join(root, 'applications', 'examples', 'routes.py') + with open(routes, 'w') as r: + r.write("routers=dict(examples=dict(default_function='exdef'))") + # create language files for examples app + base = os.path.join(root, 'applications', 'examples', 'static') for lang in ('en', 'it'): - os.mkdir(abspath('applications', 'examples', 'static', lang)) - open(abspath('applications', 'examples', 'static', - lang, 'file'), 'w').close() + os.mkdir(os.path.join(base, lang)) + open(os.path.join(base, lang, 'file'), 'w').close() - global oldcwd - if oldcwd is None: # do this only once - oldcwd = os.getcwd() - if not os.path.isdir('gluon'): - os.chdir(os.path.realpath( - '../../')) # run from web2py base directory - import gluon.main # for initialization after chdir - global logger + global old_root, root, logger + if old_root is None: # do this only once + old_root = global_settings.applications_parent + root = global_settings.applications_parent = tempfile.mkdtemp() + make_apptree(root) logger = logging.getLogger('web2py.rewrite') - global_settings.applications_parent = tempfile.mkdtemp() - global root - root = global_settings.applications_parent - make_apptree() def tearDownModule(): - global oldcwd - if oldcwd is not None: - os.chdir(oldcwd) - oldcwd = None + if old_root is not None: + global_settings.applications_parent = old_root + shutil.rmtree(root) + + +def norm_root(root): + return root.replace('/', os.sep) class TestRouter(unittest.TestCase): @@ -1037,12 +1027,14 @@ class TestRouter(unittest.TestCase): 'http://domain.com/app2/static/filename-with_underscore'), norm_root("%s/applications/app2/static/filename-with_underscore" % root)) - from gluon.globals import current - current.response.static_version = None - self.assertEqual(str(URL(a='init', c='default', f='a_b')), "/a_b") self.assertEqual(str(URL(a='app1', c='default', f='a_b')), "/app1/a-b") self.assertEqual(str(URL(a='app2', c='default', f='a_b')), "/app2/a_b") + + from gluon.globals import current + if hasattr(current, 'response'): + current.response.static_version = None + self.assertEqual( str(URL(a='app1', c='static', f='a/b_c')), "/app1/static/a/b_c") self.assertEqual( @@ -1555,4 +1547,3 @@ class TestRouter(unittest.TestCase): load(rdict=router_collide) self.assertEqual(filter_url('http://welcome.com/welcome/admin/index', domain='welcome', out=True), "/welcome/admin") - diff --git a/gluon/tests/test_routes.py b/gluon/tests/test_routes.py index 0fc5f2b8..73482c5a 100644 --- a/gluon/tests/test_routes.py +++ b/gluon/tests/test_routes.py @@ -1,81 +1,67 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """Unit tests for rewrite.py regex routing option""" -import sys import os -import unittest import tempfile -import logging +import unittest +import shutil -from gluon.rewrite import load, filter_url, filter_err, get_effective_router, regex_filter_out, regex_select +from gluon.rewrite import load, filter_url, filter_err, regex_filter_out from gluon.html import URL -from gluon.fileutils import abspath from gluon.settings import global_settings from gluon.http import HTTP from gluon.storage import Storage from gluon._compat import to_bytes -logger = None -oldcwd = None -root = None + +old_root = root = None + +def setUpModule(): + def make_apptree(root): + "build a temporary applications tree" + # applications/ + os.mkdir(os.path.join(root, 'applications')) + # applications/app/ + for app in ('admin', 'examples', 'welcome'): + os.mkdir(os.path.join(root, 'applications', app)) + # applications/app/(controllers, static) + for subdir in ('controllers', 'static'): + os.mkdir(os.path.join(root, 'applications', app, subdir)) + # applications/admin/controllers/*.py + base = os.path.join(root, 'applications', 'admin', 'controllers') + for ctr in ('appadmin', 'default', 'gae', 'mercurial', 'shell', 'wizard'): + open(os.path.join(base, '%s.py' % ctr), 'w').close() + # applications/examples/controllers/*.py + base = os.path.join(root, 'applications', 'examples', 'controllers') + for ctr in ('ajax_examples', 'appadmin', 'default', 'global', 'spreadsheet'): + open(os.path.join(base, '%s.py' % ctr), 'w').close() + # applications/welcome/controllers/*.py + base = os.path.join(root, 'applications', 'welcome', 'controllers') + for ctr in ('appadmin', 'default'): + open(os.path.join(base, '%s.py' % ctr), 'w').close() + # create an app-specific routes.py for examples app + routes = os.path.join(root, 'applications', 'examples', 'routes.py') + with open(routes, 'w') as r: + r.write("default_function='exdef'\n") + + global old_root, root + if old_root is None: # do this only once + old_root = global_settings.applications_parent + root = global_settings.applications_parent = tempfile.mkdtemp() + make_apptree(root) + + +def tearDownModule(): + if old_root is not None: + global_settings.applications_parent = old_root + shutil.rmtree(root) def norm_root(root): return root.replace('/', os.sep) -def setUpModule(): - def make_apptree(): - "build a temporary applications tree" - # applications/ - os.mkdir(abspath('applications')) - # applications/app/ - for app in ('admin', 'examples', 'welcome'): - os.mkdir(abspath('applications', app)) - # applications/app/(controllers, static) - for subdir in ('controllers', 'static'): - os.mkdir(abspath('applications', app, subdir)) - # applications/admin/controllers/*.py - for ctr in ('appadmin', 'default', 'gae', 'mercurial', 'shell', 'wizard'): - open(abspath('applications', 'admin', - 'controllers', '%s.py' % ctr), 'w').close() - # applications/examples/controllers/*.py - for ctr in ('ajax_examples', 'appadmin', 'default', 'global', 'spreadsheet'): - open(abspath('applications', 'examples', - 'controllers', '%s.py' % ctr), 'w').close() - # applications/welcome/controllers/*.py - for ctr in ('appadmin', 'default'): - open(abspath('applications', 'welcome', - 'controllers', '%s.py' % ctr), 'w').close() - # create an app-specific routes.py for examples app - routes = open(abspath('applications', 'examples', 'routes.py'), 'w') - routes.write("default_function='exdef'\n") - routes.close() - - global oldcwd - if oldcwd is None: # do this only once - oldcwd = os.getcwd() - if not os.path.isdir('gluon'): - os.chdir(os.path.realpath( - '../../')) # run from web2py base directory - from gluon import main # for initialization after chdir - global logger - logger = logging.getLogger('web2py.rewrite') - global_settings.applications_parent = tempfile.mkdtemp() - global root - root = global_settings.applications_parent - make_apptree() - - -def tearDownModule(): - global oldcwd - if oldcwd is not None: - os.chdir(oldcwd) - oldcwd = None - - class TestRoutes(unittest.TestCase): """ Tests the regex routing logic from gluon.rewrite """ diff --git a/gluon/tests/test_scheduler.py b/gluon/tests/test_scheduler.py index ee1270fb..393abdab 100644 --- a/gluon/tests/test_scheduler.py +++ b/gluon/tests/test_scheduler.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """ @@ -9,43 +8,46 @@ import unittest import glob import datetime import sys +import shutil from gluon.storage import Storage -from gluon.languages import translator +from gluon.languages import TranslatorFactory from gluon.scheduler import JobGraph, Scheduler, CronParser from gluon.dal import DAL +from gluon.fileutils import create_app +test_app_name = '_test_scheduler' + class BaseTestScheduler(unittest.TestCase): + def setUp(self): + self.tearDown() + appdir = os.path.join('applications', test_app_name) + os.mkdir(appdir) + create_app(appdir) + self.db = None - self.cleanfolder() from gluon.globals import current - s = Storage({'application': 'welcome', - 'folder': 'applications/welcome', + s = Storage({'application': test_app_name, + 'folder': "applications/%s" % test_app_name, 'controller': 'default'}) current.request = s - T = translator('', 'en') + T = TranslatorFactory('', 'en') current.T = T - self.db = DAL('sqlite://dummy2.db', check_reserved=['all']) - - def cleanfolder(self): - if self.db: - self.db.close() - try: - os.unlink('dummy2.db') - except: - pass - tfiles = glob.glob('*_scheduler*.table') - for a in tfiles: - os.unlink(a) + self.db = DAL('sqlite://dummy2.db', + folder="applications/%s/databases" % test_app_name, + check_reserved=['all']) def tearDown(self): - self.cleanfolder() try: self.inner_teardown() except: pass + appdir = os.path.join('applications', test_app_name) + if os.path.exists(appdir): + shutil.rmtree(appdir) + class CronParserTest(unittest.TestCase): @@ -54,105 +56,105 @@ class CronParserTest(unittest.TestCase): # minute asterisk base = datetime.datetime(2010, 1, 23, 12, 18) itr = CronParser('*/1 * * * *', base) - n1 = itr.get_next() # 19 + n1 = itr.next() # 19 self.assertEqual(base.year, n1.year) self.assertEqual(base.month, n1.month) self.assertEqual(base.day, n1.day) self.assertEqual(base.hour, n1.hour) self.assertEqual(base.minute, n1.minute - 1) for i in range(39): # ~ 58 - itr.get_next() - n2 = itr.get_next() + itr.next() + n2 = itr.next() self.assertEqual(n2.minute, 59) - n3 = itr.get_next() + n3 = itr.next() self.assertEqual(n3.minute, 0) self.assertEqual(n3.hour, 13) itr = CronParser('*/5 * * * *', base) - n4 = itr.get_next() + n4 = itr.next() self.assertEqual(n4.minute, 20) for i in range(6): - itr.get_next() - n5 = itr.get_next() + itr.next() + n5 = itr.next() self.assertEqual(n5.minute, 55) - n6 = itr.get_next() + n6 = itr.next() self.assertEqual(n6.minute, 0) self.assertEqual(n6.hour, 13) base = datetime.datetime(2010, 1, 23, 12, 18) itr = CronParser('4/34 * * * *', base) - n7 = itr.get_next() + n7 = itr.next() self.assertEqual(n7.minute, 38) self.assertEqual(n7.hour, 12) - n8 = itr.get_next() + n8 = itr.next() self.assertEqual(n8.minute, 4) self.assertEqual(n8.hour, 13) def testHour(self): base = datetime.datetime(2010, 1, 24, 12, 2) itr = CronParser('0 */3 * * *', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.hour, 15) self.assertEqual(n1.minute, 0) for i in range(2): - itr.get_next() - n2 = itr.get_next() + itr.next() + n2 = itr.next() self.assertEqual(n2.hour, 0) self.assertEqual(n2.day, 25) def testDay(self): base = datetime.datetime(2010, 2, 24, 12, 9) itr = CronParser('0 0 */3 * *', base) - n1 = itr.get_next() + n1 = itr.next() # 1 4 7 10 13 16 19 22 25 28 self.assertEqual(n1.day, 25) - n2 = itr.get_next() + n2 = itr.next() self.assertEqual(n2.day, 28) - n3 = itr.get_next() + n3 = itr.next() self.assertEqual(n3.day, 1) self.assertEqual(n3.month, 3) # test leap year base = datetime.datetime(1996, 2, 27) itr = CronParser('0 0 * * *', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.day, 28) self.assertEqual(n1.month, 2) - n2 = itr.get_next() + n2 = itr.next() self.assertEqual(n2.day, 29) self.assertEqual(n2.month, 2) base2 = datetime.datetime(2000, 2, 27) itr2 = CronParser('0 0 * * *', base2) - n3 = itr2.get_next() + n3 = itr2.next() self.assertEqual(n3.day, 28) self.assertEqual(n3.month, 2) - n4 = itr2.get_next() + n4 = itr2.next() self.assertEqual(n4.day, 29) self.assertEqual(n4.month, 2) def testWeekDay(self): base = datetime.datetime(2010, 2, 25) itr = CronParser('0 0 * * sat', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.isoweekday(), 6) self.assertEqual(n1.day, 27) - n2 = itr.get_next() + n2 = itr.next() self.assertEqual(n2.isoweekday(), 6) self.assertEqual(n2.day, 6) self.assertEqual(n2.month, 3) base = datetime.datetime(2010, 1, 25) itr = CronParser('0 0 1 * wed', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.month, 1) self.assertEqual(n1.day, 27) self.assertEqual(n1.year, 2010) - n2 = itr.get_next() + n2 = itr.next() self.assertEqual(n2.month, 2) self.assertEqual(n2.day, 1) self.assertEqual(n2.year, 2010) - n3 = itr.get_next() + n3 = itr.next() self.assertEqual(n3.month, 2) self.assertEqual(n3.day, 3) self.assertEqual(n3.year, 2010) @@ -160,43 +162,43 @@ class CronParserTest(unittest.TestCase): def testMonth(self): base = datetime.datetime(2010, 1, 25) itr = CronParser('0 0 1 * *', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.month, 2) self.assertEqual(n1.day, 1) - n2 = itr.get_next() + n2 = itr.next() self.assertEqual(n2.month, 3) self.assertEqual(n2.day, 1) for i in range(8): - itr.get_next() - n3 = itr.get_next() + itr.next() + n3 = itr.next() self.assertEqual(n3.month, 12) self.assertEqual(n3.year, 2010) - n4 = itr.get_next() + n4 = itr.next() self.assertEqual(n4.month, 1) self.assertEqual(n4.year, 2011) base = datetime.datetime(2010, 1, 25) itr = CronParser('0 0 1 */4 *', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.month, 5) self.assertEqual(n1.day, 1) base = datetime.datetime(2010, 1, 25) itr = CronParser('0 0 1 1-3 *', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.month, 2) self.assertEqual(n1.day, 1) - n2 = itr.get_next() + n2 = itr.next() self.assertEqual(n2.month, 3) self.assertEqual(n2.day, 1) - n3 = itr.get_next() + n3 = itr.next() self.assertEqual(n3.month, 1) self.assertEqual(n3.day, 1) def testSundayToThursdayWithAlphaConversion(self): base = datetime.datetime(2010, 8, 25, 15, 56) itr = CronParser("30 22 * * sun-thu", base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(base.year, n1.year) self.assertEqual(base.month, n1.month) self.assertEqual(base.day, n1.day) @@ -206,19 +208,19 @@ class CronParserTest(unittest.TestCase): def testISOWeekday(self): base = datetime.datetime(2010, 2, 25) itr = CronParser('0 0 * * 7', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.isoweekday(), 7) self.assertEqual(n1.day, 28) - n2 = itr.get_next() + n2 = itr.next() self.assertEqual(n2.isoweekday(), 7) self.assertEqual(n2.day, 7) self.assertEqual(n2.month, 3) base = datetime.datetime(2010, 2, 22) itr = CronParser('0 0 * * */2', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.isoweekday(), 2) self.assertEqual(n1.day, 23) - n2 = itr.get_next() + n2 = itr.next() self.assertEqual(n2.isoweekday(), 4) self.assertEqual(n2.day, 25) @@ -226,21 +228,21 @@ class CronParserTest(unittest.TestCase): base = datetime.datetime(2012, 1, 1, 0, 0) itr = CronParser('0 * * 3 *', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.year, base.year) self.assertEqual(n1.month, 3) self.assertEqual(n1.day, base.day) self.assertEqual(n1.hour, base.hour) self.assertEqual(n1.minute, base.minute) - n2 = itr.get_next() + n2 = itr.next() self.assertEqual(n2.year, base.year) self.assertEqual(n2.month, 3) self.assertEqual(n2.day, base.day) self.assertEqual(n2.hour, base.hour + 1) self.assertEqual(n2.minute, base.minute) - n3 = itr.get_next() + n3 = itr.next() self.assertEqual(n3.year, base.year) self.assertEqual(n3.month, 3) self.assertEqual(n3.day, base.day) @@ -251,26 +253,26 @@ class CronParserTest(unittest.TestCase): base = datetime.datetime(2013, 3, 1, 12, 17, 34, 257877) c = CronParser('00 03 16,30 * *', base) - n1 = c.get_next() + n1 = c.next() self.assertEqual(n1.month, 3) self.assertEqual(n1.day, 16) - n2 = c.get_next() + n2 = c.next() self.assertEqual(n2.month, 3) self.assertEqual(n2.day, 30) - n3 = c.get_next() + n3 = c.next() self.assertEqual(n3.month, 4) self.assertEqual(n3.day, 16) def test_rangeGenerator(self): base = datetime.datetime(2013, 3, 4, 0, 0) itr = CronParser('1-9/2 0 1 * *', base) - n1 = itr.get_next() - n2 = itr.get_next() - n3 = itr.get_next() - n4 = itr.get_next() - n5 = itr.get_next() + n1 = itr.next() + n2 = itr.next() + n3 = itr.next() + n4 = itr.next() + n5 = itr.next() self.assertEqual(n1.minute, 1) self.assertEqual(n2.minute, 3) self.assertEqual(n3.minute, 5) @@ -290,45 +292,49 @@ class CronParserTest(unittest.TestCase): def test_invalidcron(self): base = datetime.datetime(2013, 3, 4, 0, 0) itr = CronParser('5 4 31 2 *', base) - self.assertRaises(ValueError, itr.get_next) + self.assertRaises(ValueError, itr.next) + itr = CronParser('1- * * * *', base) + self.assertRaises(ValueError, itr.next) + itr = CronParser('-1 * * * *', base) + self.assertRaises(ValueError, itr.next) itr = CronParser('* * 5-1 * *', base) - self.assertRaises(ValueError, itr.get_next) + self.assertRaises(ValueError, itr.next) itr = CronParser('* * * janu-jun *', base) - self.assertRaises(KeyError, itr.get_next) + self.assertRaises(ValueError, itr.next) itr = CronParser('* * * * * *', base) - self.assertRaises(ValueError, itr.get_next) + self.assertRaises(ValueError, itr.next) itr = CronParser('* * * *', base) - self.assertRaises(ValueError, itr.get_next) + self.assertRaises(ValueError, itr.next) def testLastDayOfMonth(self): base = datetime.datetime(2015, 9, 4) itr = CronParser('0 0 L * *', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.month, 9) self.assertEqual(n1.day, 30) - n2 = itr.get_next() + n2 = itr.next() self.assertEqual(n2.month, 10) self.assertEqual(n2.day, 31) - n3 = itr.get_next() + n3 = itr.next() self.assertEqual(n3.month, 11) self.assertEqual(n3.day, 30) - n4 = itr.get_next() + n4 = itr.next() self.assertEqual(n4.month, 12) self.assertEqual(n4.day, 31) base = datetime.datetime(1996, 2, 27) itr = CronParser('0 0 L * *', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.day, 29) self.assertEqual(n1.month, 2) - n2 = itr.get_next() + n2 = itr.next() self.assertEqual(n2.day, 31) self.assertEqual(n2.month, 3) def testSpecialExpr(self): base = datetime.datetime(2000, 1, 1) itr = CronParser('@yearly', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.day, 1) self.assertEqual(n1.month, 1) self.assertEqual(n1.year, base.year + 1) @@ -336,7 +342,7 @@ class CronParserTest(unittest.TestCase): self.assertEqual(n1.minute, 0) itr = CronParser('@annually', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.day, 1) self.assertEqual(n1.month, 1) self.assertEqual(n1.year, base.year + 1) @@ -344,7 +350,7 @@ class CronParserTest(unittest.TestCase): self.assertEqual(n1.minute, 0) itr = CronParser('@monthly', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.day, 1) self.assertEqual(n1.month, base.month + 1) self.assertEqual(n1.year, base.year) @@ -352,19 +358,19 @@ class CronParserTest(unittest.TestCase): self.assertEqual(n1.minute, 0) itr = CronParser('@weekly', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.day, 2) self.assertEqual(n1.month, base.month) self.assertEqual(n1.year, base.year) self.assertEqual(n1.hour, 0) self.assertEqual(n1.minute, 0) - n2 = itr.get_next() + n2 = itr.next() self.assertEqual(n2.day, 9) self.assertEqual(n2.month, base.month) self.assertEqual(n2.year, base.year) self.assertEqual(n2.hour, 0) self.assertEqual(n2.minute, 0) - n3 = itr.get_next() + n3 = itr.next() self.assertEqual(n3.day, 16) self.assertEqual(n3.month, base.month) self.assertEqual(n3.year, base.year) @@ -372,7 +378,7 @@ class CronParserTest(unittest.TestCase): self.assertEqual(n3.minute, 0) itr = CronParser('@daily', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.day, 2) self.assertEqual(n1.month, base.month) self.assertEqual(n1.year, base.year) @@ -380,7 +386,7 @@ class CronParserTest(unittest.TestCase): self.assertEqual(n1.minute, 0) itr = CronParser('@midnight', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.day, 2) self.assertEqual(n1.month, base.month) self.assertEqual(n1.year, base.year) @@ -388,7 +394,7 @@ class CronParserTest(unittest.TestCase): self.assertEqual(n1.minute, 0) itr = CronParser('@hourly', base) - n1 = itr.get_next() + n1 = itr.next() self.assertEqual(n1.day, 1) self.assertEqual(n1.month, base.month) self.assertEqual(n1.year, base.year) @@ -551,12 +557,12 @@ class TestsForSchedulerAPIs(BaseTestScheduler): def isnotqueued(result): self.assertEqual(result.id, None) self.assertEqual(result.uuid, None) - self.assertEqual(len(result.errors.keys()) > 0, True) + self.assertEqual(len(list(result.errors.keys())) > 0, True) def isqueued(result): self.assertNotEqual(result.id, None) self.assertNotEqual(result.uuid, None) - self.assertEqual(len(result.errors.keys()), 0) + self.assertEqual(len(list(result.errors.keys())), 0) s = Scheduler(self.db) fname = 'foo' @@ -624,12 +630,11 @@ class testForSchedulerRunnerBase(BaseTestScheduler): import os import time from gluon.scheduler import Scheduler -db_dal = os.path.abspath(os.path.join(request.folder, '..', '..', 'dummy2.db')) +db_dal = os.path.abspath(os.path.join(request.folder, 'databases', 'dummy2.db')) sched_dal = DAL('sqlite://%s' % db_dal, folder=os.path.dirname(db_dal)) sched = Scheduler(sched_dal, max_empty_runs=15, migrate=False, heartbeat=1) def termination(): sched.terminate() - sched_dal.commit() """ with open(fdest, 'w') as q: q.write(initlines) @@ -637,7 +642,7 @@ def termination(): def exec_sched(self): import subprocess - call_args = [sys.executable, 'web2py.py', '--no-banner', '-D', '20','-K', 'welcome'] + call_args = [sys.executable, 'web2py.py', '--no_banner', '-D', 'INFO','-K', test_app_name] ret = subprocess.call(call_args, env=dict(os.environ)) return ret @@ -680,7 +685,7 @@ def demo4(): ("task times_run is 2", task.times_run == 2), ("task ran 2 times only", len(task_run) == 2), ("scheduler_run records are COMPLETED ", (task_run[0].status == task_run[1].status == 'COMPLETED')), - ("period is respected", (task_run[1].start_time > task_run[0].start_time + datetime.timedelta(seconds=task.period))) + ("period is respected", (task_run[1].start_time >= task_run[0].start_time + datetime.timedelta(seconds=task.period))) ] self.exec_asserts(res, 'REPEATS') @@ -840,7 +845,7 @@ def demo8(): ("task times_failed is 2", task.times_failed == 2), ("task ran 2 times only", len(task_run) == 2), ("scheduler_run records are FAILED", (task_run[0].status == task_run[1].status == 'FAILED')), - ("period is respected", (task_run[1].start_time > task_run[0].start_time + datetime.timedelta(seconds=task.period))) + ("period is respected", (task_run[1].start_time >= task_run[0].start_time + datetime.timedelta(seconds=task.period))) ] self.exec_asserts(res, 'FAILED') diff --git a/gluon/tests/test_serializers.py b/gluon/tests/test_serializers.py index a7f2a44c..d3c2cb48 100644 --- a/gluon/tests/test_serializers.py +++ b/gluon/tests/test_serializers.py @@ -13,7 +13,7 @@ import decimal from gluon.serializers import * from gluon.storage import Storage # careful with the import path 'cause of isinstance() checks -from gluon.languages import translator +from gluon.languages import TranslatorFactory from gluon.html import SPAN @@ -46,11 +46,12 @@ class TestSerializers(unittest.TestCase): obj = {'a': decimal.Decimal('4.312312312312')} self.assertEqual(json(obj), u'{"a": "4.312312312312"}') # lazyT translated - T = translator('', 'en') + T = TranslatorFactory('', 'en') lazy_translation = T('abc') self.assertEqual(json(lazy_translation), u'"abc"') # html helpers are xml()ed before too - self.assertEqual(json(SPAN('abc')), u'"abc"') + self.assertEqual(json(SPAN('abc'), cls=None), u'"abc"') + self.assertEqual(json(SPAN('abc')), u'"\\u003cspan\\u003eabc\\u003c/span\\u003e"') # unicode keys make a difference with loads_json base = {u'è': 1, 'b': 2} base_enc = json(base) diff --git a/gluon/tests/test_sqlhtml.py b/gluon/tests/test_sqlhtml.py index 92c2916e..703b10eb 100644 --- a/gluon/tests/test_sqlhtml.py +++ b/gluon/tests/test_sqlhtml.py @@ -4,6 +4,7 @@ """ Unit tests for gluon.sqlhtml """ +import datetime import os import sys import unittest @@ -17,7 +18,7 @@ from pydal.objects import Table from gluon.tools import Auth, Mail from gluon.globals import Request, Response, Session from gluon.storage import Storage -from gluon.languages import translator +from gluon.languages import TranslatorFactory from gluon.http import HTTP from gluon.validators import * @@ -253,7 +254,7 @@ class TestSQLFORM(unittest.TestCase): request.folder = 'applications/admin' response = Response() session = Session() - T = translator('', 'en') + T = TranslatorFactory('', 'en') session.connect(request, response) from gluon.globals import current current.request = request @@ -276,7 +277,7 @@ class TestSQLFORM(unittest.TestCase): self.db.commit() - + def test_SQLFORM(self): form = SQLFORM(self.db.auth_user) self.assertEqual(form.xml()[:5], b'')}}""") - raise IOError - - with monkey_patch(template, 'open', dummy_open): - self.assertEqual( - render(filename=pjoin('views', 'default', 'index.html'), - path='views'), - 'left to right') - self.assertEqual( - render(filename=pjoin('views', 'default', 'indexbrackets.html'), - path='views', delimiters=('[[', ']]')), - 'left to right') - self.assertRaises( - RestrictedError, - render, - filename=pjoin('views', 'default', 'missing.html'), - path='views') - response = template.DummyResponse() - response.delimiters = ('[[', ']]') - self.assertEqual( - render(filename=pjoin('views', 'default', 'indexbrackets.html'), - path='views', context={'response': response}), - 'left to right') - self.assertEqual( - render(filename=pjoin('views', 'default', 'noescape.html'), - context={'NOESCAPE': template.NOESCAPE}), - '') diff --git a/gluon/tests/test_tools.py b/gluon/tests/test_tools.py index 2ad46c5e..3f619cb2 100644 --- a/gluon/tests/test_tools.py +++ b/gluon/tests/test_tools.py @@ -18,10 +18,10 @@ from gluon.dal import DAL, Field from pydal.objects import Table from gluon import tools from gluon.tools import Auth, Mail, Recaptcha2, prettydate, Expose -from gluon._compat import PY2 +from gluon._compat import PY2, to_bytes from gluon.globals import Request, Response, Session from gluon.storage import Storage -from gluon.languages import translator +from gluon.languages import TranslatorFactory from gluon.http import HTTP from gluon import SPAN, H3, TABLE, TR, TD, A, URL, current @@ -193,7 +193,7 @@ class TestMail(unittest.TestCase): message = TestMail.DummySMTP.inbox.pop() attachment = message.parsed_payload.get_payload(1).get_payload(decode=True) with open(module_file, 'rb') as mf: - self.assertEqual(attachment.decode('utf-8'), mf.read().decode('utf-8')) + self.assertEqual(to_bytes(attachment), to_bytes(mf.read())) # Test missing attachment name error stream = open(module_file) self.assertRaises(Exception, lambda *args, **kwargs: Mail.Attachment(*args, **kwargs), stream) @@ -208,13 +208,6 @@ class TestMail(unittest.TestCase): self.assertTrue('Content-Id: ' in message.payload) -# class TestRecaptcha2(unittest.TestCase): -# def test_Recaptcha2(self): -# from html import FORM -# form = FORM(Recaptcha2(public_key='public_key', private_key='private_key')) -# rtn = '
' -# self.assertEqual(form.xml(), rtn) - # TODO: class TestAuthJWT(unittest.TestCase): class TestAuthJWT(unittest.TestCase): def setUp(self): @@ -278,7 +271,7 @@ class TestAuthJWT(unittest.TestCase): # request.folder = 'applications/admin' # response = Response() # session = Session() -# T = translator('', 'en') +# T = TranslatorFactory('', 'en') # session.connect(request, response) # from gluon.globals import current # current.request = request @@ -441,7 +434,7 @@ class TestAuthJWT(unittest.TestCase): # # request.folder = 'applications/admin' # # response = Response() # # session = Session() -# # T = translator('', 'en') +# # T = TranslatorFactory('', 'en') # # session.connect(request, response) # # from gluon.globals import current # # current.request = request @@ -502,7 +495,7 @@ class TestAuth(unittest.TestCase): self.request.folder = 'applications/admin' self.response = Response() self.session = Session() - T = translator('', 'en') + T = TranslatorFactory('', 'en') self.session.connect(self.request, self.response) from gluon.globals import current self.current = current @@ -1387,4 +1380,3 @@ class TestExpose(unittest.TestCase): def test_not_authorized(self): with self.assertRaises(HTTP): self.make_expose(base='inside', show='link_to_file3') - diff --git a/gluon/tests/test_utils.py b/gluon/tests/test_utils.py index 8b94844f..6f6f9d50 100644 --- a/gluon/tests/test_utils.py +++ b/gluon/tests/test_utils.py @@ -9,7 +9,8 @@ from hashlib import md5 from gluon.utils import md5_hash, compare, is_valid_ip_address, web2py_uuid import gluon.utils -from gluon.utils import simple_hash, get_digest, secure_dumps, secure_loads +from gluon.validators import simple_hash, get_digest +from gluon.utils import secure_dumps, secure_loads class TestUtils(unittest.TestCase): diff --git a/gluon/tests/test_validators.py b/gluon/tests/test_validators.py deleted file mode 100644 index 73712664..00000000 --- a/gluon/tests/test_validators.py +++ /dev/null @@ -1,1199 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -"""Unit tests for http.py """ - -import unittest -import datetime -import decimal -import re - -from gluon.validators import * -from gluon._compat import PY2, to_bytes - -class TestValidators(unittest.TestCase): - - def myassertRegex(self, *args, **kwargs): - if PY2: - return getattr(self, 'assertRegexpMatches')(*args, **kwargs) - return getattr(self, 'assertRegex')(*args, **kwargs) - - def test_MISC(self): - """ Test miscelaneous utility functions and some general behavior guarantees """ - from gluon.validators import translate, options_sorter, Validator, UTC - self.assertEqual(translate(None), None) - self.assertEqual(options_sorter(('a', 'a'), ('a', 'a')), -1) - self.assertEqual(options_sorter(('A', 'A'), ('a', 'a')), -1) - self.assertEqual(options_sorter(('b', 'b'), ('a', 'a')), 1) - self.assertRaises(NotImplementedError, Validator(), 1) - utc = UTC() - dt = datetime.datetime.now() - self.assertEqual(utc.utcoffset(dt), UTC.ZERO) - self.assertEqual(utc.dst(dt), UTC.ZERO) - self.assertEqual(utc.tzname(dt), 'UTC') - - def test_IS_MATCH(self): - rtn = IS_MATCH('.+')('hello') - self.assertEqual(rtn, ('hello', None)) - rtn = IS_MATCH('hell')('hello') - self.assertEqual(rtn, ('hello', None)) - rtn = IS_MATCH('hell.*', strict=False)('hello') - self.assertEqual(rtn, ('hello', None)) - rtn = IS_MATCH('hello')('shello') - self.assertEqual(rtn, ('shello', 'Invalid expression')) - rtn = IS_MATCH('hello', search=True)('shello') - self.assertEqual(rtn, ('shello', None)) - rtn = IS_MATCH('hello', search=True, strict=False)('shellox') - self.assertEqual(rtn, ('shellox', None)) - rtn = IS_MATCH('.*hello.*', search=True, strict=False)('shellox') - self.assertEqual(rtn, ('shellox', None)) - rtn = IS_MATCH('.+')('') - self.assertEqual(rtn, ('', 'Invalid expression')) - rtn = IS_MATCH('hell', strict=True)('hellas') - self.assertEqual(rtn, ('hellas', 'Invalid expression')) - rtn = IS_MATCH('hell$', strict=True)('hellas') - self.assertEqual(rtn, ('hellas', 'Invalid expression')) - rtn = IS_MATCH('^.hell$', strict=True)('shell') - self.assertEqual(rtn, ('shell', None)) - rtn = IS_MATCH(u'hell', is_unicode=True)('àòè') - if PY2: - self.assertEqual(rtn, ('\xc3\xa0\xc3\xb2\xc3\xa8', 'Invalid expression')) - else: - self.assertEqual(rtn, ('àòè', 'Invalid expression')) - rtn = IS_MATCH(u'hell', is_unicode=True)(u'hell') - self.assertEqual(rtn, (u'hell', None)) - rtn = IS_MATCH('hell', is_unicode=True)(u'hell') - self.assertEqual(rtn, (u'hell', None)) - # regr test for #1044 - rtn = IS_MATCH('hello')(u'\xff') - self.assertEqual(rtn, (u'\xff', 'Invalid expression')) - - def test_IS_EQUAL_TO(self): - rtn = IS_EQUAL_TO('aaa')('aaa') - self.assertEqual(rtn, ('aaa', None)) - rtn = IS_EQUAL_TO('aaa')('aab') - self.assertEqual(rtn, ('aab', 'No match')) - - def test_IS_EXPR(self): - rtn = IS_EXPR('int(value) < 2')('1') - self.assertEqual(rtn, ('1', None)) - rtn = IS_EXPR('int(value) < 2')('2') - self.assertEqual(rtn, ('2', 'Invalid expression')) - rtn = IS_EXPR(lambda value: int(value))('1') - self.assertEqual(rtn, ('1', 1)) - rtn = IS_EXPR(lambda value: int(value) < 2 and 'invalid' or None)('2') - self.assertEqual(rtn, ('2', None)) - - def test_IS_LENGTH(self): - rtn = IS_LENGTH()('') - self.assertEqual(rtn, ('', None)) - rtn = IS_LENGTH()('1234567890') - self.assertEqual(rtn, ('1234567890', None)) - rtn = IS_LENGTH(maxsize=5, minsize=0)('1234567890') # too long - self.assertEqual(rtn, ('1234567890', 'Enter from 0 to 5 characters')) - rtn = IS_LENGTH(maxsize=50, minsize=20)('1234567890') # too short - self.assertEqual(rtn, ('1234567890', 'Enter from 20 to 50 characters')) - rtn = IS_LENGTH()(None) - self.assertEqual(rtn, (None, None)) - rtn = IS_LENGTH(minsize=0)(None) - self.assertEqual(rtn, (None, None)) - rtn = IS_LENGTH(minsize=1)(None) - self.assertEqual(rtn, (None, 'Enter from 1 to 255 characters')) - rtn = IS_LENGTH(minsize=1)([]) - self.assertEqual(rtn, ([], 'Enter from 1 to 255 characters')) - rtn = IS_LENGTH(minsize=1)([1, 2]) - self.assertEqual(rtn, ([1, 2], None)) - rtn = IS_LENGTH(minsize=1)([1]) - self.assertEqual(rtn, ([1], None)) - # test non utf-8 str - cpstr = u'lálá'.encode('cp1252') - rtn = IS_LENGTH(minsize=4)(cpstr) - self.assertEqual(rtn, (cpstr, None)) - rtn = IS_LENGTH(maxsize=4)(cpstr) - self.assertEqual(rtn, (cpstr, None)) - rtn = IS_LENGTH(minsize=0, maxsize=3)(cpstr) - self.assertEqual(rtn, (cpstr, 'Enter from 0 to 3 characters')) - # test unicode - rtn = IS_LENGTH(2)(u'°2') - if PY2: - self.assertEqual(rtn, ('\xc2\xb02', None)) - else: - self.assertEqual(rtn, (u'°2', None)) - rtn = IS_LENGTH(2)(u'°12') - if PY2: - self.assertEqual(rtn, (u'\xb012', 'Enter from 0 to 2 characters')) - else: - self.assertEqual(rtn, (u'°12', 'Enter from 0 to 2 characters')) - # test automatic str() - rtn = IS_LENGTH(minsize=1)(1) - self.assertEqual(rtn, ('1', None)) - rtn = IS_LENGTH(minsize=2)(1) - self.assertEqual(rtn, (1, 'Enter from 2 to 255 characters')) - # test FieldStorage - import cgi - from io import BytesIO - a = cgi.FieldStorage() - a.file = BytesIO(b'abc') - rtn = IS_LENGTH(minsize=4)(a) - self.assertEqual(rtn, (a, 'Enter from 4 to 255 characters')) - urlencode_data = b"key2=value2x&key3=value3&key4=value4" - urlencode_environ = { - 'CONTENT_LENGTH': str(len(urlencode_data)), - 'CONTENT_TYPE': 'application/x-www-form-urlencoded', - 'QUERY_STRING': 'key1=value1&key2=value2y', - 'REQUEST_METHOD': 'POST', - } - fake_stdin = BytesIO(urlencode_data) - fake_stdin.seek(0) - a = cgi.FieldStorage(fp=fake_stdin, environ=urlencode_environ) - rtn = IS_LENGTH(minsize=6)(a) - self.assertEqual(rtn, (a, 'Enter from 6 to 255 characters')) - a = cgi.FieldStorage() - rtn = IS_LENGTH(minsize=6)(a) - self.assertEqual(rtn, (a, 'Enter from 6 to 255 characters')) - rtn = IS_LENGTH(6)(a) - self.assertEqual(rtn, (a, None)) - - def test_IS_JSON(self): - rtn = IS_JSON()('{"a": 100}') - self.assertEqual(rtn, ({u'a': 100}, None)) - rtn = IS_JSON()('spam1234') - self.assertEqual(rtn, ('spam1234', 'Invalid json')) - rtn = IS_JSON(native_json=True)('{"a": 100}') - self.assertEqual(rtn, ('{"a": 100}', None)) - rtn = IS_JSON().formatter(None) - self.assertEqual(rtn, None) - rtn = IS_JSON().formatter({'a': 100}) - self.assertEqual(rtn, '{"a": 100}') - rtn = IS_JSON(native_json=True).formatter({'a': 100}) - self.assertEqual(rtn, {'a': 100}) - - def test_IS_IN_SET(self): - rtn = IS_IN_SET(['max', 'john'])('max') - self.assertEqual(rtn, ('max', None)) - rtn = IS_IN_SET(['max', 'john'])('massimo') - self.assertEqual(rtn, ('massimo', 'Value not allowed')) - rtn = IS_IN_SET(['max', 'john'], multiple=True)(('max', 'john')) - self.assertEqual(rtn, (('max', 'john'), None)) - rtn = IS_IN_SET(['max', 'john'], multiple=True)(('bill', 'john')) - self.assertEqual(rtn, (('bill', 'john'), 'Value not allowed')) - rtn = IS_IN_SET(('id1', 'id2'), ['first label', 'second label'])('id1') # Traditional way - self.assertEqual(rtn, ('id1', None)) - rtn = IS_IN_SET({'id1': 'first label', 'id2': 'second label'})('id1') - self.assertEqual(rtn, ('id1', None)) - rtn = IS_IN_SET(['id1', 'id2'], error_message='oops', multiple=True)(None) - self.assertEqual(rtn, ([], None)) - rtn = IS_IN_SET(['id1', 'id2'], error_message='oops', multiple=True)('') - self.assertEqual(rtn, ([], None)) - rtn = IS_IN_SET(['id1', 'id2'], error_message='oops', multiple=True)('id1') - self.assertEqual(rtn, (['id1'], None)) - rtn = IS_IN_SET(['id1', 'id2'], error_message='oops', multiple=(1, 2))(None) - self.assertEqual(rtn, ([], 'oops')) - import itertools - rtn = IS_IN_SET(itertools.chain(['1', '3', '5'], ['2', '4', '6']))('1') - self.assertEqual(rtn, ('1', None)) - rtn = IS_IN_SET([('id1', 'first label'), ('id2', 'second label')])('id1') # Redundant way - self.assertEqual(rtn, ('id1', None)) - rtn = IS_IN_SET([('id1', 'first label'), ('id2', 'second label')]).options(zero=False) - self.assertEqual(rtn, [('id1', 'first label'), ('id2', 'second label')]) - rtn = IS_IN_SET(['id1', 'id2']).options(zero=False) - self.assertEqual(rtn, [('id1', 'id1'), ('id2', 'id2')]) - rtn = IS_IN_SET(['id2', 'id1'], sort=True).options(zero=False) - self.assertEqual(rtn, [('id1', 'id1'), ('id2', 'id2')]) - - def test_IS_IN_DB(self): - from gluon.dal import DAL, Field - db = DAL('sqlite:memory') - db.define_table('person', Field('name')) - george_id = db.person.insert(name='george') - costanza_id = db.person.insert(name='costanza') - rtn = IS_IN_DB(db, 'person.id', '%(name)s')(george_id) - self.assertEqual(rtn, (george_id, None)) - rtn = IS_IN_DB(db, 'person.name', '%(name)s')('george') - self.assertEqual(rtn, ('george', None)) - rtn = IS_IN_DB(db, db.person, '%(name)s')(george_id) - self.assertEqual(rtn, (george_id, None)) - rtn = IS_IN_DB(db(db.person.id > 0), db.person, '%(name)s')(george_id) - self.assertEqual(rtn, (george_id, None)) - rtn = IS_IN_DB(db, 'person.id', '%(name)s', error_message='oops')(george_id + costanza_id) - self.assertEqual(rtn, (george_id + costanza_id, 'oops')) - rtn = IS_IN_DB(db, db.person.id, '%(name)s')(george_id) - self.assertEqual(rtn, (george_id, None)) - rtn = IS_IN_DB(db, db.person.id, '%(name)s', error_message='oops')(george_id + costanza_id) - self.assertEqual(rtn, (george_id + costanza_id, 'oops')) - rtn = IS_IN_DB(db, 'person.id', '%(name)s', multiple=True)([george_id, costanza_id]) - self.assertEqual(rtn, ([george_id, costanza_id], None)) - rtn = IS_IN_DB(db, 'person.id', '%(name)s', multiple=True, error_message='oops')("I'm not even an id") - self.assertEqual(rtn, (["I'm not even an id"], 'oops')) - rtn = IS_IN_DB(db, 'person.id', '%(name)s', multiple=True, delimiter=',')('%d,%d' % (george_id, costanza_id)) - self.assertEqual(rtn, (('%d,%d' % (george_id, costanza_id)).split(','), None)) - rtn = IS_IN_DB(db, 'person.id', '%(name)s', multiple=(1, 3), delimiter=',')('%d,%d' % (george_id, costanza_id)) - self.assertEqual(rtn, (('%d,%d' % (george_id, costanza_id)).split(','), None)) - rtn = IS_IN_DB(db, 'person.id', '%(name)s', multiple=(1, 2), delimiter=',', error_message='oops')('%d,%d' % (george_id, costanza_id)) - self.assertEqual(rtn, (('%d,%d' % (george_id, costanza_id)).split(','), 'oops')) - rtn = IS_IN_DB(db, db.person.id, '%(name)s', error_message='oops').options(zero=False) - self.assertEqual(sorted(rtn), [('%d' % george_id, 'george'), ('%d' % costanza_id, 'costanza')]) - rtn = IS_IN_DB(db, db.person.id, db.person.name, error_message='oops', sort=True).options(zero=True) - self.assertEqual(rtn, [('', ''), ('%d' % costanza_id, 'costanza'), ('%d' % george_id, 'george')]) - # Test None - rtn = IS_IN_DB(db, 'person.id', '%(name)s', error_message='oops')(None) - self.assertEqual(rtn, (None, 'oops')) - rtn = IS_IN_DB(db, 'person.name', '%(name)s', error_message='oops')(None) - self.assertEqual(rtn, (None, 'oops')) - # Test using the set it made for options - vldtr = IS_IN_DB(db, 'person.name', '%(name)s', error_message='oops') - vldtr.options() - rtn = vldtr('george') - self.assertEqual(rtn, ('george', None)) - rtn = vldtr('jerry') - self.assertEqual(rtn, ('jerry', 'oops')) - vldtr = IS_IN_DB(db, 'person.name', '%(name)s', error_message='oops', multiple=True) - vldtr.options() - rtn = vldtr(['george', 'costanza']) - self.assertEqual(rtn, (['george', 'costanza'], None)) - # Test it works with self reference - db.define_table('category', - Field('parent_id', 'reference category', requires=IS_EMPTY_OR(IS_IN_DB(db, 'category.id', '%(name)s'))), - Field('name') - ) - ret = db.category.validate_and_insert(name='seinfeld') - self.assertFalse(list(ret.errors)) - ret = db.category.validate_and_insert(name='characters', parent_id=ret.id) - self.assertFalse(list(ret.errors)) - rtn = IS_IN_DB(db, 'category.id', '%(name)s')(ret.id) - self.assertEqual(rtn, (ret.id, None)) - # Test _and - vldtr = IS_IN_DB(db, 'person.name', '%(name)s', error_message='oops', _and=IS_LENGTH(maxsize=7, error_message='bad')) - rtn = vldtr('george') - self.assertEqual(rtn, ('george', None)) - rtn = vldtr('costanza') - self.assertEqual(rtn, ('costanza', 'bad')) - rtn = vldtr('jerry') - self.assertEqual(rtn, ('jerry', 'oops')) - vldtr.options() # test theset with _and - rtn = vldtr('jerry') - self.assertEqual(rtn, ('jerry', 'oops')) - # Test auto_add - rtn = IS_IN_DB(db, 'person.id', '%(name)s', error_message='oops')('jerry') - self.assertEqual(rtn, ('jerry', 'oops')) - rtn = IS_IN_DB(db, 'person.id', '%(name)s', auto_add=True)('jerry') - self.assertEqual(rtn, (3, None)) - # Test it works with reference table - db.define_table('ref_table', - Field('name'), - Field('person_id', 'reference person') - ) - ret = db.ref_table.validate_and_insert(name='test reference table') - self.assertFalse(list(ret.errors)) - ret = db.ref_table.validate_and_insert(name='test reference table', person_id=george_id) - self.assertFalse(list(ret.errors)) - rtn = IS_IN_DB(db, 'ref_table.person_id', '%(name)s')(george_id) - self.assertEqual(rtn, (george_id, None)) - # Test it works with reference table.field and keyed table - db.define_table('person_keyed', - Field('name'), - primarykey=['name']) - db.person_keyed.insert(name='george') - db.person_keyed.insert(name='costanza') - rtn = IS_IN_DB(db, 'person_keyed.name')('george') - self.assertEqual(rtn, ('george', None)) - db.define_table('ref_table_field', - Field('name'), - Field('person_name', 'reference person_keyed.name') - ) - ret = db.ref_table_field.validate_and_insert(name='test reference table.field') - self.assertFalse(list(ret.errors)) - ret = db.ref_table_field.validate_and_insert(name='test reference table.field', person_name='george') - self.assertFalse(list(ret.errors)) - vldtr = IS_IN_DB(db, 'ref_table_field.person_name', '%(name)s') - vldtr.options() - rtn = vldtr('george') - self.assertEqual(rtn, ('george', None)) - # Test it works with list:reference table - db.define_table('list_ref_table', - Field('name'), - Field('person_list', 'list:reference person')) - ret = db.list_ref_table.validate_and_insert(name='test list:reference table') - self.assertFalse(list(ret.errors)) - ret = db.list_ref_table.validate_and_insert(name='test list:reference table', person_list=[george_id,costanza_id]) - self.assertFalse(list(ret.errors)) - vldtr = IS_IN_DB(db, 'list_ref_table.person_list') - vldtr.options() - rtn = vldtr([george_id,costanza_id]) - self.assertEqual(rtn, ([george_id,costanza_id], None)) - # Test it works with list:reference table.field and keyed table - #db.define_table('list_ref_table_field', - # Field('name'), - # Field('person_list', 'list:reference person_keyed.name')) - #ret = db.list_ref_table_field.validate_and_insert(name='test list:reference table.field') - #self.assertFalse(list(ret.errors)) - #ret = db.list_ref_table_field.validate_and_insert(name='test list:reference table.field', person_list=['george','costanza']) - #self.assertFalse(list(ret.errors)) - #vldtr = IS_IN_DB(db, 'list_ref_table_field.person_list') - #vldtr.options() - #rtn = vldtr(['george','costanza']) - #self.assertEqual(rtn, (['george','costanza'], None)) - db.person.drop() - db.category.drop() - db.person_keyed.drop() - db.ref_table.drop() - db.ref_table_field.drop() - db.list_ref_table.drop() - #db.list_ref_table_field.drop() - - def test_IS_NOT_IN_DB(self): - from gluon.dal import DAL, Field - db = DAL('sqlite:memory') - db.define_table('person', Field('name'), Field('nickname')) - db.person.insert(name='george') - db.person.insert(name='costanza', nickname='T Bone') - rtn = IS_NOT_IN_DB(db, 'person.name', error_message='oops')('george') - self.assertEqual(rtn, ('george', 'oops')) - rtn = IS_NOT_IN_DB(db, 'person.name', error_message='oops', allowed_override=['george'])('george') - self.assertEqual(rtn, ('george', None)) - rtn = IS_NOT_IN_DB(db, 'person.name', error_message='oops')(' ') - self.assertEqual(rtn, (' ', 'oops')) - rtn = IS_NOT_IN_DB(db, 'person.name')('jerry') - self.assertEqual(rtn, ('jerry', None)) - rtn = IS_NOT_IN_DB(db, 'person.name')(u'jerry') - self.assertEqual(rtn, ('jerry', None)) - rtn = IS_NOT_IN_DB(db(db.person.id > 0), 'person.name')(u'jerry') - self.assertEqual(rtn, ('jerry', None)) - rtn = IS_NOT_IN_DB(db, db.person, error_message='oops')(1) - self.assertEqual(rtn, ('1', 'oops')) - vldtr = IS_NOT_IN_DB(db, 'person.name', error_message='oops') - vldtr.set_self_id({'name': 'costanza', 'nickname': 'T Bone'}) - rtn = vldtr('george') - self.assertEqual(rtn, ('george', 'oops')) - rtn = vldtr('costanza') - self.assertEqual(rtn, ('costanza', None)) - - db.person.drop() - - def test_IS_INT_IN_RANGE(self): - rtn = IS_INT_IN_RANGE(1, 5)('4') - self.assertEqual(rtn, (4, None)) - rtn = IS_INT_IN_RANGE(1, 5)(4) - self.assertEqual(rtn, (4, None)) - rtn = IS_INT_IN_RANGE(1, 5)(1) - self.assertEqual(rtn, (1, None)) - rtn = IS_INT_IN_RANGE(1, 5)(5) - self.assertEqual(rtn, (5, 'Enter an integer between 1 and 4')) - rtn = IS_INT_IN_RANGE(1, 5)(5) - self.assertEqual(rtn, (5, 'Enter an integer between 1 and 4')) - rtn = IS_INT_IN_RANGE(1, 5)(3.5) - self.assertEqual(rtn, (3.5, 'Enter an integer between 1 and 4')) - rtn = IS_INT_IN_RANGE(None, 5)('4') - self.assertEqual(rtn, (4, None)) - rtn = IS_INT_IN_RANGE(None, 5)('6') - self.assertEqual(rtn, ('6', 'Enter an integer less than or equal to 4')) - rtn = IS_INT_IN_RANGE(1, None)('4') - self.assertEqual(rtn, (4, None)) - rtn = IS_INT_IN_RANGE(1, None)('0') - self.assertEqual(rtn, ('0', 'Enter an integer greater than or equal to 1')) - rtn = IS_INT_IN_RANGE()(6) - self.assertEqual(rtn, (6, None)) - rtn = IS_INT_IN_RANGE()('abc') - self.assertEqual(rtn, ('abc', 'Enter an integer')) - - def test_IS_FLOAT_IN_RANGE(self): - # with None - rtn = IS_FLOAT_IN_RANGE(1, 5)(None) - self.assertEqual(rtn, (None, 'Enter a number between 1 and 5')) - rtn = IS_FLOAT_IN_RANGE(1, 5)('4') - self.assertEqual(rtn, (4.0, None)) - rtn = IS_FLOAT_IN_RANGE(1, 5)(4) - self.assertEqual(rtn, (4.0, None)) - rtn = IS_FLOAT_IN_RANGE(1, 5)(1) - self.assertEqual(rtn, (1.0, None)) - rtn = IS_FLOAT_IN_RANGE(1, 5)(5.25) - self.assertEqual(rtn, (5.25, 'Enter a number between 1 and 5')) - rtn = IS_FLOAT_IN_RANGE(1, 5)(6.0) - self.assertEqual(rtn, (6.0, 'Enter a number between 1 and 5')) - rtn = IS_FLOAT_IN_RANGE(1, 5)(3.5) - self.assertEqual(rtn, (3.5, None)) - rtn = IS_FLOAT_IN_RANGE(1, None)(3.5) - self.assertEqual(rtn, (3.5, None)) - rtn = IS_FLOAT_IN_RANGE(None, 5)(3.5) - self.assertEqual(rtn, (3.5, None)) - rtn = IS_FLOAT_IN_RANGE(1, None)(0.5) - self.assertEqual(rtn, (0.5, 'Enter a number greater than or equal to 1')) - rtn = IS_FLOAT_IN_RANGE(None, 5)(6.5) - self.assertEqual(rtn, (6.5, 'Enter a number less than or equal to 5')) - rtn = IS_FLOAT_IN_RANGE()(6.5) - self.assertEqual(rtn, (6.5, None)) - rtn = IS_FLOAT_IN_RANGE()('abc') - self.assertEqual(rtn, ('abc', 'Enter a number')) - rtn = IS_FLOAT_IN_RANGE()('6,5') - self.assertEqual(rtn, ('6,5', 'Enter a number')) - rtn = IS_FLOAT_IN_RANGE(dot=',')('6.5') - self.assertEqual(rtn, (6.5, None)) - # With .formatter(None) - rtn = IS_FLOAT_IN_RANGE(dot=',').formatter(None) - self.assertEqual(rtn, None) - rtn = IS_FLOAT_IN_RANGE(dot=',').formatter(0.25) - self.assertEqual(rtn, '0,25') - # To trigger str2dec "if not '.' in s:" line - rtn = IS_FLOAT_IN_RANGE(dot=',').formatter(1) - self.assertEqual(rtn, '1,00') - - def test_IS_DECIMAL_IN_RANGE(self): - # with None - rtn = IS_DECIMAL_IN_RANGE(1, 5)(None) - self.assertEqual(rtn, (None, 'Enter a number between 1 and 5')) - rtn = IS_DECIMAL_IN_RANGE(1, 5)('4') - self.assertEqual(rtn, (decimal.Decimal('4'), None)) - rtn = IS_DECIMAL_IN_RANGE(1, 5)(4) - self.assertEqual(rtn, (decimal.Decimal('4'), None)) - rtn = IS_DECIMAL_IN_RANGE(1, 5)(1) - self.assertEqual(rtn, (decimal.Decimal('1'), None)) - rtn = IS_DECIMAL_IN_RANGE(1, 5)(5.25) - self.assertEqual(rtn, (5.25, 'Enter a number between 1 and 5')) - rtn = IS_DECIMAL_IN_RANGE(5.25, 6)(5.25) - self.assertEqual(rtn, (decimal.Decimal('5.25'), None)) - rtn = IS_DECIMAL_IN_RANGE(5.25, 6)('5.25') - self.assertEqual(rtn, (decimal.Decimal('5.25'), None)) - rtn = IS_DECIMAL_IN_RANGE(1, 5)(6.0) - self.assertEqual(rtn, (6.0, 'Enter a number between 1 and 5')) - rtn = IS_DECIMAL_IN_RANGE(1, 5)(3.5) - self.assertEqual(rtn, (decimal.Decimal('3.5'), None)) - rtn = IS_DECIMAL_IN_RANGE(1.5, 5.5)(3.5) - self.assertEqual(rtn, (decimal.Decimal('3.5'), None)) - rtn = IS_DECIMAL_IN_RANGE(1.5, 5.5)(6.5) - self.assertEqual(rtn, (6.5, 'Enter a number between 1.5 and 5.5')) - rtn = IS_DECIMAL_IN_RANGE(1.5, None)(6.5) - self.assertEqual(rtn, (decimal.Decimal('6.5'), None)) - rtn = IS_DECIMAL_IN_RANGE(1.5, None)(0.5) - self.assertEqual(rtn, (0.5, 'Enter a number greater than or equal to 1.5')) - rtn = IS_DECIMAL_IN_RANGE(None, 5.5)(4.5) - self.assertEqual(rtn, (decimal.Decimal('4.5'), None)) - rtn = IS_DECIMAL_IN_RANGE(None, 5.5)(6.5) - self.assertEqual(rtn, (6.5, 'Enter a number less than or equal to 5.5')) - rtn = IS_DECIMAL_IN_RANGE()(6.5) - self.assertEqual(rtn, (decimal.Decimal('6.5'), None)) - rtn = IS_DECIMAL_IN_RANGE(0, 99)(123.123) - self.assertEqual(rtn, (123.123, 'Enter a number between 0 and 99')) - rtn = IS_DECIMAL_IN_RANGE(0, 99)('123.123') - self.assertEqual(rtn, ('123.123', 'Enter a number between 0 and 99')) - rtn = IS_DECIMAL_IN_RANGE(0, 99)('12.34') - self.assertEqual(rtn, (decimal.Decimal('12.34'), None)) - rtn = IS_DECIMAL_IN_RANGE()('abc') - self.assertEqual(rtn, ('abc', 'Enter a number')) - rtn = IS_DECIMAL_IN_RANGE()('6,5') - self.assertEqual(rtn, ('6,5', 'Enter a number')) - rtn = IS_DECIMAL_IN_RANGE(dot=',')('6.5') - self.assertEqual(rtn, (decimal.Decimal('6.5'), None)) - rtn = IS_DECIMAL_IN_RANGE(1, 5)(decimal.Decimal('4')) - self.assertEqual(rtn, (decimal.Decimal('4'), None)) - # With .formatter(None) - rtn = IS_DECIMAL_IN_RANGE(dot=',').formatter(None) - self.assertEqual(rtn, None) - rtn = IS_DECIMAL_IN_RANGE(dot=',').formatter(0.25) - self.assertEqual(rtn, '0,25') - - def test_IS_NOT_EMPTY(self): - rtn = IS_NOT_EMPTY()(1) - self.assertEqual(rtn, (1, None)) - rtn = IS_NOT_EMPTY()(0) - self.assertEqual(rtn, (0, None)) - rtn = IS_NOT_EMPTY()('x') - self.assertEqual(rtn, ('x', None)) - rtn = IS_NOT_EMPTY()(' x ') - self.assertEqual(rtn, (' x ', None)) - rtn = IS_NOT_EMPTY()(None) - self.assertEqual(rtn, (None, 'Enter a value')) - rtn = IS_NOT_EMPTY()('') - self.assertEqual(rtn, ('', 'Enter a value')) - rtn = IS_NOT_EMPTY()(b'') - self.assertEqual(rtn, (b'', 'Enter a value')) - rtn = IS_NOT_EMPTY()(' ') - self.assertEqual(rtn, (' ', 'Enter a value')) - rtn = IS_NOT_EMPTY()(' \n\t') - self.assertEqual(rtn, (' \n\t', 'Enter a value')) - rtn = IS_NOT_EMPTY()([]) - self.assertEqual(rtn, ([], 'Enter a value')) - rtn = IS_NOT_EMPTY(empty_regex='def')('def') - self.assertEqual(rtn, ('def', 'Enter a value')) - rtn = IS_NOT_EMPTY(empty_regex='de[fg]')('deg') - self.assertEqual(rtn, ('deg', 'Enter a value')) - rtn = IS_NOT_EMPTY(empty_regex='def')('abc') - self.assertEqual(rtn, ('abc', None)) - - def test_IS_ALPHANUMERIC(self): - rtn = IS_ALPHANUMERIC()('1') - self.assertEqual(rtn, ('1', None)) - rtn = IS_ALPHANUMERIC()('') - self.assertEqual(rtn, ('', None)) - rtn = IS_ALPHANUMERIC()('A_a') - self.assertEqual(rtn, ('A_a', None)) - rtn = IS_ALPHANUMERIC()('!') - self.assertEqual(rtn, ('!', 'Enter only letters, numbers, and underscore')) - - def test_IS_EMAIL(self): - rtn = IS_EMAIL()('a@b.com') - self.assertEqual(rtn, ('a@b.com', None)) - rtn = IS_EMAIL()('abc@def.com') - self.assertEqual(rtn, ('abc@def.com', None)) - rtn = IS_EMAIL()('abc@3def.com') - self.assertEqual(rtn, ('abc@3def.com', None)) - rtn = IS_EMAIL()('abc@def.us') - self.assertEqual(rtn, ('abc@def.us', None)) - rtn = IS_EMAIL()('abc@d_-f.us') - self.assertEqual(rtn, ('abc@d_-f.us', None)) - rtn = IS_EMAIL()('@def.com') # missing name - self.assertEqual(rtn, ('@def.com', 'Enter a valid email address')) - rtn = IS_EMAIL()('"abc@def".com') # quoted name - self.assertEqual(rtn, ('"abc@def".com', 'Enter a valid email address')) - rtn = IS_EMAIL()('abc+def.com') # no @ - self.assertEqual(rtn, ('abc+def.com', 'Enter a valid email address')) - rtn = IS_EMAIL()('abc@def.x') # one-char TLD - self.assertEqual(rtn, ('abc@def.x', 'Enter a valid email address')) - rtn = IS_EMAIL()('abc@def.12') # numeric TLD - self.assertEqual(rtn, ('abc@def.12', 'Enter a valid email address')) - rtn = IS_EMAIL()('abc@def..com') # double-dot in domain - self.assertEqual(rtn, ('abc@def..com', 'Enter a valid email address')) - rtn = IS_EMAIL()('abc@.def.com') # dot starts domain - self.assertEqual(rtn, ('abc@.def.com', 'Enter a valid email address')) - rtn = IS_EMAIL()('abc@def.c_m') # underscore in TLD - self.assertEqual(rtn, ('abc@def.c_m', 'Enter a valid email address')) - rtn = IS_EMAIL()('NotAnEmail') # missing @ - self.assertEqual(rtn, ('NotAnEmail', 'Enter a valid email address')) - rtn = IS_EMAIL()('abc@NotAnEmail') # missing TLD - self.assertEqual(rtn, ('abc@NotAnEmail', 'Enter a valid email address')) - rtn = IS_EMAIL()('customer/department@example.com') - self.assertEqual(rtn, ('customer/department@example.com', None)) - rtn = IS_EMAIL()('$A12345@example.com') - self.assertEqual(rtn, ('$A12345@example.com', None)) - rtn = IS_EMAIL()('!def!xyz%abc@example.com') - self.assertEqual(rtn, ('!def!xyz%abc@example.com', None)) - rtn = IS_EMAIL()('_Yosemite.Sam@example.com') - self.assertEqual(rtn, ('_Yosemite.Sam@example.com', None)) - rtn = IS_EMAIL()('~@example.com') - self.assertEqual(rtn, ('~@example.com', None)) - rtn = IS_EMAIL()('.wooly@example.com') # dot starts name - self.assertEqual(rtn, ('.wooly@example.com', 'Enter a valid email address')) - rtn = IS_EMAIL()('wo..oly@example.com') # adjacent dots in name - self.assertEqual(rtn, ('wo..oly@example.com', 'Enter a valid email address')) - rtn = IS_EMAIL()('pootietang.@example.com') # dot ends name - self.assertEqual(rtn, ('pootietang.@example.com', 'Enter a valid email address')) - rtn = IS_EMAIL()('.@example.com') # name is bare dot - self.assertEqual(rtn, ('.@example.com', 'Enter a valid email address')) - rtn = IS_EMAIL()('Ima.Fool@example.com') - self.assertEqual(rtn, ('Ima.Fool@example.com', None)) - rtn = IS_EMAIL()('Ima Fool@example.com') # space in name - self.assertEqual(rtn, ('Ima Fool@example.com', 'Enter a valid email address')) - rtn = IS_EMAIL()('localguy@localhost') # localhost as domain - self.assertEqual(rtn, ('localguy@localhost', None)) - # test for banned - rtn = IS_EMAIL(banned='^.*\.com(|\..*)$')('localguy@localhost') # localhost as domain - self.assertEqual(rtn, ('localguy@localhost', None)) - rtn = IS_EMAIL(banned='^.*\.com(|\..*)$')('abc@example.com') - self.assertEqual(rtn, ('abc@example.com', 'Enter a valid email address')) - # test for forced - rtn = IS_EMAIL(forced='^.*\.edu(|\..*)$')('localguy@localhost') - self.assertEqual(rtn, ('localguy@localhost', 'Enter a valid email address')) - rtn = IS_EMAIL(forced='^.*\.edu(|\..*)$')('localguy@example.edu') - self.assertEqual(rtn, ('localguy@example.edu', None)) - # test for not a string at all - rtn = IS_EMAIL(error_message='oops')(42) - self.assertEqual(rtn, (42, 'oops')) - - # test for Internationalized Domain Names, see https://docs.python.org/2/library/codecs.html#module-encodings.idna - rtn = IS_EMAIL()('web2py@Alliancefrançaise.nu') - self.assertEqual(rtn, ('web2py@Alliancefrançaise.nu', None)) - - - def test_IS_LIST_OF_EMAILS(self): - emails = ['localguy@localhost', '_Yosemite.Sam@example.com'] - rtn = IS_LIST_OF_EMAILS()(','.join(emails)) - self.assertEqual(rtn, (','.join(emails), None)) - rtn = IS_LIST_OF_EMAILS()(';'.join(emails)) - self.assertEqual(rtn, (';'.join(emails), None)) - rtn = IS_LIST_OF_EMAILS()(' '.join(emails)) - self.assertEqual(rtn, (' '.join(emails), None)) - emails.append('a') - rtn = IS_LIST_OF_EMAILS()(';'.join(emails)) - self.assertEqual(rtn, ('localguy@localhost;_Yosemite.Sam@example.com;a', 'Invalid emails: a')) - rtn = IS_LIST_OF_EMAILS().formatter(['test@example.com', 'dude@example.com']) - self.assertEqual(rtn, 'test@example.com, dude@example.com') - - def test_IS_URL(self): - rtn = IS_URL()('http://example.com') - self.assertEqual(rtn, ('http://example.com', None)) - rtn = IS_URL(error_message='oops')('http://example,com') - self.assertEqual(rtn, ('http://example,com', 'oops')) - rtn = IS_URL(error_message='oops')('http://www.example.com:8800/a/b/c/d/e/f/g/h') - self.assertEqual(rtn, ('http://www.example.com:8800/a/b/c/d/e/f/g/h', None)) - rtn = IS_URL(error_message='oops', prepend_scheme='http')('example.com') - self.assertEqual(rtn, ('http://example.com', None)) - rtn = IS_URL()('http://example.com?q=george&p=22') - self.assertEqual(rtn, ('http://example.com?q=george&p=22', None)) - rtn = IS_URL(mode='generic', prepend_scheme=None)('example.com') - self.assertEqual(rtn, ('example.com', None)) - - def test_IS_TIME(self): - rtn = IS_TIME()('21:30') - self.assertEqual(rtn, (datetime.time(21, 30), None)) - rtn = IS_TIME()('21-30') - self.assertEqual(rtn, (datetime.time(21, 30), None)) - rtn = IS_TIME()('21.30') - self.assertEqual(rtn, (datetime.time(21, 30), None)) - rtn = IS_TIME()('21:30:59') - self.assertEqual(rtn, (datetime.time(21, 30, 59), None)) - rtn = IS_TIME()('5:30') - self.assertEqual(rtn, (datetime.time(5, 30), None)) - rtn = IS_TIME()('5:30 am') - self.assertEqual(rtn, (datetime.time(5, 30), None)) - rtn = IS_TIME()('5:30 pm') - self.assertEqual(rtn, (datetime.time(17, 30), None)) - rtn = IS_TIME()('5:30 whatever') - self.assertEqual(rtn, ('5:30 whatever', 'Enter time as hh:mm:ss (seconds, am, pm optional)')) - rtn = IS_TIME()('5:30 20') - self.assertEqual(rtn, ('5:30 20', 'Enter time as hh:mm:ss (seconds, am, pm optional)')) - rtn = IS_TIME()('24:30') - self.assertEqual(rtn, ('24:30', 'Enter time as hh:mm:ss (seconds, am, pm optional)')) - rtn = IS_TIME()('21:60') - self.assertEqual(rtn, ('21:60', 'Enter time as hh:mm:ss (seconds, am, pm optional)')) - rtn = IS_TIME()('21:30::') - self.assertEqual(rtn, ('21:30::', 'Enter time as hh:mm:ss (seconds, am, pm optional)')) - rtn = IS_TIME()('') - self.assertEqual(rtn, ('', 'Enter time as hh:mm:ss (seconds, am, pm optional)')) - - def test_IS_DATE(self): - v = IS_DATE(format="%m/%d/%Y", error_message="oops") - rtn = v('03/03/2008') - self.assertEqual(rtn, (datetime.date(2008, 3, 3), None)) - rtn = v('31/03/2008') - self.assertEqual(rtn, ('31/03/2008', 'oops')) - rtn = IS_DATE(format="%m/%d/%Y", error_message="oops").formatter(datetime.date(1834, 12, 14)) - self.assertEqual(rtn, '12/14/1834') - - def test_IS_DATETIME(self): - v = IS_DATETIME(format="%m/%d/%Y %H:%M", error_message="oops") - rtn = v('03/03/2008 12:40') - self.assertEqual(rtn, (datetime.datetime(2008, 3, 3, 12, 40), None)) - rtn = v('31/03/2008 29:40') - self.assertEqual(rtn, ('31/03/2008 29:40', 'oops')) - # Test timezone is removed and value is properly converted - # - # https://github.com/web2py/web2py/issues/1094 - - class DummyTimezone(datetime.tzinfo): - - ONE = datetime.timedelta(hours=1) - - def utcoffset(self, dt): - return DummyTimezone.ONE - - def tzname(self, dt): - return "UTC+1" - - def dst(self, dt): - return DummyTimezone.ONE - - def localize(self, dt, is_dst=False): - return dt.replace(tzinfo=self) - v = IS_DATETIME(format="%Y-%m-%d %H:%M", error_message="oops", timezone=DummyTimezone()) - rtn = v('1982-12-14 08:00') - self.assertEqual(rtn, (datetime.datetime(1982, 12, 14, 7, 0), None)) - - def test_IS_DATE_IN_RANGE(self): - v = IS_DATE_IN_RANGE(minimum=datetime.date(2008, 1, 1), - maximum=datetime.date(2009, 12, 31), - format="%m/%d/%Y", error_message="oops") - - rtn = v('03/03/2008') - self.assertEqual(rtn, (datetime.date(2008, 3, 3), None)) - rtn = v('03/03/2010') - self.assertEqual(rtn, ('03/03/2010', 'oops')) - rtn = v(datetime.date(2008, 3, 3)) - self.assertEqual(rtn, (datetime.date(2008, 3, 3), None)) - rtn = v(datetime.date(2010, 3, 3)) - self.assertEqual(rtn, (datetime.date(2010, 3, 3), 'oops')) - v = IS_DATE_IN_RANGE(maximum=datetime.date(2009, 12, 31), - format="%m/%d/%Y") - rtn = v('03/03/2010') - self.assertEqual(rtn, ('03/03/2010', 'Enter date on or before 12/31/2009')) - v = IS_DATE_IN_RANGE(minimum=datetime.date(2008, 1, 1), - format="%m/%d/%Y") - rtn = v('03/03/2007') - self.assertEqual(rtn, ('03/03/2007', 'Enter date on or after 01/01/2008')) - v = IS_DATE_IN_RANGE(minimum=datetime.date(2008, 1, 1), - maximum=datetime.date(2009, 12, 31), - format="%m/%d/%Y") - rtn = v('03/03/2007') - self.assertEqual(rtn, ('03/03/2007', 'Enter date in range 01/01/2008 12/31/2009')) - - def test_IS_DATETIME_IN_RANGE(self): - v = IS_DATETIME_IN_RANGE( - minimum=datetime.datetime(2008, 1, 1, 12, 20), - maximum=datetime.datetime(2009, 12, 31, 12, 20), - format="%m/%d/%Y %H:%M", error_message="oops") - rtn = v('03/03/2008 12:40') - self.assertEqual(rtn, (datetime.datetime(2008, 3, 3, 12, 40), None)) - rtn = v('03/03/2010 10:34') - self.assertEqual(rtn, ('03/03/2010 10:34', 'oops')) - rtn = v(datetime.datetime(2008, 3, 3, 0, 0)) - self.assertEqual(rtn, (datetime.datetime(2008, 3, 3, 0, 0), None)) - rtn = v(datetime.datetime(2010, 3, 3, 0, 0)) - self.assertEqual(rtn, (datetime.datetime(2010, 3, 3, 0, 0), 'oops')) - v = IS_DATETIME_IN_RANGE(maximum=datetime.datetime(2009, 12, 31, 12, 20), - format='%m/%d/%Y %H:%M:%S') - rtn = v('03/03/2010 12:20:00') - self.assertEqual(rtn, ('03/03/2010 12:20:00', 'Enter date and time on or before 12/31/2009 12:20:00')) - v = IS_DATETIME_IN_RANGE(minimum=datetime.datetime(2008, 1, 1, 12, 20), - format='%m/%d/%Y %H:%M:%S') - rtn = v('03/03/2007 12:20:00') - self.assertEqual(rtn, ('03/03/2007 12:20:00', 'Enter date and time on or after 01/01/2008 12:20:00')) - v = IS_DATETIME_IN_RANGE(minimum=datetime.datetime(2008, 1, 1, 12, 20), - maximum=datetime.datetime(2009, 12, 31, 12, 20), - format='%m/%d/%Y %H:%M:%S') - rtn = v('03/03/2007 12:20:00') - self.assertEqual(rtn, ('03/03/2007 12:20:00', 'Enter date and time in range 01/01/2008 12:20:00 12/31/2009 12:20:00')) - v = IS_DATETIME_IN_RANGE(maximum=datetime.datetime(2009, 12, 31, 12, 20), - format='%Y-%m-%d %H:%M:%S', error_message='oops') - rtn = v('clearly not a date') - self.assertEqual(rtn, ('clearly not a date', 'oops')) - - def test_IS_LIST_OF(self): - values = [0, 1, 2, 3, 4] - rtn = IS_LIST_OF(IS_INT_IN_RANGE(0, 10))(values) - self.assertEqual(rtn, (values, None)) - values.append(11) - rtn = IS_LIST_OF(IS_INT_IN_RANGE(0, 10))(values) - self.assertEqual(rtn, (values, 'Enter an integer between 0 and 9')) - rtn = IS_LIST_OF(IS_INT_IN_RANGE(0, 10))(1) - self.assertEqual(rtn, ([1], None)) - rtn = IS_LIST_OF(IS_INT_IN_RANGE(0, 10), minimum=10)([1, 2]) - self.assertEqual(rtn, ([1, 2], 'Minimum length is 10')) - rtn = IS_LIST_OF(IS_INT_IN_RANGE(0, 10), maximum=2)([1, 2, 3]) - self.assertEqual(rtn, ([1, 2, 3], 'Maximum length is 2')) - # regression test for issue 742 - rtn = IS_LIST_OF(minimum=1)('') - self.assertEqual(rtn, ([], 'Minimum length is 1')) - - def test_IS_LOWER(self): - rtn = IS_LOWER()('ABC') - self.assertEqual(rtn, ('abc', None)) - rtn = IS_LOWER()(b'ABC') - self.assertEqual(rtn, (b'abc', None)) - rtn = IS_LOWER()('Ñ') - self.assertEqual(rtn, ('ñ', None)) - - def test_IS_UPPER(self): - rtn = IS_UPPER()('abc') - self.assertEqual(rtn, ('ABC', None)) - rtn = IS_UPPER()(b'abc') - self.assertEqual(rtn, (b'ABC', None)) - rtn = IS_UPPER()('ñ') - self.assertEqual(rtn, ('Ñ', None)) - - def test_IS_SLUG(self): - rtn = IS_SLUG()('abc123') - self.assertEqual(rtn, ('abc123', None)) - rtn = IS_SLUG()('ABC123') - self.assertEqual(rtn, ('abc123', None)) - rtn = IS_SLUG()('abc-123') - self.assertEqual(rtn, ('abc-123', None)) - rtn = IS_SLUG()('abc--123') - self.assertEqual(rtn, ('abc-123', None)) - rtn = IS_SLUG()('abc 123') - self.assertEqual(rtn, ('abc-123', None)) - rtn = IS_SLUG()('abc\t_123') - self.assertEqual(rtn, ('abc-123', None)) - rtn = IS_SLUG()('-abc-') - self.assertEqual(rtn, ('abc', None)) - rtn = IS_SLUG()('--a--b--_ -c--') - self.assertEqual(rtn, ('a-b-c', None)) - rtn = IS_SLUG()('abc&123') - self.assertEqual(rtn, ('abc123', None)) - rtn = IS_SLUG()('abc&123&def') - self.assertEqual(rtn, ('abc123def', None)) - rtn = IS_SLUG()('ñ') - self.assertEqual(rtn, ('n', None)) - rtn = IS_SLUG(maxlen=4)('abc123') - self.assertEqual(rtn, ('abc1', None)) - rtn = IS_SLUG()('abc_123') - self.assertEqual(rtn, ('abc-123', None)) - rtn = IS_SLUG(keep_underscores=False)('abc_123') - self.assertEqual(rtn, ('abc-123', None)) - rtn = IS_SLUG(keep_underscores=True)('abc_123') - self.assertEqual(rtn, ('abc_123', None)) - rtn = IS_SLUG(check=False)('abc') - self.assertEqual(rtn, ('abc', None)) - rtn = IS_SLUG(check=True)('abc') - self.assertEqual(rtn, ('abc', None)) - rtn = IS_SLUG(check=False)('a bc') - self.assertEqual(rtn, ('a-bc', None)) - rtn = IS_SLUG(check=True)('a bc') - self.assertEqual(rtn, ('a bc', 'Must be slug')) - - def test_ANY_OF(self): - rtn = ANY_OF([IS_EMAIL(), IS_ALPHANUMERIC()])('a@b.co') - self.assertEqual(rtn, ('a@b.co', None)) - rtn = ANY_OF([IS_EMAIL(), IS_ALPHANUMERIC()])('abco') - self.assertEqual(rtn, ('abco', None)) - rtn = ANY_OF([IS_EMAIL(), IS_ALPHANUMERIC()])('@ab.co') - self.assertEqual(rtn, ('@ab.co', 'Enter only letters, numbers, and underscore')) - rtn = ANY_OF([IS_ALPHANUMERIC(), IS_EMAIL()])('@ab.co') - self.assertEqual(rtn, ('@ab.co', 'Enter a valid email address')) - rtn = ANY_OF([IS_DATE(), IS_EMAIL()])('a@b.co') - self.assertEqual(rtn, ('a@b.co', None)) - rtn = ANY_OF([IS_DATE(), IS_EMAIL()])('1982-12-14') - self.assertEqual(rtn, (datetime.date(1982, 12, 14), None)) - rtn = ANY_OF([IS_DATE(format='%m/%d/%Y'), IS_EMAIL()]).formatter(datetime.date(1834, 12, 14)) - self.assertEqual(rtn, '12/14/1834') - - def test_IS_EMPTY_OR(self): - rtn = IS_EMPTY_OR(IS_EMAIL())('abc@def.com') - self.assertEqual(rtn, ('abc@def.com', None)) - rtn = IS_EMPTY_OR(IS_EMAIL())(' ') - self.assertEqual(rtn, (None, None)) - rtn = IS_EMPTY_OR(IS_EMAIL(), null='abc')(' ') - self.assertEqual(rtn, ('abc', None)) - rtn = IS_EMPTY_OR(IS_EMAIL(), null='abc', empty_regex='def')('def') - self.assertEqual(rtn, ('abc', None)) - rtn = IS_EMPTY_OR(IS_EMAIL())('abc') - self.assertEqual(rtn, ('abc', 'Enter a valid email address')) - rtn = IS_EMPTY_OR(IS_EMAIL())(' abc ') - self.assertEqual(rtn, (' abc ', 'Enter a valid email address')) - rtn = IS_EMPTY_OR(IS_IN_SET([('id1', 'first label'), ('id2', 'second label')], zero='zero')).options(zero=False) - self.assertEqual(rtn, [('', ''), ('id1', 'first label'), ('id2', 'second label')]) - rtn = IS_EMPTY_OR(IS_IN_SET([('id1', 'first label'), ('id2', 'second label')], zero='zero')).options() - self.assertEqual(rtn, [('', 'zero'), ('id1', 'first label'), ('id2', 'second label')]) - rtn = IS_EMPTY_OR((IS_LOWER(), IS_EMAIL()))('AAA') - self.assertEqual(rtn, ('aaa', 'Enter a valid email address')) - rtn = IS_EMPTY_OR([IS_LOWER(), IS_EMAIL()])('AAA') - self.assertEqual(rtn, ('aaa', 'Enter a valid email address')) - - def test_CLEANUP(self): - rtn = CLEANUP()('helloò') - self.assertEqual(rtn, ('hello', None)) - - def test_CRYPT(self): - rtn = str(CRYPT(digest_alg='md5', salt=True)('test')[0]) - self.myassertRegex(rtn, r'^md5\$.{16}\$.{32}$') - rtn = str(CRYPT(digest_alg='sha1', salt=True)('test')[0]) - self.myassertRegex(rtn, r'^sha1\$.{16}\$.{40}$') - rtn = str(CRYPT(digest_alg='sha256', salt=True)('test')[0]) - self.myassertRegex(rtn, r'^sha256\$.{16}\$.{64}$') - rtn = str(CRYPT(digest_alg='sha384', salt=True)('test')[0]) - self.myassertRegex(rtn, r'^sha384\$.{16}\$.{96}$') - rtn = str(CRYPT(digest_alg='sha512', salt=True)('test')[0]) - self.myassertRegex(rtn, r'^sha512\$.{16}\$.{128}$') - alg = 'pbkdf2(1000,20,sha512)' - rtn = str(CRYPT(digest_alg=alg, salt=True)('test')[0]) - self.myassertRegex(rtn, r'^pbkdf2\(1000,20,sha512\)\$.{16}\$.{40}$') - rtn = str(CRYPT(digest_alg='md5', key='mykey', salt=True)('test')[0]) - self.myassertRegex(rtn, r'^md5\$.{16}\$.{32}$') - a = str(CRYPT(digest_alg='sha1', salt=False)('test')[0]) - self.assertEqual(CRYPT(digest_alg='sha1', salt=False)('test')[0], a) - self.assertEqual(CRYPT(digest_alg='sha1', salt=False)('test')[0], a[6:]) - self.assertEqual(CRYPT(digest_alg='md5', salt=False)('test')[0], a) - self.assertEqual(CRYPT(digest_alg='md5', salt=False)('test')[0], a[6:]) - - def test_IS_STRONG(self): - rtn = IS_STRONG(es=True)('Abcd1234') - self.assertEqual(rtn, ('Abcd1234', - 'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|')) - rtn = IS_STRONG(es=True)('Abcd1234!') - self.assertEqual(rtn, ('Abcd1234!', None)) - rtn = IS_STRONG(es=True, entropy=1)('a') - self.assertEqual(rtn, ('a', None)) - rtn = IS_STRONG(es=True, entropy=1, min=2)('a') - self.assertEqual(rtn, ('a', 'Minimum length is 2')) - rtn = IS_STRONG(es=True, entropy=100)('abc123') - self.assertEqual(rtn, ('abc123', 'Entropy (32.35) less than required (100)')) - rtn = IS_STRONG(es=True, entropy=100)('and') - self.assertEqual(rtn, ('and', 'Entropy (14.57) less than required (100)')) - rtn = IS_STRONG(es=True, entropy=100)('aaa') - self.assertEqual(rtn, ('aaa', 'Entropy (14.42) less than required (100)')) - rtn = IS_STRONG(es=True, entropy=100)('a1d') - self.assertEqual(rtn, ('a1d', 'Entropy (15.97) less than required (100)')) - rtn = IS_STRONG(es=True, entropy=100)('añd') - if PY2: - self.assertEqual(rtn, ('a\xc3\xb1d', 'Entropy (18.13) less than required (100)')) - else: - self.assertEqual(rtn, ('añd', 'Entropy (18.13) less than required (100)')) - rtn = IS_STRONG()('********') - self.assertEqual(rtn, ('********', None)) - rtn = IS_STRONG(es=True, max=4)('abcde') - self.assertEqual(rtn, - ('abcde', - '|'.join(['Minimum length is 8', - 'Maximum length is 4', - 'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|', - 'Must include at least 1 uppercase', - 'Must include at least 1 number'])) - ) - rtn = IS_STRONG(es=True)('abcde') - self.assertEqual(rtn, - ('abcde', - '|'.join(['Minimum length is 8', - 'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|', - 'Must include at least 1 uppercase', - 'Must include at least 1 number'])) - ) - rtn = IS_STRONG(upper=0, lower=0, number=0, es=True)('Abcde1') - self.assertEqual(rtn, - ('Abcde1', - '|'.join(['Minimum length is 8', - 'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|', - 'May not include any uppercase letters', - 'May not include any lowercase letters', - 'May not include any numbers'])) - ) - - def test_IS_IMAGE(self): - class DummyImageFile(object): - - def __init__(self, filename, ext, width, height): - from io import BytesIO - import struct - self.filename = filename + '.' + ext - self.file = BytesIO() - if ext == 'bmp': - self.file.write(b'BM') - self.file.write(b' ' * 16) - self.file.write(struct.pack(' 2: raise HTTP(400, 'Invalid JWT header, token contains spaces') token = parts[1] @@ -1378,6 +1379,7 @@ class Auth(AuthAPI): login_after_password_change=True, login_after_registration=False, login_captcha=None, + login_specify_error=False, long_expiration=3600 * 30 * 24, # one month mailer=None, manager_actions={}, @@ -1752,7 +1754,7 @@ class Auth(AuthAPI): # _next variable in the request. if next: parts = next.split('/') - if ':' not in parts[0]: + if ':' not in parts[0] and parts[:2] != ['', '']: return next elif len(parts) > 2 and parts[0].endswith(':') and parts[1:3] == ['', host]: return next @@ -2242,16 +2244,16 @@ class Auth(AuthAPI): if basic_auth_realm: if callable(basic_auth_realm): basic_auth_realm = basic_auth_realm() - elif isinstance(basic_auth_realm, (unicode, str)): - basic_realm = unicode(basic_auth_realm) # Warning python 3.5 does not have method unicod + elif isinstance(basic_auth_realm, string_types): + basic_realm = to_unicode(basic_auth_realm) elif basic_auth_realm is True: - basic_realm = u'' + current.request.application - http_401 = HTTP(401, u'Not Authorized', **{'WWW-Authenticate': u'Basic realm="' + basic_realm + '"'}) + basic_realm = '' + current.request.application + http_401 = HTTP(401, 'Not Authorized', **{'WWW-Authenticate': 'Basic realm="' + basic_realm + '"'}) if not basic or not basic[:6].lower() == 'basic ': if basic_auth_realm: raise http_401 return (True, False, False) - (username, sep, password) = base64.b64decode(basic[6:]).partition(':') + (username, sep, password) = base64.b64decode(basic[6:]).partition(b':') is_valid_user = sep and self.login_bare(username, password) if not is_valid_user and basic_auth_realm: raise http_401 @@ -2259,9 +2261,9 @@ class Auth(AuthAPI): def _get_login_settings(self): table_user = self.table_user() - userfield = self.settings.login_userfield or 'username' \ + userfield = self.settings.login_userfield or ('username' \ if self.settings.login_userfield or 'username' \ - in table_user.fields else 'email' + in table_user.fields else 'email') passfield = self.settings.password_field return Storage({'table_user': table_user, 'userfield': userfield, @@ -2567,6 +2569,8 @@ class Auth(AuthAPI): settings.formstyle, 'captcha__row') accepted_form = False + specific_error = self.messages.invalid_user + if form.accepts(request, session if self.csrf_prevention else None, formname='login', dbio=False, onvalidation=onvalidation, @@ -2582,6 +2586,7 @@ class Auth(AuthAPI): user = table_user(**{username: entered_username}) if user: # user in db, check if registration pending or disabled + specific_error = self.messages.invalid_password temp_user = user if (temp_user.registration_key or '').startswith('pending'): response.flash = self.messages.registration_pending @@ -2631,11 +2636,11 @@ class Auth(AuthAPI): self.log_event(self.messages['login_failed_log'], request.post_vars) # invalid login - session.flash = self.messages.invalid_login + session.flash = specific_error if self.settings.login_specify_error else self.messages.invalid_login callback(onfail, None) - redirect( - self.url(args=request.args, vars=request.get_vars), - client_side=settings.client_side) + if 'password' in request.post_vars: + del request.post_vars['password'] + redirect(self.url(args=request.args, vars=request.vars),client_side=settings.client_side) else: # use a central authentication server cas = settings.login_form @@ -3447,7 +3452,8 @@ class Auth(AuthAPI): if log is DEFAULT: log = self.messages['reset_password_log'] userfield = self.settings.login_userfield or 'username' \ - if 'username' in table_user.fields else 'email' + if self.settings.login_userfield or 'username' \ + in table_user.fields else 'email' if userfield == 'email': table_user.email.requires = [ IS_EMAIL(error_message=self.messages.invalid_email), @@ -3455,7 +3461,7 @@ class Auth(AuthAPI): error_message=self.messages.invalid_email)] if not self.settings.email_case_sensitive: table_user.email.requires.insert(0, IS_LOWER()) - else: + elif userfield == 'username': table_user.username.requires = [ IS_IN_DB(self.db, table_user.username, error_message=self.messages.invalid_username)] @@ -3569,7 +3575,7 @@ class Auth(AuthAPI): requires = table_user[passfield].requires if not isinstance(requires, (list, tuple)): requires = [requires] - requires = list(filter(lambda t: isinstance(t, CRYPT), requires)) + requires = [t for t in requires if isinstance(t, CRYPT)] if requires: requires[0] = CRYPT(**requires[0].__dict__) # Copy the existing CRYPT attributes requires[0].min_length = 0 # But do not enforce minimum length for the old password @@ -3655,14 +3661,15 @@ class Auth(AuthAPI): onvalidation=onvalidation, hideerror=self.settings.hideerror): extra_fields = self.settings.extra_fields.get(self.settings.table_user_name, []) - if any(f.compute for f in extra_fields): - user = table_user[self.user.id] - self._update_session_user(user) - self.update_groups() - else: - self.user.update(table_user._filter_fields(form.vars)) - session.flash = self.messages.profile_updated - self.log_event(log, self.user) + if not form.deleted: + if any(f.compute for f in extra_fields): + user = table_user[self.user.id] + self._update_session_user(user) + self.update_groups() + else: + self.user.update(table_user._filter_fields(form.vars)) + session.flash = self.messages.profile_updated + self.log_event(log, self.user) callback(onaccept, form) if form.deleted: return self.logout() @@ -4609,7 +4616,6 @@ class Crud(object): # pragma: no cover results = None return form, results - urllib2.install_opener(urllib2.build_opener(urllib2.HTTPCookieProcessor())) @@ -4627,7 +4633,7 @@ def fetch(url, data=None, headers=None, from google.appengine.api import urlfetch except ImportError: req = urllib2.Request(url, data, headers) - html = urllib2.urlopen(req).read() + html = urlopen(req).read() else: method = ((data is None) and urlfetch.GET) or urlfetch.POST while url is not None: @@ -4995,7 +5001,7 @@ class Service(object): elif r and not isinstance(r, types.GeneratorType) and isinstance(r[0], (dict, Storage)): import csv writer = csv.writer(s) - writer.writerow(r[0].keys()) + writer.writerow(list(r[0].keys())) for line in r: writer.writerow([none_exception(v) for v in line.values()]) @@ -5212,7 +5218,7 @@ class Service(object): def serve_xmlrpc(self): request = current.request response = current.response - services = self.xmlrpc_procedures.values() + services = list(self.xmlrpc_procedures.values()) return response.xmlrpc(request, services) def serve_amfrpc(self, version=0): @@ -5567,7 +5573,7 @@ class PluginManager(object): return self.__dict__[key] def keys(self): - return self.__dict__.keys() + return list(self.__dict__.keys()) def __contains__(self, key): return key in self.__dict__ @@ -5813,7 +5819,7 @@ class Wiki(object): settings.templates = templates settings.controller = controller settings.function = function - settings.groups = auth.user_groups.values() \ + settings.groups = list(auth.user_groups.values()) \ if groups is None else groups db = auth.db @@ -5909,7 +5915,7 @@ class Wiki(object): if (auth.user and check_credentials(current.request, gae_login=False) and 'wiki_editor' not in auth.user_groups.values() and - self.settings.groups == auth.user_groups.values()): + self.settings.groups == list(auth.user_groups.values())): group = db.auth_group(role='wiki_editor') gid = group.id if group else db.auth_group.insert( role='wiki_editor') diff --git a/gluon/utils.py b/gluon/utils.py index ea738047..e4771d9c 100644 --- a/gluon/utils.py +++ b/gluon/utils.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +#pylint: disable=invalid-name,redefined-builtin """ | This file is part of the web2py Web Framework @@ -24,9 +25,7 @@ import socket import base64 import zlib import hashlib -import binascii import hmac -from hashlib import md5, sha1, sha224, sha256, sha384, sha512 from gluon._compat import basestring, pickle, PY2, xrange, to_bytes, to_native _struct_2_long_long = struct.Struct('=QQ') @@ -38,26 +37,6 @@ except ImportError: import gluon.contrib.pyaes as PYAES HAVE_AES = False -if hasattr(hashlib, "pbkdf2_hmac"): - def pbkdf2_hex(data, salt, iterations=1000, keylen=24, hashfunc=None): - hashfunc = hashfunc or sha1 - hmac = hashlib.pbkdf2_hmac(hashfunc().name, to_bytes(data), - to_bytes(salt), iterations, keylen) - return binascii.hexlify(hmac) - HAVE_PBKDF2 = True -else: - try: - try: - from gluon.contrib.pbkdf2_ctypes import pbkdf2_hex - except (ImportError, AttributeError): - from gluon.contrib.pbkdf2 import pbkdf2_hex - HAVE_PBKDF2 = True - except ImportError: - try: - from .pbkdf2 import pbkdf2_hex - HAVE_PBKDF2 = True - except (ImportError, ValueError): - HAVE_PBKDF2 = False HAVE_COMPARE_DIGEST = False if hasattr(hmac, 'compare_digest'): @@ -110,59 +89,7 @@ def compare(a, b): def md5_hash(text): """Generate an md5 hash with the given text.""" - return md5(to_bytes(text)).hexdigest() - - -def simple_hash(text, key='', salt='', digest_alg='md5'): - """Generate hash with the given text using the specified digest algorithm.""" - text = to_bytes(text) - key = to_bytes(key) - salt = to_bytes(salt) - if not digest_alg: - raise RuntimeError("simple_hash with digest_alg=None") - elif not isinstance(digest_alg, str): # manual approach - h = digest_alg(text + key + salt) - elif digest_alg.startswith('pbkdf2'): # latest and coolest! - iterations, keylen, alg = digest_alg[7:-1].split(',') - return to_native(pbkdf2_hex(text, salt, int(iterations), - int(keylen), get_digest(alg))) - elif key: # use hmac - digest_alg = get_digest(digest_alg) - h = hmac.new(key + salt, text, digest_alg) - else: # compatible with third party systems - h = get_digest(digest_alg)() - h.update(text + salt) - return h.hexdigest() - - -def get_digest(value): - """Return a hashlib digest algorithm from a string.""" - if not isinstance(value, str): - return value - value = value.lower() - if value == "md5": - return md5 - elif value == "sha1": - return sha1 - elif value == "sha224": - return sha224 - elif value == "sha256": - return sha256 - elif value == "sha384": - return sha384 - elif value == "sha512": - return sha512 - else: - raise ValueError("Invalid digest algorithm: %s" % value) - -DIGEST_ALG_BY_SIZE = { - 128 // 4: 'md5', - 160 // 4: 'sha1', - 224 // 4: 'sha224', - 256 // 4: 'sha256', - 384 // 4: 'sha384', - 512 // 4: 'sha512', -} + return hashlib.md5(to_bytes(text)).hexdigest() def get_callable_argspec(fn): @@ -178,12 +105,13 @@ def get_callable_argspec(fn): def pad(s, n=32): - # PKCS7v1.5 https://www.ietf.org/rfc/rfc2315.txt + """does padding according to PKCS7v1.5 https://www.ietf.org/rfc/rfc2315.txt""" padlen = n - len(s) % n return s + bytes(bytearray(padlen * [padlen])) def unpad(s, n=32): + """removed padding""" padlen = s[-1] if isinstance(padlen, str): padlen = ord(padlen) # python2 @@ -194,6 +122,7 @@ def unpad(s, n=32): def secure_dumps(data, encryption_key, hash_key=None, compression_level=None): + """dumps data, followed by a signature""" dump = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) if compression_level: dump = zlib.compress(dump, compression_level) @@ -207,6 +136,7 @@ def secure_dumps(data, encryption_key, hash_key=None, compression_level=None): def secure_loads(data, encryption_key, hash_key=None, compression_level=None): + """loads a signed data dump""" data = to_bytes(data) components = data.count(b':') if components == 1: @@ -230,18 +160,20 @@ def secure_loads(data, encryption_key, hash_key=None, compression_level=None): if compression_level: data = zlib.decompress(data) return pickle.loads(data) - except Exception as e: + except Exception: return None def __pad_deprecated(s, n=32, padchar=b' '): - return s + (32 - len(s) % 32) * padchar + """reprecated data, here for backward compatibility""" + return s + (n - len(s) % n) * padchar def secure_dumps_deprecated(data, encryption_key, hash_key=None, compression_level=None): + """dumps data with a signature (deprecated because of incorrect padding)""" encryption_key = to_bytes(encryption_key) if not hash_key: - hash_key = sha1(encryption_key).hexdigest() + hash_key = hashlib.sha1(encryption_key).hexdigest() dump = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) if compression_level: dump = zlib.compress(dump, compression_level) @@ -253,12 +185,13 @@ def secure_dumps_deprecated(data, encryption_key, hash_key=None, compression_lev def secure_loads_deprecated(data, encryption_key, hash_key=None, compression_level=None): + """loads signed data (deprecated because of incorrect padding)""" encryption_key = to_bytes(encryption_key) data = to_native(data) if ':' not in data: return None if not hash_key: - hash_key = sha1(encryption_key).hexdigest() + hash_key = hashlib.sha1(encryption_key).hexdigest() signature, encrypted_data = data.split(':', 1) encrypted_data = to_bytes(encrypted_data) actual_signature = hmac.new(to_bytes(hash_key), encrypted_data, hashlib.md5).hexdigest() @@ -274,7 +207,7 @@ def secure_loads_deprecated(data, encryption_key, hash_key=None, compression_lev if compression_level: data = zlib.decompress(data) return pickle.loads(data) - except Exception as e: + except Exception: return None ### compute constant CTOKENS @@ -365,7 +298,7 @@ def web2py_uuid(ctokens=UNPACKED_CTOKENS): rand_longs[1] ^ ctokens[1]) return str(uuid.UUID(bytes=byte_s, version=4)) -REGEX_IPv4 = re.compile('(\d+)\.(\d+)\.(\d+)\.(\d+)') +REGEX_IPv4 = re.compile(r'(\d+)\.(\d+)\.(\d+)\.(\d+)') def is_valid_ip_address(address): @@ -439,26 +372,49 @@ def getipaddrinfo(host): return [] -def local_html_escape(data, quote=False): +def unlocalised_http_header_date(data): """ - Works with bytes. - Replace special characters "&", "<" and ">" to HTML-safe sequences. - If the optional flag quote is true (the default), the quotation mark - characters, both double quote (") and single quote (') characters are also - translated. + Converts input datetime to format defined by RFC 7231, section 7.1.1.1 + + Previously, %a and %b formats were used for weekday and month names, but + those are not locale-safe. uWSGI requires latin1-encodable headers and + for example in cs_CS locale, fourth day in week is not encodable in latin1, + as it's "Čt". + + Example output: Sun, 06 Nov 1994 08:49:37 GMT """ - if PY2: - import cgi - data = cgi.escape(data, quote) - return data.replace("'", "'") if quote else data - else: - import html - if isinstance(data, str): - return html.escape(data, quote=quote) - data = data.replace(b"&", b"&") # Must be done first! - data = data.replace(b"<", b"<") - data = data.replace(b">", b">") - if quote: - data = data.replace(b'"', b""") - data = data.replace(b'\'', b"'") - return data + + short_weekday = { + "0": "Sun", + "1": "Mon", + "2": "Tue", + "3": "Wed", + "4": "Thu", + "5": "Fri", + "6": "Sat", + }.get(time.strftime("%w", data)) + + day_of_month = time.strftime("%d", data) + + short_month = { + "01": "Jan", + "02": "Feb", + "03": "Mar", + "04": "Apr", + "05": "May", + "06": "Jun", + "07": "Jul", + "08": "Aug", + "09": "Sep", + "10": "Oct", + "11": "Nov", + "12": "Dec", + }.get(time.strftime("%m", data)) + + year_and_time = time.strftime("%Y %H:%M:%S GMT", data) + + return "{}, {} {} {}".format( + short_weekday, + day_of_month, + short_month, + year_and_time) diff --git a/gluon/validators.py b/gluon/validators.py index 7e47f79c..c51f9014 100644 --- a/gluon/validators.py +++ b/gluon/validators.py @@ -1,3919 +1,2 @@ -#!/bin/env python -# -*- coding: utf-8 -*- - -""" -| This file is part of the web2py Web Framework -| Copyrighted by Massimo Di Pierro -| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) -| Thanks to ga2arch for help with IS_IN_DB and IS_NOT_IN_DB on GAE - -Validators ------------ -""" -import os -import re -import datetime -import time -import cgi -import json -import urllib -import struct -import decimal -import unicodedata - -from gluon._compat import StringIO, integer_types, basestring, unicodeT, urllib_unquote, unichr, to_bytes, PY2, \ - to_unicode, to_native, string_types, urlparse -from gluon.utils import simple_hash, web2py_uuid, DIGEST_ALG_BY_SIZE -from pydal.objects import Field, FieldVirtual, FieldMethod -from functools import reduce - -regex_isint = re.compile('^[+-]?\d+$') - -JSONErrors = (NameError, TypeError, ValueError, AttributeError, - KeyError) - -__all__ = [ - 'ANY_OF', - 'CLEANUP', - 'CRYPT', - 'IS_ALPHANUMERIC', - 'IS_DATE_IN_RANGE', - 'IS_DATE', - 'IS_DATETIME_IN_RANGE', - 'IS_DATETIME', - 'IS_DECIMAL_IN_RANGE', - 'IS_EMAIL', - 'IS_LIST_OF_EMAILS', - 'IS_EMPTY_OR', - 'IS_EXPR', - 'IS_FLOAT_IN_RANGE', - 'IS_IMAGE', - 'IS_IN_DB', - 'IS_IN_SET', - 'IS_INT_IN_RANGE', - 'IS_IPV4', - 'IS_IPV6', - 'IS_IPADDRESS', - 'IS_LENGTH', - 'IS_LIST_OF', - 'IS_LOWER', - 'IS_MATCH', - 'IS_EQUAL_TO', - 'IS_NOT_EMPTY', - 'IS_NOT_IN_DB', - 'IS_NULL_OR', - 'IS_SLUG', - 'IS_STRONG', - 'IS_TIME', - 'IS_UPLOAD_FILENAME', - 'IS_UPPER', - 'IS_URL', - 'IS_JSON', -] - -try: - from gluon.globals import current - have_current = True -except ImportError: - have_current = False - - -def translate(text): - if text is None: - return None - elif isinstance(text, (str, unicodeT)) and have_current: - if hasattr(current, 'T'): - return str(current.T(text)) - return str(text) - - -def options_sorter(x, y): - return (str(x[1]).upper() > str(y[1]).upper() and 1) or -1 - - -class Validator(object): - """ - Root for all validators, mainly for documentation purposes. - - Validators are classes used to validate input fields (including forms - generated from database tables). - - Here is an example of using a validator with a FORM:: - - INPUT(_name='a', requires=IS_INT_IN_RANGE(0, 10)) - - Here is an example of how to require a validator for a table field:: - - db.define_table('person', Field('name')) - db.person.name.requires=IS_NOT_EMPTY() - - Validators are always assigned using the requires attribute of a field. A - field can have a single validator or multiple validators. Multiple - validators are made part of a list:: - - db.person.name.requires=[IS_NOT_EMPTY(), IS_NOT_IN_DB(db, 'person.id')] - - Validators are called by the function accepts on a FORM or other HTML - helper object that contains a form. They are always called in the order in - which they are listed. - - Built-in validators have constructors that take the optional argument error - message which allows you to change the default error message. - Here is an example of a validator on a database table:: - - db.person.name.requires=IS_NOT_EMPTY(error_message=T('Fill this')) - - where we have used the translation operator T to allow for - internationalization. - - Notice that default error messages are not translated. - """ - - def formatter(self, value): - """ - For some validators returns a formatted version (matching the validator) - of value. Otherwise just returns the value. - """ - return value - - def __call__(self, value): - raise NotImplementedError - - -class IS_MATCH(Validator): - """ - Example: - Used as:: - - INPUT(_type='text', _name='name', requires=IS_MATCH('.+')) - - The argument of IS_MATCH is a regular expression:: - - >>> IS_MATCH('.+')('hello') - ('hello', None) - - >>> IS_MATCH('hell')('hello') - ('hello', None) - - >>> IS_MATCH('hell.*', strict=False)('hello') - ('hello', None) - - >>> IS_MATCH('hello')('shello') - ('shello', 'invalid expression') - - >>> IS_MATCH('hello', search=True)('shello') - ('shello', None) - - >>> IS_MATCH('hello', search=True, strict=False)('shellox') - ('shellox', None) - - >>> IS_MATCH('.*hello.*', search=True, strict=False)('shellox') - ('shellox', None) - - >>> IS_MATCH('.+')('') - ('', 'invalid expression') - - """ - - def __init__(self, expression, error_message='Invalid expression', - strict=False, search=False, extract=False, - is_unicode=False): - - if strict or not search: - if not expression.startswith('^'): - expression = '^(%s)' % expression - if strict: - if not expression.endswith('$'): - expression = '(%s)$' % expression - if is_unicode: - if not isinstance(expression, unicodeT): - expression = expression.decode('utf8') - self.regex = re.compile(expression, re.UNICODE) - else: - self.regex = re.compile(expression) - self.error_message = error_message - self.extract = extract - self.is_unicode = is_unicode or (not(PY2)) - - def __call__(self, value): - if not(PY2): # PY3 convert bytes to unicode - value = to_unicode(value) - - if self.is_unicode or not(PY2): - if not isinstance(value, unicodeT): - match = self.regex.search(str(value).decode('utf8')) - else: - match = self.regex.search(value) - else: - if not isinstance(value, unicodeT): - match = self.regex.search(str(value)) - else: - match = self.regex.search(value.encode('utf8')) - if match is not None: - return (self.extract and match.group() or value, None) - return (value, translate(self.error_message)) - - -class IS_EQUAL_TO(Validator): - """ - Example: - Used as:: - - INPUT(_type='text', _name='password') - INPUT(_type='text', _name='password2', - requires=IS_EQUAL_TO(request.vars.password)) - - The argument of IS_EQUAL_TO is a string:: - - >>> IS_EQUAL_TO('aaa')('aaa') - ('aaa', None) - - >>> IS_EQUAL_TO('aaa')('aab') - ('aab', 'no match') - - """ - - def __init__(self, expression, error_message='No match'): - self.expression = expression - self.error_message = error_message - - def __call__(self, value): - if value == self.expression: - return (value, None) - return (value, translate(self.error_message)) - - -class IS_EXPR(Validator): - """ - Example: - Used as:: - - INPUT(_type='text', _name='name', - requires=IS_EXPR('5 < int(value) < 10')) - - The argument of IS_EXPR must be python condition:: - - >>> IS_EXPR('int(value) < 2')('1') - ('1', None) - - >>> IS_EXPR('int(value) < 2')('2') - ('2', 'invalid expression') - - """ - - def __init__(self, expression, error_message='Invalid expression', environment=None): - self.expression = expression - self.error_message = error_message - self.environment = environment or {} - - def __call__(self, value): - if callable(self.expression): - return (value, self.expression(value)) - # for backward compatibility - self.environment.update(value=value) - exec('__ret__=' + self.expression, self.environment) - if self.environment['__ret__']: - return (value, None) - return (value, translate(self.error_message)) - - -class IS_LENGTH(Validator): - """ - Checks if length of field's value fits between given boundaries. Works - for both text and file inputs. - - Args: - maxsize: maximum allowed length / size - minsize: minimum allowed length / size - - Examples: - Check if text string is shorter than 33 characters:: - - INPUT(_type='text', _name='name', requires=IS_LENGTH(32)) - - Check if password string is longer than 5 characters:: - - INPUT(_type='password', _name='name', requires=IS_LENGTH(minsize=6)) - - Check if uploaded file has size between 1KB and 1MB:: - - INPUT(_type='file', _name='name', requires=IS_LENGTH(1048576, 1024)) - - Other examples:: - - >>> IS_LENGTH()('') - ('', None) - >>> IS_LENGTH()('1234567890') - ('1234567890', None) - >>> IS_LENGTH(maxsize=5, minsize=0)('1234567890') # too long - ('1234567890', 'enter from 0 to 5 characters') - >>> IS_LENGTH(maxsize=50, minsize=20)('1234567890') # too short - ('1234567890', 'enter from 20 to 50 characters') - """ - - def __init__(self, maxsize=255, minsize=0, - error_message='Enter from %(min)g to %(max)g characters'): - self.maxsize = maxsize - self.minsize = minsize - self.error_message = error_message - - def __call__(self, value): - if value is None: - length = 0 - if self.minsize <= length <= self.maxsize: - return (value, None) - elif isinstance(value, cgi.FieldStorage): - if value.file: - value.file.seek(0, os.SEEK_END) - length = value.file.tell() - value.file.seek(0, os.SEEK_SET) - elif hasattr(value, 'value'): - val = value.value - if val: - length = len(val) - else: - length = 0 - if self.minsize <= length <= self.maxsize: - return (value, None) - elif isinstance(value, str): - try: - lvalue = len(to_unicode(value)) - except: - lvalue = len(value) - if self.minsize <= lvalue <= self.maxsize: - return (value, None) - elif isinstance(value, unicodeT): - if self.minsize <= len(value) <= self.maxsize: - return (value.encode('utf8'), None) - elif isinstance(value, (bytes, bytearray)): - if self.minsize <= len(value) <= self.maxsize: - return (value, None) - elif isinstance(value, (tuple, list)): - if self.minsize <= len(value) <= self.maxsize: - return (value, None) - elif self.minsize <= len(str(value)) <= self.maxsize: - return (str(value), None) - return (value, translate(self.error_message) - % dict(min=self.minsize, max=self.maxsize)) - - -class IS_JSON(Validator): - """ - Example: - Used as:: - - INPUT(_type='text', _name='name', - requires=IS_JSON(error_message="This is not a valid json input") - - >>> IS_JSON()('{"a": 100}') - ({u'a': 100}, None) - - >>> IS_JSON()('spam1234') - ('spam1234', 'invalid json') - """ - - def __init__(self, error_message='Invalid json', native_json=False): - self.native_json = native_json - self.error_message = error_message - - def __call__(self, value): - try: - if self.native_json: - json.loads(value) # raises error in case of malformed json - return (value, None) # the serialized value is not passed - else: - return (json.loads(value), None) - except JSONErrors: - return (value, translate(self.error_message)) - - def formatter(self, value): - if value is None: - return None - if self.native_json: - return value - else: - return json.dumps(value) - - -class IS_IN_SET(Validator): - """ - Example: - Used as:: - - INPUT(_type='text', _name='name', - requires=IS_IN_SET(['max', 'john'],zero='')) - - The argument of IS_IN_SET must be a list or set:: - - >>> IS_IN_SET(['max', 'john'])('max') - ('max', None) - >>> IS_IN_SET(['max', 'john'])('massimo') - ('massimo', 'value not allowed') - >>> IS_IN_SET(['max', 'john'], multiple=True)(('max', 'john')) - (('max', 'john'), None) - >>> IS_IN_SET(['max', 'john'], multiple=True)(('bill', 'john')) - (('bill', 'john'), 'value not allowed') - >>> IS_IN_SET(('id1','id2'), ['first label','second label'])('id1') # Traditional way - ('id1', None) - >>> IS_IN_SET({'id1':'first label', 'id2':'second label'})('id1') - ('id1', None) - >>> import itertools - >>> IS_IN_SET(itertools.chain(['1','3','5'],['2','4','6']))('1') - ('1', None) - >>> IS_IN_SET([('id1','first label'), ('id2','second label')])('id1') # Redundant way - ('id1', None) - - """ - - def __init__( - self, - theset, - labels=None, - error_message='Value not allowed', - multiple=False, - zero='', - sort=False, - ): - self.multiple = multiple - if isinstance(theset, dict): - self.theset = [str(item) for item in theset] - self.labels = theset.values() - elif theset and isinstance(theset, (tuple, list)) \ - and isinstance(theset[0], (tuple, list)) and len(theset[0]) == 2: - self.theset = [str(item) for item, label in theset] - self.labels = [str(label) for item, label in theset] - else: - self.theset = [str(item) for item in theset] - self.labels = labels - self.error_message = error_message - self.zero = zero - self.sort = sort - - def options(self, zero=True): - if not self.labels: - items = [(k, k) for (i, k) in enumerate(self.theset)] - else: - items = [(k, list(self.labels)[i]) for (i, k) in enumerate(self.theset)] - if self.sort: - items.sort(key=lambda o: str(o[1]).upper()) - if zero and self.zero is not None and not self.multiple: - items.insert(0, ('', self.zero)) - return items - - def __call__(self, value): - if self.multiple: - # if below was values = re.compile("[\w\-:]+").findall(str(value)) - if not value: - values = [] - elif isinstance(value, (tuple, list)): - values = value - else: - values = [value] - else: - values = [value] - thestrset = [str(x) for x in self.theset] - failures = [x for x in values if not str(x) in thestrset] - if failures and self.theset: - return (value, translate(self.error_message)) - if self.multiple: - if isinstance(self.multiple, (tuple, list)) and \ - not self.multiple[0] <= len(values) < self.multiple[1]: - return (values, translate(self.error_message)) - return (values, None) - return (value, None) - - -regex1 = re.compile('\w+\.\w+') -regex2 = re.compile('%\(([^\)]+)\)\d*(?:\.\d+)?[a-zA-Z]') - - -class IS_IN_DB(Validator): - """ - Example: - Used as:: - - INPUT(_type='text', _name='name', - requires=IS_IN_DB(db, db.mytable.myfield, zero='')) - - used for reference fields, rendered as a dropbox - """ - - def __init__( - self, - dbset, - field, - label=None, - error_message='Value not in database', - orderby=None, - groupby=None, - distinct=None, - cache=None, - multiple=False, - zero='', - sort=False, - _and=None, - left=None, - delimiter=None, - auto_add=False, - ): - from pydal.objects import Table - if hasattr(dbset, 'define_table'): - self.dbset = dbset() - else: - self.dbset = dbset - - if isinstance(field, Table): - field = field._id - elif isinstance(field, str): - items = field.split('.') - if len(items) == 1: - field = items[0] + '.id' - - (ktable, kfield) = str(field).split('.') - if not label: - label = '%%(%s)s' % kfield - if isinstance(label, str): - if regex1.match(str(label)): - label = '%%(%s)s' % str(label).split('.')[-1] - fieldnames = regex2.findall(label) - if kfield not in fieldnames: - fieldnames.append(kfield) # kfield must be last - elif isinstance(label, Field): - fieldnames = [label.name, kfield] # kfield must be last - label = '%%(%s)s' % label.name - elif callable(label): - fieldnames = '*' - else: - raise NotImplementedError - - self.fieldnames = fieldnames # fields requires to build the formatting - self.label = label - self.ktable = ktable - self.kfield = kfield - self.error_message = error_message - self.theset = None - self.orderby = orderby - self.groupby = groupby - self.distinct = distinct - self.cache = cache - self.multiple = multiple - self.zero = zero - self.sort = sort - self._and = _and - self.left = left - self.delimiter = delimiter - self.auto_add = auto_add - - def set_self_id(self, id): - if self._and: - self._and.record_id = id - - def build_set(self): - table = self.dbset.db[self.ktable] - if self.fieldnames == '*': - fields = [f for f in table] - else: - fields = [table[k] for k in self.fieldnames] - ignore = (FieldVirtual, FieldMethod) - fields = filter(lambda f: not isinstance(f, ignore), fields) - if self.dbset.db._dbname != 'gae': - orderby = self.orderby or reduce(lambda a, b: a | b, fields) - groupby = self.groupby - distinct = self.distinct - left = self.left - dd = dict(orderby=orderby, groupby=groupby, - distinct=distinct, cache=self.cache, - cacheable=True, left=left) - records = self.dbset(table).select(*fields, **dd) - else: - orderby = self.orderby or \ - reduce(lambda a, b: a | b, ( - f for f in fields if not f.name == 'id')) - dd = dict(orderby=orderby, cache=self.cache, cacheable=True) - records = self.dbset(table).select(table.ALL, **dd) - self.theset = [str(r[self.kfield]) for r in records] - if isinstance(self.label, str): - self.labels = [self.label % r for r in records] - else: - self.labels = [self.label(r) for r in records] - - def options(self, zero=True): - self.build_set() - items = [(k, self.labels[i]) for (i, k) in enumerate(self.theset)] - if self.sort: - items.sort(key=lambda o: str(o[1]).upper()) - if zero and self.zero is not None and not self.multiple: - items.insert(0, ('', self.zero)) - return items - - def maybe_add(self, table, fieldname, value): - d = {fieldname: value} - record = table(**d) - if record: - return record.id - else: - return table.insert(**d) - - def __call__(self, value): - table = self.dbset.db[self.ktable] - field = table[self.kfield] - - if self.multiple: - if self._and: - raise NotImplementedError - if isinstance(value, list): - values = value - elif self.delimiter: - values = value.split(self.delimiter) # because of autocomplete - elif value: - values = [value] - else: - values = [] - - if field.type in ('id', 'integer'): - new_values = [] - for value in values: - if not (isinstance(value, integer_types) or value.isdigit()): - if self.auto_add: - value = str(self.maybe_add(table, self.fieldnames[0], value)) - else: - return (values, translate(self.error_message)) - new_values.append(value) - values = new_values - - if isinstance(self.multiple, (tuple, list)) and \ - not self.multiple[0] <= len(values) < self.multiple[1]: - return (values, translate(self.error_message)) - if self.theset: - if not [v for v in values if v not in self.theset]: - return (values, None) - else: - def count(values, s=self.dbset, f=field): - return s(f.belongs(map(int, values))).count() - - if self.dbset.db._adapter.dbengine == "google:datastore": - range_ids = range(0, len(values), 30) - total = sum(count(values[i:i + 30]) for i in range_ids) - if total == len(values): - return (values, None) - elif count(values) == len(values): - return (values, None) - else: - if field.type in ('id', 'integer'): - if isinstance(value, integer_types) or (isinstance(value, string_types) and value.isdigit()): - value = int(value) - elif self.auto_add: - value = self.maybe_add(table, self.fieldnames[0], value) - else: - return (value, translate(self.error_message)) - - try: - value = int(value) - except TypeError: - return (value, translate(self.error_message)) - - if self.theset: - if str(value) in self.theset: - if self._and: - return self._and(value) - else: - return (value, None) - else: - if self.dbset(field == value).count(): - if self._and: - return self._and(value) - else: - return (value, None) - return (value, translate(self.error_message)) - - -class IS_NOT_IN_DB(Validator): - """ - Example: - Used as:: - - INPUT(_type='text', _name='name', requires=IS_NOT_IN_DB(db, db.table)) - - makes the field unique - """ - - def __init__( - self, - dbset, - field, - error_message='Value already in database or empty', - allowed_override=[], - ignore_common_filters=False, - ): - - from pydal.objects import Table - if isinstance(field, Table): - field = field._id - - if hasattr(dbset, 'define_table'): - self.dbset = dbset() - else: - self.dbset = dbset - self.field = field - self.error_message = error_message - self.record_id = 0 - self.allowed_override = allowed_override - self.ignore_common_filters = ignore_common_filters - - def set_self_id(self, id): - self.record_id = id - - def __call__(self, value): - value = to_native(str(value)) - if not value.strip(): - return (value, translate(self.error_message)) - if value in self.allowed_override: - return (value, None) - (tablename, fieldname) = str(self.field).split('.') - table = self.dbset.db[tablename] - field = table[fieldname] - subset = self.dbset(field == value, - ignore_common_filters=self.ignore_common_filters) - id = self.record_id - if isinstance(id, dict): - fields = [table[f] for f in id] - row = subset.select(*fields, **dict(limitby=(0, 1), orderby_on_limitby=False)).first() - if row and any(str(row[f]) != str(id[f]) for f in id): - return (value, translate(self.error_message)) - else: - row = subset.select(table._id, field, limitby=(0, 1), orderby_on_limitby=False).first() - if row and str(row[table._id]) != str(id): - return (value, translate(self.error_message)) - return (value, None) - - -def range_error_message(error_message, what_to_enter, minimum, maximum): - """build the error message for the number range validators""" - if error_message is None: - error_message = 'Enter ' + what_to_enter - if minimum is not None and maximum is not None: - error_message += ' between %(min)g and %(max)g' - elif minimum is not None: - error_message += ' greater than or equal to %(min)g' - elif maximum is not None: - error_message += ' less than or equal to %(max)g' - if type(maximum) in integer_types: - maximum -= 1 - return translate(error_message) % dict(min=minimum, max=maximum) - - -class IS_INT_IN_RANGE(Validator): - """ - Determines that the argument is (or can be represented as) an int, - and that it falls within the specified range. The range is interpreted - in the Pythonic way, so the test is: min <= value < max. - - The minimum and maximum limits can be None, meaning no lower or upper limit, - respectively. - - Example: - Used as:: - - INPUT(_type='text', _name='name', requires=IS_INT_IN_RANGE(0, 10)) - - >>> IS_INT_IN_RANGE(1,5)('4') - (4, None) - >>> IS_INT_IN_RANGE(1,5)(4) - (4, None) - >>> IS_INT_IN_RANGE(1,5)(1) - (1, None) - >>> IS_INT_IN_RANGE(1,5)(5) - (5, 'enter an integer between 1 and 4') - >>> IS_INT_IN_RANGE(1,5)(5) - (5, 'enter an integer between 1 and 4') - >>> IS_INT_IN_RANGE(1,5)(3.5) - (3.5, 'enter an integer between 1 and 4') - >>> IS_INT_IN_RANGE(None,5)('4') - (4, None) - >>> IS_INT_IN_RANGE(None,5)('6') - ('6', 'enter an integer less than or equal to 4') - >>> IS_INT_IN_RANGE(1,None)('4') - (4, None) - >>> IS_INT_IN_RANGE(1,None)('0') - ('0', 'enter an integer greater than or equal to 1') - >>> IS_INT_IN_RANGE()(6) - (6, None) - >>> IS_INT_IN_RANGE()('abc') - ('abc', 'enter an integer') - """ - - def __init__( - self, - minimum=None, - maximum=None, - error_message=None, - ): - self.minimum = int(minimum) if minimum is not None else None - self.maximum = int(maximum) if maximum is not None else None - self.error_message = range_error_message( - error_message, 'an integer', self.minimum, self.maximum) - - def __call__(self, value): - if regex_isint.match(str(value)): - v = int(value) - if ((self.minimum is None or v >= self.minimum) and - (self.maximum is None or v < self.maximum)): - return (v, None) - return (value, self.error_message) - - -def str2dec(number): - s = str(number) - if '.' not in s: - s += '.00' - else: - s += '0' * (2 - len(s.split('.')[1])) - return s - - -class IS_FLOAT_IN_RANGE(Validator): - """ - Determines that the argument is (or can be represented as) a float, - and that it falls within the specified inclusive range. - The comparison is made with native arithmetic. - - The minimum and maximum limits can be None, meaning no lower or upper limit, - respectively. - - Example: - Used as:: - - INPUT(_type='text', _name='name', requires=IS_FLOAT_IN_RANGE(0, 10)) - - >>> IS_FLOAT_IN_RANGE(1,5)('4') - (4.0, None) - >>> IS_FLOAT_IN_RANGE(1,5)(4) - (4.0, None) - >>> IS_FLOAT_IN_RANGE(1,5)(1) - (1.0, None) - >>> IS_FLOAT_IN_RANGE(1,5)(5.25) - (5.25, 'enter a number between 1 and 5') - >>> IS_FLOAT_IN_RANGE(1,5)(6.0) - (6.0, 'enter a number between 1 and 5') - >>> IS_FLOAT_IN_RANGE(1,5)(3.5) - (3.5, None) - >>> IS_FLOAT_IN_RANGE(1,None)(3.5) - (3.5, None) - >>> IS_FLOAT_IN_RANGE(None,5)(3.5) - (3.5, None) - >>> IS_FLOAT_IN_RANGE(1,None)(0.5) - (0.5, 'enter a number greater than or equal to 1') - >>> IS_FLOAT_IN_RANGE(None,5)(6.5) - (6.5, 'enter a number less than or equal to 5') - >>> IS_FLOAT_IN_RANGE()(6.5) - (6.5, None) - >>> IS_FLOAT_IN_RANGE()('abc') - ('abc', 'enter a number') - """ - - def __init__( - self, - minimum=None, - maximum=None, - error_message=None, - dot='.' - ): - self.minimum = float(minimum) if minimum is not None else None - self.maximum = float(maximum) if maximum is not None else None - self.dot = str(dot) - self.error_message = range_error_message( - error_message, 'a number', self.minimum, self.maximum) - - def __call__(self, value): - try: - if self.dot == '.': - v = float(value) - else: - v = float(str(value).replace(self.dot, '.')) - if ((self.minimum is None or v >= self.minimum) and - (self.maximum is None or v <= self.maximum)): - return (v, None) - except (ValueError, TypeError): - pass - return (value, self.error_message) - - def formatter(self, value): - if value is None: - return None - return str2dec(value).replace('.', self.dot) - - -class IS_DECIMAL_IN_RANGE(Validator): - """ - Determines that the argument is (or can be represented as) a Python Decimal, - and that it falls within the specified inclusive range. - The comparison is made with Python Decimal arithmetic. - - The minimum and maximum limits can be None, meaning no lower or upper limit, - respectively. - - Example: - Used as:: - - INPUT(_type='text', _name='name', requires=IS_DECIMAL_IN_RANGE(0, 10)) - - >>> IS_DECIMAL_IN_RANGE(1,5)('4') - (Decimal('4'), None) - >>> IS_DECIMAL_IN_RANGE(1,5)(4) - (Decimal('4'), None) - >>> IS_DECIMAL_IN_RANGE(1,5)(1) - (Decimal('1'), None) - >>> IS_DECIMAL_IN_RANGE(1,5)(5.25) - (5.25, 'enter a number between 1 and 5') - >>> IS_DECIMAL_IN_RANGE(5.25,6)(5.25) - (Decimal('5.25'), None) - >>> IS_DECIMAL_IN_RANGE(5.25,6)('5.25') - (Decimal('5.25'), None) - >>> IS_DECIMAL_IN_RANGE(1,5)(6.0) - (6.0, 'enter a number between 1 and 5') - >>> IS_DECIMAL_IN_RANGE(1,5)(3.5) - (Decimal('3.5'), None) - >>> IS_DECIMAL_IN_RANGE(1.5,5.5)(3.5) - (Decimal('3.5'), None) - >>> IS_DECIMAL_IN_RANGE(1.5,5.5)(6.5) - (6.5, 'enter a number between 1.5 and 5.5') - >>> IS_DECIMAL_IN_RANGE(1.5,None)(6.5) - (Decimal('6.5'), None) - >>> IS_DECIMAL_IN_RANGE(1.5,None)(0.5) - (0.5, 'enter a number greater than or equal to 1.5') - >>> IS_DECIMAL_IN_RANGE(None,5.5)(4.5) - (Decimal('4.5'), None) - >>> IS_DECIMAL_IN_RANGE(None,5.5)(6.5) - (6.5, 'enter a number less than or equal to 5.5') - >>> IS_DECIMAL_IN_RANGE()(6.5) - (Decimal('6.5'), None) - >>> IS_DECIMAL_IN_RANGE(0,99)(123.123) - (123.123, 'enter a number between 0 and 99') - >>> IS_DECIMAL_IN_RANGE(0,99)('123.123') - ('123.123', 'enter a number between 0 and 99') - >>> IS_DECIMAL_IN_RANGE(0,99)('12.34') - (Decimal('12.34'), None) - >>> IS_DECIMAL_IN_RANGE()('abc') - ('abc', 'enter a number') - """ - - def __init__( - self, - minimum=None, - maximum=None, - error_message=None, - dot='.' - ): - self.minimum = decimal.Decimal(str(minimum)) if minimum is not None else None - self.maximum = decimal.Decimal(str(maximum)) if maximum is not None else None - self.dot = str(dot) - self.error_message = range_error_message( - error_message, 'a number', self.minimum, self.maximum) - - def __call__(self, value): - try: - if isinstance(value, decimal.Decimal): - v = value - else: - v = decimal.Decimal(str(value).replace(self.dot, '.')) - if ((self.minimum is None or v >= self.minimum) and - (self.maximum is None or v <= self.maximum)): - return (v, None) - except (ValueError, TypeError, decimal.InvalidOperation): - pass - return (value, self.error_message) - - def formatter(self, value): - if value is None: - return None - return str2dec(value).replace('.', self.dot) - - -def is_empty(value, empty_regex=None): - _value = value - """test empty field""" - if isinstance(value, (str, unicodeT)): - value = value.strip() - if empty_regex is not None and empty_regex.match(value): - value = '' - if value is None or value == '' or value == b'' or value == []: - return (_value, True) - return (_value, False) - - -class IS_NOT_EMPTY(Validator): - """ - Example: - Used as:: - - INPUT(_type='text', _name='name', requires=IS_NOT_EMPTY()) - - >>> IS_NOT_EMPTY()(1) - (1, None) - >>> IS_NOT_EMPTY()(0) - (0, None) - >>> IS_NOT_EMPTY()('x') - ('x', None) - >>> IS_NOT_EMPTY()(' x ') - ('x', None) - >>> IS_NOT_EMPTY()(None) - (None, 'enter a value') - >>> IS_NOT_EMPTY()('') - ('', 'enter a value') - >>> IS_NOT_EMPTY()(' ') - ('', 'enter a value') - >>> IS_NOT_EMPTY()(' \\n\\t') - ('', 'enter a value') - >>> IS_NOT_EMPTY()([]) - ([], 'enter a value') - >>> IS_NOT_EMPTY(empty_regex='def')('def') - ('', 'enter a value') - >>> IS_NOT_EMPTY(empty_regex='de[fg]')('deg') - ('', 'enter a value') - >>> IS_NOT_EMPTY(empty_regex='def')('abc') - ('abc', None) - """ - - def __init__(self, error_message='Enter a value', empty_regex=None): - self.error_message = error_message - if empty_regex is not None: - self.empty_regex = re.compile(empty_regex) - else: - self.empty_regex = None - - def __call__(self, value): - value, empty = is_empty(value, empty_regex=self.empty_regex) - if empty: - return (value, translate(self.error_message)) - return (value, None) - - -class IS_ALPHANUMERIC(IS_MATCH): - """ - Example: - Used as:: - - INPUT(_type='text', _name='name', requires=IS_ALPHANUMERIC()) - - >>> IS_ALPHANUMERIC()('1') - ('1', None) - >>> IS_ALPHANUMERIC()('') - ('', None) - >>> IS_ALPHANUMERIC()('A_a') - ('A_a', None) - >>> IS_ALPHANUMERIC()('!') - ('!', 'enter only letters, numbers, and underscore') - """ - - def __init__(self, error_message='Enter only letters, numbers, and underscore'): - IS_MATCH.__init__(self, '^[\w]*$', error_message) - - -class IS_EMAIL(Validator): - """ - Checks if field's value is a valid email address. Can be set to disallow - or force addresses from certain domain(s). - - Email regex adapted from - http://haacked.com/archive/2007/08/21/i-knew-how-to-validate-an-email-address-until-i.aspx, - generally following the RFCs, except that we disallow quoted strings - and permit underscores and leading numerics in subdomain labels - - Args: - banned: regex text for disallowed address domains - forced: regex text for required address domains - - Both arguments can also be custom objects with a match(value) method. - - Example: - Check for valid email address:: - - INPUT(_type='text', _name='name', - requires=IS_EMAIL()) - - Check for valid email address that can't be from a .com domain:: - - INPUT(_type='text', _name='name', - requires=IS_EMAIL(banned='^.*\.com(|\..*)$')) - - Check for valid email address that must be from a .edu domain:: - - INPUT(_type='text', _name='name', - requires=IS_EMAIL(forced='^.*\.edu(|\..*)$')) - - >>> IS_EMAIL()('a@b.com') - ('a@b.com', None) - >>> IS_EMAIL()('abc@def.com') - ('abc@def.com', None) - >>> IS_EMAIL()('abc@3def.com') - ('abc@3def.com', None) - >>> IS_EMAIL()('abc@def.us') - ('abc@def.us', None) - >>> IS_EMAIL()('abc@d_-f.us') - ('abc@d_-f.us', None) - >>> IS_EMAIL()('@def.com') # missing name - ('@def.com', 'enter a valid email address') - >>> IS_EMAIL()('"abc@def".com') # quoted name - ('"abc@def".com', 'enter a valid email address') - >>> IS_EMAIL()('abc+def.com') # no @ - ('abc+def.com', 'enter a valid email address') - >>> IS_EMAIL()('abc@def.x') # one-char TLD - ('abc@def.x', 'enter a valid email address') - >>> IS_EMAIL()('abc@def.12') # numeric TLD - ('abc@def.12', 'enter a valid email address') - >>> IS_EMAIL()('abc@def..com') # double-dot in domain - ('abc@def..com', 'enter a valid email address') - >>> IS_EMAIL()('abc@.def.com') # dot starts domain - ('abc@.def.com', 'enter a valid email address') - >>> IS_EMAIL()('abc@def.c_m') # underscore in TLD - ('abc@def.c_m', 'enter a valid email address') - >>> IS_EMAIL()('NotAnEmail') # missing @ - ('NotAnEmail', 'enter a valid email address') - >>> IS_EMAIL()('abc@NotAnEmail') # missing TLD - ('abc@NotAnEmail', 'enter a valid email address') - >>> IS_EMAIL()('customer/department@example.com') - ('customer/department@example.com', None) - >>> IS_EMAIL()('$A12345@example.com') - ('$A12345@example.com', None) - >>> IS_EMAIL()('!def!xyz%abc@example.com') - ('!def!xyz%abc@example.com', None) - >>> IS_EMAIL()('_Yosemite.Sam@example.com') - ('_Yosemite.Sam@example.com', None) - >>> IS_EMAIL()('~@example.com') - ('~@example.com', None) - >>> IS_EMAIL()('.wooly@example.com') # dot starts name - ('.wooly@example.com', 'enter a valid email address') - >>> IS_EMAIL()('wo..oly@example.com') # adjacent dots in name - ('wo..oly@example.com', 'enter a valid email address') - >>> IS_EMAIL()('pootietang.@example.com') # dot ends name - ('pootietang.@example.com', 'enter a valid email address') - >>> IS_EMAIL()('.@example.com') # name is bare dot - ('.@example.com', 'enter a valid email address') - >>> IS_EMAIL()('Ima.Fool@example.com') - ('Ima.Fool@example.com', None) - >>> IS_EMAIL()('Ima Fool@example.com') # space in name - ('Ima Fool@example.com', 'enter a valid email address') - >>> IS_EMAIL()('localguy@localhost') # localhost as domain - ('localguy@localhost', None) - - """ - - body_regex = re.compile(''' - ^(?!\.) # name may not begin with a dot - ( - [-a-z0-9!\#$%&'*+/=?^_`{|}~] # all legal characters except dot - | - (? obtained on 2008-Nov-10 - -official_url_schemes = [ - 'aaa', - 'aaas', - 'acap', - 'cap', - 'cid', - 'crid', - 'data', - 'dav', - 'dict', - 'dns', - 'fax', - 'file', - 'ftp', - 'go', - 'gopher', - 'h323', - 'http', - 'https', - 'icap', - 'im', - 'imap', - 'info', - 'ipp', - 'iris', - 'iris.beep', - 'iris.xpc', - 'iris.xpcs', - 'iris.lws', - 'ldap', - 'mailto', - 'mid', - 'modem', - 'msrp', - 'msrps', - 'mtqp', - 'mupdate', - 'news', - 'nfs', - 'nntp', - 'opaquelocktoken', - 'pop', - 'pres', - 'prospero', - 'rtsp', - 'service', - 'shttp', - 'sip', - 'sips', - 'snmp', - 'soap.beep', - 'soap.beeps', - 'tag', - 'tel', - 'telnet', - 'tftp', - 'thismessage', - 'tip', - 'tv', - 'urn', - 'vemmi', - 'wais', - 'xmlrpc.beep', - 'xmlrpc.beep', - 'xmpp', - 'z39.50r', - 'z39.50s', -] -unofficial_url_schemes = [ - 'about', - 'adiumxtra', - 'aim', - 'afp', - 'aw', - 'callto', - 'chrome', - 'cvs', - 'ed2k', - 'feed', - 'fish', - 'gg', - 'gizmoproject', - 'iax2', - 'irc', - 'ircs', - 'itms', - 'jar', - 'javascript', - 'keyparc', - 'lastfm', - 'ldaps', - 'magnet', - 'mms', - 'msnim', - 'mvn', - 'notes', - 'nsfw', - 'psyc', - 'paparazzi:http', - 'rmi', - 'rsync', - 'secondlife', - 'sgn', - 'skype', - 'ssh', - 'sftp', - 'smb', - 'sms', - 'soldat', - 'steam', - 'svn', - 'teamspeak', - 'unreal', - 'ut2004', - 'ventrilo', - 'view-source', - 'webcal', - 'wyciwyg', - 'xfire', - 'xri', - 'ymsgr', -] -all_url_schemes = [None] + official_url_schemes + unofficial_url_schemes -http_schemes = [None, 'http', 'https'] - -# Defined in RFC 3490, Section 3.1, Requirement #1 -# Use this regex to split the authority component of a unicode URL into -# its component labels -label_split_regex = re.compile(u'[\u002e\u3002\uff0e\uff61]') - - -def escape_unicode(string): - """ - Converts a unicode string into US-ASCII, using a simple conversion scheme. - Each unicode character that does not have a US-ASCII equivalent is - converted into a URL escaped form based on its hexadecimal value. - For example, the unicode character '\u4e86' will become the string '%4e%86' - - Args: - string: unicode string, the unicode string to convert into an - escaped US-ASCII form - - Returns: - string: the US-ASCII escaped form of the inputted string - - @author: Jonathan Benn - """ - returnValue = StringIO() - - for character in string: - code = ord(character) - if code > 0x7F: - hexCode = hex(code) - returnValue.write('%' + hexCode[2:4] + '%' + hexCode[4:6]) - else: - returnValue.write(character) - - return returnValue.getvalue() - - -def unicode_to_ascii_authority(authority): - """ - Follows the steps in RFC 3490, Section 4 to convert a unicode authority - string into its ASCII equivalent. - For example, u'www.Alliancefran\xe7aise.nu' will be converted into - 'www.xn--alliancefranaise-npb.nu' - - Args: - authority: unicode string, the URL authority component to convert, - e.g. u'www.Alliancefran\xe7aise.nu' - - Returns: - string: the US-ASCII character equivalent to the inputed authority, - e.g. 'www.xn--alliancefranaise-npb.nu' - - Raises: - Exception: if the function is not able to convert the inputed - authority - - @author: Jonathan Benn - """ - # RFC 3490, Section 4, Step 1 - # The encodings.idna Python module assumes that AllowUnassigned == True - - # RFC 3490, Section 4, Step 2 - labels = label_split_regex.split(authority) - - # RFC 3490, Section 4, Step 3 - # The encodings.idna Python module assumes that UseSTD3ASCIIRules == False - - # RFC 3490, Section 4, Step 4 - # We use the ToASCII operation because we are about to put the authority - # into an IDN-unaware slot - asciiLabels = [] - import encodings.idna - for label in labels: - if label: - asciiLabels.append(to_native(encodings.idna.ToASCII(label))) - else: - # encodings.idna.ToASCII does not accept an empty string, but - # it is necessary for us to allow for empty labels so that we - # don't modify the URL - asciiLabels.append('') - # RFC 3490, Section 4, Step 5 - return str(reduce(lambda x, y: x + unichr(0x002E) + y, asciiLabels)) - - -def unicode_to_ascii_url(url, prepend_scheme): - """ - Converts the inputed unicode url into a US-ASCII equivalent. This function - goes a little beyond RFC 3490, which is limited in scope to the domain name - (authority) only. Here, the functionality is expanded to what was observed - on Wikipedia on 2009-Jan-22: - - Component Can Use Unicode? - --------- ---------------- - scheme No - authority Yes - path Yes - query Yes - fragment No - - The authority component gets converted to punycode, but occurrences of - unicode in other components get converted into a pair of URI escapes (we - assume 4-byte unicode). E.g. the unicode character U+4E2D will be - converted into '%4E%2D'. Testing with Firefox v3.0.5 has shown that it can - understand this kind of URI encoding. - - Args: - url: unicode string, the URL to convert from unicode into US-ASCII - prepend_scheme: string, a protocol scheme to prepend to the URL if - we're having trouble parsing it. - e.g. "http". Input None to disable this functionality - - Returns: - string: a US-ASCII equivalent of the inputed url - - @author: Jonathan Benn - """ - # convert the authority component of the URL into an ASCII punycode string, - # but encode the rest using the regular URI character encoding - components = urlparse.urlparse(url) - prepended = False - # If no authority was found - if not components.netloc: - # Try appending a scheme to see if that fixes the problem - scheme_to_prepend = prepend_scheme or 'http' - components = urlparse.urlparse(to_unicode(scheme_to_prepend) + u'://' + url) - prepended = True - - # if we still can't find the authority - if not components.netloc: - raise Exception('No authority component found, ' + - 'could not decode unicode to US-ASCII') - - # We're here if we found an authority, let's rebuild the URL - scheme = components.scheme - authority = components.netloc - path = components.path - query = components.query - fragment = components.fragment - - if prepended: - scheme = '' - - unparsed = urlparse.urlunparse((scheme, - unicode_to_ascii_authority(authority), - escape_unicode(path), - '', - escape_unicode(query), - str(fragment))) - if unparsed.startswith('//'): - unparsed = unparsed[2:] # Remove the // urlunparse puts in the beginning - return unparsed - - -class IS_GENERIC_URL(Validator): - """ - Rejects a URL string if any of the following is true: - * The string is empty or None - * The string uses characters that are not allowed in a URL - * The URL scheme specified (if one is specified) is not valid - - Based on RFC 2396: http://www.faqs.org/rfcs/rfc2396.html - - This function only checks the URL's syntax. It does not check that the URL - points to a real document, for example, or that it otherwise makes sense - semantically. This function does automatically prepend 'http://' in front - of a URL if and only if that's necessary to successfully parse the URL. - Please note that a scheme will be prepended only for rare cases - (e.g. 'google.ca:80') - - The list of allowed schemes is customizable with the allowed_schemes - parameter. If you exclude None from the list, then abbreviated URLs - (lacking a scheme such as 'http') will be rejected. - - The default prepended scheme is customizable with the prepend_scheme - parameter. If you set prepend_scheme to None then prepending will be - disabled. URLs that require prepending to parse will still be accepted, - but the return value will not be modified. - - @author: Jonathan Benn - - >>> IS_GENERIC_URL()('http://user@abc.com') - ('http://user@abc.com', None) - - Args: - error_message: a string, the error message to give the end user - if the URL does not validate - allowed_schemes: a list containing strings or None. Each element - is a scheme the inputed URL is allowed to use - prepend_scheme: a string, this scheme is prepended if it's - necessary to make the URL valid - - """ - - def __init__( - self, - error_message='Enter a valid URL', - allowed_schemes=None, - prepend_scheme=None, - ): - - self.error_message = error_message - if allowed_schemes is None: - self.allowed_schemes = all_url_schemes - else: - self.allowed_schemes = allowed_schemes - self.prepend_scheme = prepend_scheme - if self.prepend_scheme not in self.allowed_schemes: - raise SyntaxError("prepend_scheme='%s' is not in allowed_schemes=%s" - % (self.prepend_scheme, self.allowed_schemes)) - - GENERIC_URL = re.compile(r"%[^0-9A-Fa-f]{2}|%[^0-9A-Fa-f][0-9A-Fa-f]|%[0-9A-Fa-f][^0-9A-Fa-f]|%$|%[0-9A-Fa-f]$|%[^0-9A-Fa-f]$") - GENERIC_URL_VALID = re.compile(r"[A-Za-z0-9;/?:@&=+$,\-_\.!~*'\(\)%]+$") - URL_FRAGMENT_VALID = re.compile(r"[|A-Za-z0-9;/?:@&=+$,\-_\.!~*'\(\)%]+$") - - def __call__(self, value): - """ - Args: - value: a string, the URL to validate - - Returns: - a tuple, where tuple[0] is the inputed value (possible - prepended with prepend_scheme), and tuple[1] is either - None (success!) or the string error_message - """ - - # if we dont have anything or the URL misuses the '%' character - - if not value or self.GENERIC_URL.search(value): - return (value, translate(self.error_message)) - - if '#' in value: - url, fragment_part = value.split('#') - else: - url, fragment_part = value, '' - # if the URL is only composed of valid characters - if self.GENERIC_URL_VALID.match(url) and (not fragment_part or self.URL_FRAGMENT_VALID.match(fragment_part)): - # Then parse the URL into its components and check on - try: - components = urlparse.urlparse(urllib_unquote(value))._asdict() - except ValueError: - return (value, translate(self.error_message)) - - # Clean up the scheme before we check it - scheme = components['scheme'] - if len(scheme) == 0: - scheme = None - else: - scheme = components['scheme'].lower() - # If the scheme doesn't really exists - if scheme not in self.allowed_schemes or not scheme and ':' in components['path']: - # for the possible case of abbreviated URLs with - # ports, check to see if adding a valid scheme fixes - # the problem (but only do this if it doesn't have - # one already!) - if '://' not in value and None in self.allowed_schemes: - schemeToUse = self.prepend_scheme or 'http' - prependTest = self.__call__( - schemeToUse + '://' + value) - # if the prepend test succeeded - if prependTest[1] is None: - # if prepending in the output is enabled - if self.prepend_scheme: - return prependTest - else: - return (value, None) - else: - return (value, None) - # else the URL is not valid - return (value, translate(self.error_message)) - -# Sources (obtained 2017-Nov-11): -# http://data.iana.org/TLD/tlds-alpha-by-domain.txt -# see scripts/parse_top_level_domains.py for an easy update - -official_top_level_domains = [ - # a - 'aaa', 'aarp', 'abarth', 'abb', 'abbott', 'abbvie', 'abc', - 'able', 'abogado', 'abudhabi', 'ac', 'academy', 'accenture', - 'accountant', 'accountants', 'aco', 'active', 'actor', 'ad', - 'adac', 'ads', 'adult', 'ae', 'aeg', 'aero', 'aetna', 'af', - 'afamilycompany', 'afl', 'africa', 'ag', 'agakhan', 'agency', - 'ai', 'aig', 'aigo', 'airbus', 'airforce', 'airtel', 'akdn', - 'al', 'alfaromeo', 'alibaba', 'alipay', 'allfinanz', 'allstate', - 'ally', 'alsace', 'alstom', 'am', 'americanexpress', - 'americanfamily', 'amex', 'amfam', 'amica', 'amsterdam', - 'analytics', 'android', 'anquan', 'anz', 'ao', 'aol', - 'apartments', 'app', 'apple', 'aq', 'aquarelle', 'ar', 'arab', - 'aramco', 'archi', 'army', 'arpa', 'art', 'arte', 'as', 'asda', - 'asia', 'associates', 'at', 'athleta', 'attorney', 'au', - 'auction', 'audi', 'audible', 'audio', 'auspost', 'author', - 'auto', 'autos', 'avianca', 'aw', 'aws', 'ax', 'axa', 'az', - 'azure', - # b - 'ba', 'baby', 'baidu', 'banamex', 'bananarepublic', 'band', - 'bank', 'bar', 'barcelona', 'barclaycard', 'barclays', - 'barefoot', 'bargains', 'baseball', 'basketball', 'bauhaus', - 'bayern', 'bb', 'bbc', 'bbt', 'bbva', 'bcg', 'bcn', 'bd', 'be', - 'beats', 'beauty', 'beer', 'bentley', 'berlin', 'best', - 'bestbuy', 'bet', 'bf', 'bg', 'bh', 'bharti', 'bi', 'bible', - 'bid', 'bike', 'bing', 'bingo', 'bio', 'biz', 'bj', 'black', - 'blackfriday', 'blanco', 'blockbuster', 'blog', 'bloomberg', - 'blue', 'bm', 'bms', 'bmw', 'bn', 'bnl', 'bnpparibas', 'bo', - 'boats', 'boehringer', 'bofa', 'bom', 'bond', 'boo', 'book', - 'booking', 'boots', 'bosch', 'bostik', 'boston', 'bot', - 'boutique', 'box', 'br', 'bradesco', 'bridgestone', 'broadway', - 'broker', 'brother', 'brussels', 'bs', 'bt', 'budapest', - 'bugatti', 'build', 'builders', 'business', 'buy', 'buzz', 'bv', - 'bw', 'by', 'bz', 'bzh', - # c - 'ca', 'cab', 'cafe', 'cal', 'call', 'calvinklein', 'cam', - 'camera', 'camp', 'cancerresearch', 'canon', 'capetown', - 'capital', 'capitalone', 'car', 'caravan', 'cards', 'care', - 'career', 'careers', 'cars', 'cartier', 'casa', 'case', 'caseih', - 'cash', 'casino', 'cat', 'catering', 'catholic', 'cba', 'cbn', - 'cbre', 'cbs', 'cc', 'cd', 'ceb', 'center', 'ceo', 'cern', 'cf', - 'cfa', 'cfd', 'cg', 'ch', 'chanel', 'channel', 'chase', 'chat', - 'cheap', 'chintai', 'christmas', 'chrome', 'chrysler', 'church', - 'ci', 'cipriani', 'circle', 'cisco', 'citadel', 'citi', 'citic', - 'city', 'cityeats', 'ck', 'cl', 'claims', 'cleaning', 'click', - 'clinic', 'clinique', 'clothing', 'cloud', 'club', 'clubmed', - 'cm', 'cn', 'co', 'coach', 'codes', 'coffee', 'college', - 'cologne', 'com', 'comcast', 'commbank', 'community', 'company', - 'compare', 'computer', 'comsec', 'condos', 'construction', - 'consulting', 'contact', 'contractors', 'cooking', - 'cookingchannel', 'cool', 'coop', 'corsica', 'country', 'coupon', - 'coupons', 'courses', 'cr', 'credit', 'creditcard', - 'creditunion', 'cricket', 'crown', 'crs', 'cruise', 'cruises', - 'csc', 'cu', 'cuisinella', 'cv', 'cw', 'cx', 'cy', 'cymru', - 'cyou', 'cz', - # d - 'dabur', 'dad', 'dance', 'data', 'date', 'dating', 'datsun', - 'day', 'dclk', 'dds', 'de', 'deal', 'dealer', 'deals', 'degree', - 'delivery', 'dell', 'deloitte', 'delta', 'democrat', 'dental', - 'dentist', 'desi', 'design', 'dev', 'dhl', 'diamonds', 'diet', - 'digital', 'direct', 'directory', 'discount', 'discover', 'dish', - 'diy', 'dj', 'dk', 'dm', 'dnp', 'do', 'docs', 'doctor', 'dodge', - 'dog', 'doha', 'domains', 'dot', 'download', 'drive', 'dtv', - 'dubai', 'duck', 'dunlop', 'duns', 'dupont', 'durban', 'dvag', - 'dvr', 'dz', - # e - 'earth', 'eat', 'ec', 'eco', 'edeka', 'edu', 'education', 'ee', - 'eg', 'email', 'emerck', 'energy', 'engineer', 'engineering', - 'enterprises', 'epost', 'epson', 'equipment', 'er', 'ericsson', - 'erni', 'es', 'esq', 'estate', 'esurance', 'et', 'etisalat', - 'eu', 'eurovision', 'eus', 'events', 'everbank', 'exchange', - 'expert', 'exposed', 'express', 'extraspace', - # f - 'fage', 'fail', 'fairwinds', 'faith', 'family', 'fan', 'fans', - 'farm', 'farmers', 'fashion', 'fast', 'fedex', 'feedback', - 'ferrari', 'ferrero', 'fi', 'fiat', 'fidelity', 'fido', 'film', - 'final', 'finance', 'financial', 'fire', 'firestone', 'firmdale', - 'fish', 'fishing', 'fit', 'fitness', 'fj', 'fk', 'flickr', - 'flights', 'flir', 'florist', 'flowers', 'fly', 'fm', 'fo', - 'foo', 'food', 'foodnetwork', 'football', 'ford', 'forex', - 'forsale', 'forum', 'foundation', 'fox', 'fr', 'free', - 'fresenius', 'frl', 'frogans', 'frontdoor', 'frontier', 'ftr', - 'fujitsu', 'fujixerox', 'fun', 'fund', 'furniture', 'futbol', - 'fyi', - # g - 'ga', 'gal', 'gallery', 'gallo', 'gallup', 'game', 'games', - 'gap', 'garden', 'gb', 'gbiz', 'gd', 'gdn', 'ge', 'gea', 'gent', - 'genting', 'george', 'gf', 'gg', 'ggee', 'gh', 'gi', 'gift', - 'gifts', 'gives', 'giving', 'gl', 'glade', 'glass', 'gle', - 'global', 'globo', 'gm', 'gmail', 'gmbh', 'gmo', 'gmx', 'gn', - 'godaddy', 'gold', 'goldpoint', 'golf', 'goo', 'goodhands', - 'goodyear', 'goog', 'google', 'gop', 'got', 'gov', 'gp', 'gq', - 'gr', 'grainger', 'graphics', 'gratis', 'green', 'gripe', - 'grocery', 'group', 'gs', 'gt', 'gu', 'guardian', 'gucci', - 'guge', 'guide', 'guitars', 'guru', 'gw', 'gy', - # h - 'hair', 'hamburg', 'hangout', 'haus', 'hbo', 'hdfc', 'hdfcbank', - 'health', 'healthcare', 'help', 'helsinki', 'here', 'hermes', - 'hgtv', 'hiphop', 'hisamitsu', 'hitachi', 'hiv', 'hk', 'hkt', - 'hm', 'hn', 'hockey', 'holdings', 'holiday', 'homedepot', - 'homegoods', 'homes', 'homesense', 'honda', 'honeywell', 'horse', - 'hospital', 'host', 'hosting', 'hot', 'hoteles', 'hotels', - 'hotmail', 'house', 'how', 'hr', 'hsbc', 'ht', 'hu', 'hughes', - 'hyatt', 'hyundai', - # i - 'ibm', 'icbc', 'ice', 'icu', 'id', 'ie', 'ieee', 'ifm', 'ikano', - 'il', 'im', 'imamat', 'imdb', 'immo', 'immobilien', 'in', - 'industries', 'infiniti', 'info', 'ing', 'ink', 'institute', - 'insurance', 'insure', 'int', 'intel', 'international', 'intuit', - 'investments', 'io', 'ipiranga', 'iq', 'ir', 'irish', 'is', - 'iselect', 'ismaili', 'ist', 'istanbul', 'it', 'itau', 'itv', - 'iveco', 'iwc', - # j - 'jaguar', 'java', 'jcb', 'jcp', 'je', 'jeep', 'jetzt', 'jewelry', - 'jio', 'jlc', 'jll', 'jm', 'jmp', 'jnj', 'jo', 'jobs', 'joburg', - 'jot', 'joy', 'jp', 'jpmorgan', 'jprs', 'juegos', 'juniper', - # k - 'kaufen', 'kddi', 'ke', 'kerryhotels', 'kerrylogistics', - 'kerryproperties', 'kfh', 'kg', 'kh', 'ki', 'kia', 'kim', - 'kinder', 'kindle', 'kitchen', 'kiwi', 'km', 'kn', 'koeln', - 'komatsu', 'kosher', 'kp', 'kpmg', 'kpn', 'kr', 'krd', 'kred', - 'kuokgroup', 'kw', 'ky', 'kyoto', 'kz', - # l - 'la', 'lacaixa', 'ladbrokes', 'lamborghini', 'lamer', - 'lancaster', 'lancia', 'lancome', 'land', 'landrover', 'lanxess', - 'lasalle', 'lat', 'latino', 'latrobe', 'law', 'lawyer', 'lb', - 'lc', 'lds', 'lease', 'leclerc', 'lefrak', 'legal', 'lego', - 'lexus', 'lgbt', 'li', 'liaison', 'lidl', 'life', - 'lifeinsurance', 'lifestyle', 'lighting', 'like', 'lilly', - 'limited', 'limo', 'lincoln', 'linde', 'link', 'lipsy', 'live', - 'living', 'lixil', 'lk', 'loan', 'loans', 'localhost', 'locker', - 'locus', 'loft', 'lol', 'london', 'lotte', 'lotto', 'love', - 'lpl', 'lplfinancial', 'lr', 'ls', 'lt', 'ltd', 'ltda', 'lu', - 'lundbeck', 'lupin', 'luxe', 'luxury', 'lv', 'ly', - # m - 'ma', 'macys', 'madrid', 'maif', 'maison', 'makeup', 'man', - 'management', 'mango', 'map', 'market', 'marketing', 'markets', - 'marriott', 'marshalls', 'maserati', 'mattel', 'mba', 'mc', - 'mckinsey', 'md', 'me', 'med', 'media', 'meet', 'melbourne', - 'meme', 'memorial', 'men', 'menu', 'meo', 'merckmsd', 'metlife', - 'mg', 'mh', 'miami', 'microsoft', 'mil', 'mini', 'mint', 'mit', - 'mitsubishi', 'mk', 'ml', 'mlb', 'mls', 'mm', 'mma', 'mn', 'mo', - 'mobi', 'mobile', 'mobily', 'moda', 'moe', 'moi', 'mom', - 'monash', 'money', 'monster', 'mopar', 'mormon', 'mortgage', - 'moscow', 'moto', 'motorcycles', 'mov', 'movie', 'movistar', - 'mp', 'mq', 'mr', 'ms', 'msd', 'mt', 'mtn', 'mtr', 'mu', - 'museum', 'mutual', 'mv', 'mw', 'mx', 'my', 'mz', - # n - 'na', 'nab', 'nadex', 'nagoya', 'name', 'nationwide', 'natura', - 'navy', 'nba', 'nc', 'ne', 'nec', 'net', 'netbank', 'netflix', - 'network', 'neustar', 'new', 'newholland', 'news', 'next', - 'nextdirect', 'nexus', 'nf', 'nfl', 'ng', 'ngo', 'nhk', 'ni', - 'nico', 'nike', 'nikon', 'ninja', 'nissan', 'nissay', 'nl', 'no', - 'nokia', 'northwesternmutual', 'norton', 'now', 'nowruz', - 'nowtv', 'np', 'nr', 'nra', 'nrw', 'ntt', 'nu', 'nyc', 'nz', - # o - 'obi', 'observer', 'off', 'office', 'okinawa', 'olayan', - 'olayangroup', 'oldnavy', 'ollo', 'om', 'omega', 'one', 'ong', - 'onl', 'online', 'onyourside', 'ooo', 'open', 'oracle', 'orange', - 'org', 'organic', 'origins', 'osaka', 'otsuka', 'ott', 'ovh', - # p - 'pa', 'page', 'panasonic', 'panerai', 'paris', 'pars', - 'partners', 'parts', 'party', 'passagens', 'pay', 'pccw', 'pe', - 'pet', 'pf', 'pfizer', 'pg', 'ph', 'pharmacy', 'phd', 'philips', - 'phone', 'photo', 'photography', 'photos', 'physio', 'piaget', - 'pics', 'pictet', 'pictures', 'pid', 'pin', 'ping', 'pink', - 'pioneer', 'pizza', 'pk', 'pl', 'place', 'play', 'playstation', - 'plumbing', 'plus', 'pm', 'pn', 'pnc', 'pohl', 'poker', - 'politie', 'porn', 'post', 'pr', 'pramerica', 'praxi', 'press', - 'prime', 'pro', 'prod', 'productions', 'prof', 'progressive', - 'promo', 'properties', 'property', 'protection', 'pru', - 'prudential', 'ps', 'pt', 'pub', 'pw', 'pwc', 'py', - # q - 'qa', 'qpon', 'quebec', 'quest', 'qvc', - # r - 'racing', 'radio', 'raid', 're', 'read', 'realestate', 'realtor', - 'realty', 'recipes', 'red', 'redstone', 'redumbrella', 'rehab', - 'reise', 'reisen', 'reit', 'reliance', 'ren', 'rent', 'rentals', - 'repair', 'report', 'republican', 'rest', 'restaurant', 'review', - 'reviews', 'rexroth', 'rich', 'richardli', 'ricoh', - 'rightathome', 'ril', 'rio', 'rip', 'rmit', 'ro', 'rocher', - 'rocks', 'rodeo', 'rogers', 'room', 'rs', 'rsvp', 'ru', 'rugby', - 'ruhr', 'run', 'rw', 'rwe', 'ryukyu', - # s - 'sa', 'saarland', 'safe', 'safety', 'sakura', 'sale', 'salon', - 'samsclub', 'samsung', 'sandvik', 'sandvikcoromant', 'sanofi', - 'sap', 'sapo', 'sarl', 'sas', 'save', 'saxo', 'sb', 'sbi', 'sbs', - 'sc', 'sca', 'scb', 'schaeffler', 'schmidt', 'scholarships', - 'school', 'schule', 'schwarz', 'science', 'scjohnson', 'scor', - 'scot', 'sd', 'se', 'search', 'seat', 'secure', 'security', - 'seek', 'select', 'sener', 'services', 'ses', 'seven', 'sew', - 'sex', 'sexy', 'sfr', 'sg', 'sh', 'shangrila', 'sharp', 'shaw', - 'shell', 'shia', 'shiksha', 'shoes', 'shop', 'shopping', - 'shouji', 'show', 'showtime', 'shriram', 'si', 'silk', 'sina', - 'singles', 'site', 'sj', 'sk', 'ski', 'skin', 'sky', 'skype', - 'sl', 'sling', 'sm', 'smart', 'smile', 'sn', 'sncf', 'so', - 'soccer', 'social', 'softbank', 'software', 'sohu', 'solar', - 'solutions', 'song', 'sony', 'soy', 'space', 'spiegel', 'spot', - 'spreadbetting', 'sr', 'srl', 'srt', 'st', 'stada', 'staples', - 'star', 'starhub', 'statebank', 'statefarm', 'statoil', 'stc', - 'stcgroup', 'stockholm', 'storage', 'store', 'stream', 'studio', - 'study', 'style', 'su', 'sucks', 'supplies', 'supply', 'support', - 'surf', 'surgery', 'suzuki', 'sv', 'swatch', 'swiftcover', - 'swiss', 'sx', 'sy', 'sydney', 'symantec', 'systems', 'sz', - # t - 'tab', 'taipei', 'talk', 'taobao', 'target', 'tatamotors', - 'tatar', 'tattoo', 'tax', 'taxi', 'tc', 'tci', 'td', 'tdk', - 'team', 'tech', 'technology', 'tel', 'telecity', 'telefonica', - 'temasek', 'tennis', 'teva', 'tf', 'tg', 'th', 'thd', 'theater', - 'theatre', 'tiaa', 'tickets', 'tienda', 'tiffany', 'tips', - 'tires', 'tirol', 'tj', 'tjmaxx', 'tjx', 'tk', 'tkmaxx', 'tl', - 'tm', 'tmall', 'tn', 'to', 'today', 'tokyo', 'tools', 'top', - 'toray', 'toshiba', 'total', 'tours', 'town', 'toyota', 'toys', - 'tr', 'trade', 'trading', 'training', 'travel', 'travelchannel', - 'travelers', 'travelersinsurance', 'trust', 'trv', 'tt', 'tube', - 'tui', 'tunes', 'tushu', 'tv', 'tvs', 'tw', 'tz', - # u - 'ua', 'ubank', 'ubs', 'uconnect', 'ug', 'uk', 'unicom', - 'university', 'uno', 'uol', 'ups', 'us', 'uy', 'uz', - # v - 'va', 'vacations', 'vana', 'vanguard', 'vc', 've', 'vegas', - 'ventures', 'verisign', 'versicherung', 'vet', 'vg', 'vi', - 'viajes', 'video', 'vig', 'viking', 'villas', 'vin', 'vip', - 'virgin', 'visa', 'vision', 'vista', 'vistaprint', 'viva', - 'vivo', 'vlaanderen', 'vn', 'vodka', 'volkswagen', 'volvo', - 'vote', 'voting', 'voto', 'voyage', 'vu', 'vuelos', - # w - 'wales', 'walmart', 'walter', 'wang', 'wanggou', 'warman', - 'watch', 'watches', 'weather', 'weatherchannel', 'webcam', - 'weber', 'website', 'wed', 'wedding', 'weibo', 'weir', 'wf', - 'whoswho', 'wien', 'wiki', 'williamhill', 'win', 'windows', - 'wine', 'winners', 'wme', 'wolterskluwer', 'woodside', 'work', - 'works', 'world', 'wow', 'ws', 'wtc', 'wtf', - # x - 'xbox', 'xerox', 'xfinity', 'xihuan', 'xin', 'xn--11b4c3d', - 'xn--1ck2e1b', 'xn--1qqw23a', 'xn--2scrj9c', 'xn--30rr7y', - 'xn--3bst00m', 'xn--3ds443g', 'xn--3e0b707e', 'xn--3hcrj9c', - 'xn--3oq18vl8pn36a', 'xn--3pxu8k', 'xn--42c2d9a', 'xn--45br5cyl', - 'xn--45brj9c', 'xn--45q11c', 'xn--4gbrim', 'xn--54b7fta0cc', - 'xn--55qw42g', 'xn--55qx5d', 'xn--5su34j936bgsg', 'xn--5tzm5g', - 'xn--6frz82g', 'xn--6qq986b3xl', 'xn--80adxhks', 'xn--80ao21a', - 'xn--80aqecdr1a', 'xn--80asehdb', 'xn--80aswg', 'xn--8y0a063a', - 'xn--90a3ac', 'xn--90ae', 'xn--90ais', 'xn--9dbq2a', - 'xn--9et52u', 'xn--9krt00a', 'xn--b4w605ferd', - 'xn--bck1b9a5dre4c', 'xn--c1avg', 'xn--c2br7g', 'xn--cck2b3b', - 'xn--cg4bki', 'xn--clchc0ea0b2g2a9gcd', 'xn--czr694b', - 'xn--czrs0t', 'xn--czru2d', 'xn--d1acj3b', 'xn--d1alf', - 'xn--e1a4c', 'xn--eckvdtc9d', 'xn--efvy88h', 'xn--estv75g', - 'xn--fct429k', 'xn--fhbei', 'xn--fiq228c5hs', 'xn--fiq64b', - 'xn--fiqs8s', 'xn--fiqz9s', 'xn--fjq720a', 'xn--flw351e', - 'xn--fpcrj9c3d', 'xn--fzc2c9e2c', 'xn--fzys8d69uvgm', - 'xn--g2xx48c', 'xn--gckr3f0f', 'xn--gecrj9c', 'xn--gk3at1e', - 'xn--h2breg3eve', 'xn--h2brj9c', 'xn--h2brj9c8c', 'xn--hxt814e', - 'xn--i1b6b1a6a2e', 'xn--imr513n', 'xn--io0a7i', 'xn--j1aef', - 'xn--j1amh', 'xn--j6w193g', 'xn--jlq61u9w7b', 'xn--jvr189m', - 'xn--kcrx77d1x4a', 'xn--kprw13d', 'xn--kpry57d', 'xn--kpu716f', - 'xn--kput3i', 'xn--l1acc', 'xn--lgbbat1ad8j', 'xn--mgb9awbf', - 'xn--mgba3a3ejt', 'xn--mgba3a4f16a', 'xn--mgba7c0bbn0a', - 'xn--mgbaakc7dvf', 'xn--mgbaam7a8h', 'xn--mgbab2bd', - 'xn--mgbai9azgqp6j', 'xn--mgbayh7gpa', 'xn--mgbb9fbpob', - 'xn--mgbbh1a', 'xn--mgbbh1a71e', 'xn--mgbc0a9azcg', - 'xn--mgbca7dzdo', 'xn--mgberp4a5d4ar', 'xn--mgbgu82a', - 'xn--mgbi4ecexp', 'xn--mgbpl2fh', 'xn--mgbt3dhd', 'xn--mgbtx2b', - 'xn--mgbx4cd0ab', 'xn--mix891f', 'xn--mk1bu44c', 'xn--mxtq1m', - 'xn--ngbc5azd', 'xn--ngbe9e0a', 'xn--ngbrx', 'xn--node', - 'xn--nqv7f', 'xn--nqv7fs00ema', 'xn--nyqy26a', 'xn--o3cw4h', - 'xn--ogbpf8fl', 'xn--p1acf', 'xn--p1ai', 'xn--pbt977c', - 'xn--pgbs0dh', 'xn--pssy2u', 'xn--q9jyb4c', 'xn--qcka1pmc', - 'xn--qxam', 'xn--rhqv96g', 'xn--rovu88b', 'xn--rvc1e0am3e', - 'xn--s9brj9c', 'xn--ses554g', 'xn--t60b56a', 'xn--tckwe', - 'xn--tiq49xqyj', 'xn--unup4y', 'xn--vermgensberater-ctb', - 'xn--vermgensberatung-pwb', 'xn--vhquv', 'xn--vuq861b', - 'xn--w4r85el8fhu5dnra', 'xn--w4rs40l', 'xn--wgbh1c', - 'xn--wgbl6a', 'xn--xhq521b', 'xn--xkc2al3hye2a', - 'xn--xkc2dl3a5ee0h', 'xn--y9a3aq', 'xn--yfro4i67o', - 'xn--ygbi2ammx', 'xn--zfr164b', 'xperia', 'xxx', 'xyz', - # y - 'yachts', 'yahoo', 'yamaxun', 'yandex', 'ye', 'yodobashi', - 'yoga', 'yokohama', 'you', 'youtube', 'yt', 'yun', - # z - 'za', 'zappos', 'zara', 'zero', 'zip', 'zippo', 'zm', 'zone', - 'zuerich', 'zw' -] - - -class IS_HTTP_URL(Validator): - """ - Rejects a URL string if any of the following is true: - * The string is empty or None - * The string uses characters that are not allowed in a URL - * The string breaks any of the HTTP syntactic rules - * The URL scheme specified (if one is specified) is not 'http' or 'https' - * The top-level domain (if a host name is specified) does not exist - - Based on RFC 2616: http://www.faqs.org/rfcs/rfc2616.html - - This function only checks the URL's syntax. It does not check that the URL - points to a real document, for example, or that it otherwise makes sense - semantically. This function does automatically prepend 'http://' in front - of a URL in the case of an abbreviated URL (e.g. 'google.ca'). - - The list of allowed schemes is customizable with the allowed_schemes - parameter. If you exclude None from the list, then abbreviated URLs - (lacking a scheme such as 'http') will be rejected. - - The default prepended scheme is customizable with the prepend_scheme - parameter. If you set prepend_scheme to None then prepending will be - disabled. URLs that require prepending to parse will still be accepted, - but the return value will not be modified. - - @author: Jonathan Benn - - >>> IS_HTTP_URL()('http://1.2.3.4') - ('http://1.2.3.4', None) - >>> IS_HTTP_URL()('http://abc.com') - ('http://abc.com', None) - >>> IS_HTTP_URL()('https://abc.com') - ('https://abc.com', None) - >>> IS_HTTP_URL()('httpx://abc.com') - ('httpx://abc.com', 'enter a valid URL') - >>> IS_HTTP_URL()('http://abc.com:80') - ('http://abc.com:80', None) - >>> IS_HTTP_URL()('http://user@abc.com') - ('http://user@abc.com', None) - >>> IS_HTTP_URL()('http://user@1.2.3.4') - ('http://user@1.2.3.4', None) - - Args: - error_message: a string, the error message to give the end user - if the URL does not validate - allowed_schemes: a list containing strings or None. Each element - is a scheme the inputed URL is allowed to use - prepend_scheme: a string, this scheme is prepended if it's - necessary to make the URL valid - """ - - GENERIC_VALID_IP = re.compile( - "([\w.!~*'|;:&=+$,-]+@)?\d+\.\d+\.\d+\.\d+(:\d*)*$") - GENERIC_VALID_DOMAIN = re.compile("([\w.!~*'|;:&=+$,-]+@)?(([A-Za-z0-9]+[A-Za-z0-9\-]*[A-Za-z0-9]+\.)*([A-Za-z0-9]+\.)*)*([A-Za-z]+[A-Za-z0-9\-]*[A-Za-z0-9]+)\.?(:\d*)*$") - - def __init__( - self, - error_message='Enter a valid URL', - allowed_schemes=None, - prepend_scheme='http', - allowed_tlds=None - ): - - self.error_message = error_message - if allowed_schemes is None: - self.allowed_schemes = http_schemes - else: - self.allowed_schemes = allowed_schemes - if allowed_tlds is None: - self.allowed_tlds = official_top_level_domains - else: - self.allowed_tlds = allowed_tlds - self.prepend_scheme = prepend_scheme - - for i in self.allowed_schemes: - if i not in http_schemes: - raise SyntaxError("allowed_scheme value '%s' is not in %s" % - (i, http_schemes)) - - if self.prepend_scheme not in self.allowed_schemes: - raise SyntaxError("prepend_scheme='%s' is not in allowed_schemes=%s" % - (self.prepend_scheme, self.allowed_schemes)) - - def __call__(self, value): - """ - Args: - value: a string, the URL to validate - - Returns: - a tuple, where tuple[0] is the inputed value - (possible prepended with prepend_scheme), and tuple[1] is either - None (success!) or the string error_message - """ - try: - # if the URL passes generic validation - x = IS_GENERIC_URL(error_message=self.error_message, - allowed_schemes=self.allowed_schemes, - prepend_scheme=self.prepend_scheme) - if x(value)[1] is None: - components = urlparse.urlparse(value) - authority = components.netloc - # if there is an authority component - if authority: - # if authority is a valid IP address - if self.GENERIC_VALID_IP.match(authority): - # Then this HTTP URL is valid - return (value, None) - else: - # else if authority is a valid domain name - domainMatch = self.GENERIC_VALID_DOMAIN.match( - authority) - if domainMatch: - # if the top-level domain really exists - if domainMatch.group(5).lower()\ - in self.allowed_tlds: - # Then this HTTP URL is valid - return (value, None) - else: - # else this is a relative/abbreviated URL, which will parse - # into the URL's path component - path = components.path - # relative case: if this is a valid path (if it starts with - # a slash) - if path.startswith('/'): - # Then this HTTP URL is valid - return (value, None) - else: - # abbreviated case: if we haven't already, prepend a - # scheme and see if it fixes the problem - if '://' not in value and None in self.allowed_schemes: - schemeToUse = self.prepend_scheme or 'http' - prependTest = self.__call__(schemeToUse - + '://' + value) - # if the prepend test succeeded - if prependTest[1] is None: - # if prepending in the output is enabled - if self.prepend_scheme: - return prependTest - else: - # else return the original, non-prepended - # value - return (value, None) - except: - pass - # else the HTTP URL is not valid - return (value, translate(self.error_message)) - - -class IS_URL(Validator): - """ - Rejects a URL string if any of the following is true: - - * The string is empty or None - * The string uses characters that are not allowed in a URL - * The string breaks any of the HTTP syntactic rules - * The URL scheme specified (if one is specified) is not 'http' or 'https' - * The top-level domain (if a host name is specified) does not exist - - (These rules are based on RFC 2616: http://www.faqs.org/rfcs/rfc2616.html) - - This function only checks the URL's syntax. It does not check that the URL - points to a real document, for example, or that it otherwise makes sense - semantically. This function does automatically prepend 'http://' in front - of a URL in the case of an abbreviated URL (e.g. 'google.ca'). - - If the parameter mode='generic' is used, then this function's behavior - changes. It then rejects a URL string if any of the following is true: - - * The string is empty or None - * The string uses characters that are not allowed in a URL - * The URL scheme specified (if one is specified) is not valid - - (These rules are based on RFC 2396: http://www.faqs.org/rfcs/rfc2396.html) - - The list of allowed schemes is customizable with the allowed_schemes - parameter. If you exclude None from the list, then abbreviated URLs - (lacking a scheme such as 'http') will be rejected. - - The default prepended scheme is customizable with the prepend_scheme - parameter. If you set prepend_scheme to None then prepending will be - disabled. URLs that require prepending to parse will still be accepted, - but the return value will not be modified. - - IS_URL is compatible with the Internationalized Domain Name (IDN) standard - specified in RFC 3490 (http://tools.ietf.org/html/rfc3490). As a result, - URLs can be regular strings or unicode strings. - If the URL's domain component (e.g. google.ca) contains non-US-ASCII - letters, then the domain will be converted into Punycode (defined in - RFC 3492, http://tools.ietf.org/html/rfc3492). IS_URL goes a bit beyond - the standards, and allows non-US-ASCII characters to be present in the path - and query components of the URL as well. These non-US-ASCII characters will - be escaped using the standard '%20' type syntax. e.g. the unicode - character with hex code 0x4e86 will become '%4e%86' - - Args: - error_message: a string, the error message to give the end user - if the URL does not validate - allowed_schemes: a list containing strings or None. Each element - is a scheme the inputed URL is allowed to use - prepend_scheme: a string, this scheme is prepended if it's - necessary to make the URL valid - - Code Examples:: - - INPUT(_type='text', _name='name', requires=IS_URL()) - >>> IS_URL()('abc.com') - ('http://abc.com', None) - - INPUT(_type='text', _name='name', requires=IS_URL(mode='generic')) - >>> IS_URL(mode='generic')('abc.com') - ('abc.com', None) - - INPUT(_type='text', _name='name', - requires=IS_URL(allowed_schemes=['https'], prepend_scheme='https')) - >>> IS_URL(allowed_schemes=['https'], prepend_scheme='https')('https://abc.com') - ('https://abc.com', None) - - INPUT(_type='text', _name='name', - requires=IS_URL(prepend_scheme='https')) - >>> IS_URL(prepend_scheme='https')('abc.com') - ('https://abc.com', None) - - INPUT(_type='text', _name='name', - requires=IS_URL(mode='generic', allowed_schemes=['ftps', 'https'], - prepend_scheme='https')) - >>> IS_URL(mode='generic', allowed_schemes=['ftps', 'https'], prepend_scheme='https')('https://abc.com') - ('https://abc.com', None) - >>> IS_URL(mode='generic', allowed_schemes=['ftps', 'https', None], prepend_scheme='https')('abc.com') - ('abc.com', None) - - @author: Jonathan Benn - """ - - def __init__( - self, - error_message='Enter a valid URL', - mode='http', - allowed_schemes=None, - prepend_scheme='http', - allowed_tlds=None - ): - - self.error_message = error_message - self.mode = mode.lower() - if self.mode not in ['generic', 'http']: - raise SyntaxError("invalid mode '%s' in IS_URL" % self.mode) - self.allowed_schemes = allowed_schemes - if allowed_tlds is None: - self.allowed_tlds = official_top_level_domains - else: - self.allowed_tlds = allowed_tlds - - if self.allowed_schemes: - if prepend_scheme not in self.allowed_schemes: - raise SyntaxError("prepend_scheme='%s' is not in allowed_schemes=%s" - % (prepend_scheme, self.allowed_schemes)) - - # if allowed_schemes is None, then we will defer testing - # prepend_scheme's validity to a sub-method - - self.prepend_scheme = prepend_scheme - - def __call__(self, value): - """ - Args: - value: a unicode or regular string, the URL to validate - - Returns: - a (string, string) tuple, where tuple[0] is the modified - input value and tuple[1] is either None (success!) or the - string error_message. The input value will never be modified in the - case of an error. However, if there is success then the input URL - may be modified to (1) prepend a scheme, and/or (2) convert a - non-compliant unicode URL into a compliant US-ASCII version. - """ - if self.mode == 'generic': - subMethod = IS_GENERIC_URL(error_message=self.error_message, - allowed_schemes=self.allowed_schemes, - prepend_scheme=self.prepend_scheme) - elif self.mode == 'http': - subMethod = IS_HTTP_URL(error_message=self.error_message, - allowed_schemes=self.allowed_schemes, - prepend_scheme=self.prepend_scheme, - allowed_tlds=self.allowed_tlds) - else: - raise SyntaxError("invalid mode '%s' in IS_URL" % self.mode) - - if not isinstance(value, unicodeT): - return subMethod(value) - else: - try: - asciiValue = unicode_to_ascii_url(value, self.prepend_scheme) - except Exception as e: - # If we are not able to convert the unicode url into a - # US-ASCII URL, then the URL is not valid - return (value, translate(self.error_message)) - methodResult = subMethod(asciiValue) - # if the validation of the US-ASCII version of the value failed - if not methodResult[1] is None: - # then return the original input value, not the US-ASCII version - return (value, methodResult[1]) - else: - return methodResult - - -regex_time = re.compile( - '((?P[0-9]+))([^0-9 ]+(?P[0-9 ]+))?([^0-9ap ]+(?P[0-9]*))?((?P[ap]m))?') - - -class IS_TIME(Validator): - """ - Example: - Use as:: - - INPUT(_type='text', _name='name', requires=IS_TIME()) - - understands the following formats - hh:mm:ss [am/pm] - hh:mm [am/pm] - hh [am/pm] - - [am/pm] is optional, ':' can be replaced by any other non-space non-digit:: - - >>> IS_TIME()('21:30') - (datetime.time(21, 30), None) - >>> IS_TIME()('21-30') - (datetime.time(21, 30), None) - >>> IS_TIME()('21.30') - (datetime.time(21, 30), None) - >>> IS_TIME()('21:30:59') - (datetime.time(21, 30, 59), None) - >>> IS_TIME()('5:30') - (datetime.time(5, 30), None) - >>> IS_TIME()('5:30 am') - (datetime.time(5, 30), None) - >>> IS_TIME()('5:30 pm') - (datetime.time(17, 30), None) - >>> IS_TIME()('5:30 whatever') - ('5:30 whatever', 'enter time as hh:mm:ss (seconds, am, pm optional)') - >>> IS_TIME()('5:30 20') - ('5:30 20', 'enter time as hh:mm:ss (seconds, am, pm optional)') - >>> IS_TIME()('24:30') - ('24:30', 'enter time as hh:mm:ss (seconds, am, pm optional)') - >>> IS_TIME()('21:60') - ('21:60', 'enter time as hh:mm:ss (seconds, am, pm optional)') - >>> IS_TIME()('21:30::') - ('21:30::', 'enter time as hh:mm:ss (seconds, am, pm optional)') - >>> IS_TIME()('') - ('', 'enter time as hh:mm:ss (seconds, am, pm optional)')ù - - """ - - def __init__(self, error_message='Enter time as hh:mm:ss (seconds, am, pm optional)'): - self.error_message = error_message - - def __call__(self, value): - try: - ivalue = value - value = regex_time.match(value.lower()) - (h, m, s) = (int(value.group('h')), 0, 0) - if not value.group('m') is None: - m = int(value.group('m')) - if not value.group('s') is None: - s = int(value.group('s')) - if value.group('d') == 'pm' and 0 < h < 12: - h += 12 - if value.group('d') == 'am' and h == 12: - h = 0 - if not (h in range(24) and m in range(60) and s - in range(60)): - raise ValueError('Hours or minutes or seconds are outside of allowed range') - value = datetime.time(h, m, s) - return (value, None) - except AttributeError: - pass - except ValueError: - pass - return (ivalue, translate(self.error_message)) - - -# A UTC class. -class UTC(datetime.tzinfo): - """UTC""" - ZERO = datetime.timedelta(0) - - def utcoffset(self, dt): - return UTC.ZERO - - def tzname(self, dt): - return "UTC" - - def dst(self, dt): - return UTC.ZERO -utc = UTC() - - -class IS_DATE(Validator): - """ - Examples: - Use as:: - - INPUT(_type='text', _name='name', requires=IS_DATE()) - - date has to be in the ISO8960 format YYYY-MM-DD - """ - - def __init__(self, format='%Y-%m-%d', - error_message='Enter date as %(format)s'): - self.format = translate(format) - self.error_message = str(error_message) - self.extremes = {} - - def __call__(self, value): - ovalue = value - if isinstance(value, datetime.date): - return (value, None) - try: - (y, m, d, hh, mm, ss, t0, t1, t2) = \ - time.strptime(value, str(self.format)) - value = datetime.date(y, m, d) - return (value, None) - except: - self.extremes.update(IS_DATETIME.nice(self.format)) - return (ovalue, translate(self.error_message) % self.extremes) - - def formatter(self, value): - if value is None: - return None - format = self.format - year = value.year - y = '%.4i' % year - format = format.replace('%y', y[-2:]) - format = format.replace('%Y', y) - if year < 1900: - year = 2000 - d = datetime.date(year, value.month, value.day) - return d.strftime(format) - - -class IS_DATETIME(Validator): - """ - Examples: - Use as:: - - INPUT(_type='text', _name='name', requires=IS_DATETIME()) - - datetime has to be in the ISO8960 format YYYY-MM-DD hh:mm:ss - timezome must be None or a pytz.timezone("America/Chicago") object - """ - - isodatetime = '%Y-%m-%d %H:%M:%S' - - @staticmethod - def nice(format): - code = (('%Y', '1963'), - ('%y', '63'), - ('%d', '28'), - ('%m', '08'), - ('%b', 'Aug'), - ('%B', 'August'), - ('%H', '14'), - ('%I', '02'), - ('%p', 'PM'), - ('%M', '30'), - ('%S', '59')) - for (a, b) in code: - format = format.replace(a, b) - return dict(format=format) - - def __init__(self, format='%Y-%m-%d %H:%M:%S', - error_message='Enter date and time as %(format)s', - timezone=None): - self.format = translate(format) - self.error_message = str(error_message) - self.extremes = {} - self.timezone = timezone - - def __call__(self, value): - ovalue = value - if isinstance(value, datetime.datetime): - return (value, None) - try: - (y, m, d, hh, mm, ss, t0, t1, t2) = \ - time.strptime(value, str(self.format)) - value = datetime.datetime(y, m, d, hh, mm, ss) - if self.timezone is not None: - # TODO: https://github.com/web2py/web2py/issues/1094 (temporary solution) - value = self.timezone.localize(value).astimezone(utc).replace(tzinfo=None) - return (value, None) - except: - self.extremes.update(IS_DATETIME.nice(self.format)) - return (ovalue, translate(self.error_message) % self.extremes) - - def formatter(self, value): - if value is None: - return None - format = self.format - year = value.year - y = '%.4i' % year - format = format.replace('%y', y[-2:]) - format = format.replace('%Y', y) - if year < 1900: - year = 2000 - d = datetime.datetime(year, value.month, value.day, - value.hour, value.minute, value.second) - if self.timezone is not None: - d = d.replace(tzinfo=utc).astimezone(self.timezone) - return d.strftime(format) - - -class IS_DATE_IN_RANGE(IS_DATE): - """ - Examples: - Use as:: - - >>> v = IS_DATE_IN_RANGE(minimum=datetime.date(2008,1,1), \ - maximum=datetime.date(2009,12,31), \ - format="%m/%d/%Y",error_message="Oops") - - >>> v('03/03/2008') - (datetime.date(2008, 3, 3), None) - - >>> v('03/03/2010') - ('03/03/2010', 'oops') - - >>> v(datetime.date(2008,3,3)) - (datetime.date(2008, 3, 3), None) - - >>> v(datetime.date(2010,3,3)) - (datetime.date(2010, 3, 3), 'oops') - - """ - - def __init__(self, - minimum=None, - maximum=None, - format='%Y-%m-%d', - error_message=None): - self.minimum = minimum - self.maximum = maximum - if error_message is None: - if minimum is None: - error_message = "Enter date on or before %(max)s" - elif maximum is None: - error_message = "Enter date on or after %(min)s" - else: - error_message = "Enter date in range %(min)s %(max)s" - IS_DATE.__init__(self, - format=format, - error_message=error_message) - self.extremes = dict(min=self.formatter(minimum), - max=self.formatter(maximum)) - - def __call__(self, value): - ovalue = value - (value, msg) = IS_DATE.__call__(self, value) - if msg is not None: - return (value, msg) - if self.minimum and self.minimum > value: - return (ovalue, translate(self.error_message) % self.extremes) - if self.maximum and value > self.maximum: - return (ovalue, translate(self.error_message) % self.extremes) - return (value, None) - - -class IS_DATETIME_IN_RANGE(IS_DATETIME): - """ - Examples: - Use as:: - >>> v = IS_DATETIME_IN_RANGE(\ - minimum=datetime.datetime(2008,1,1,12,20), \ - maximum=datetime.datetime(2009,12,31,12,20), \ - format="%m/%d/%Y %H:%M",error_message="Oops") - >>> v('03/03/2008 12:40') - (datetime.datetime(2008, 3, 3, 12, 40), None) - - >>> v('03/03/2010 10:34') - ('03/03/2010 10:34', 'oops') - - >>> v(datetime.datetime(2008,3,3,0,0)) - (datetime.datetime(2008, 3, 3, 0, 0), None) - - >>> v(datetime.datetime(2010,3,3,0,0)) - (datetime.datetime(2010, 3, 3, 0, 0), 'oops') - - """ - - def __init__(self, - minimum=None, - maximum=None, - format='%Y-%m-%d %H:%M:%S', - error_message=None, - timezone=None): - self.minimum = minimum - self.maximum = maximum - if error_message is None: - if minimum is None: - error_message = "Enter date and time on or before %(max)s" - elif maximum is None: - error_message = "Enter date and time on or after %(min)s" - else: - error_message = "Enter date and time in range %(min)s %(max)s" - IS_DATETIME.__init__(self, - format=format, - error_message=error_message, - timezone=timezone) - self.extremes = dict(min=self.formatter(minimum), - max=self.formatter(maximum)) - - def __call__(self, value): - ovalue = value - (value, msg) = IS_DATETIME.__call__(self, value) - if msg is not None: - return (value, msg) - if self.minimum and self.minimum > value: - return (ovalue, translate(self.error_message) % self.extremes) - if self.maximum and value > self.maximum: - return (ovalue, translate(self.error_message) % self.extremes) - return (value, None) - - -class IS_LIST_OF(Validator): - - def __init__(self, other=None, minimum=None, maximum=None, error_message=None): - self.other = other - self.minimum = minimum - self.maximum = maximum - self.error_message = error_message - - def __call__(self, value): - ivalue = value - if not isinstance(value, list): - ivalue = [ivalue] - ivalue = [i for i in ivalue if str(i).strip()] - if self.minimum is not None and len(ivalue) < self.minimum: - return (ivalue, translate(self.error_message or - 'Minimum length is %(min)s') % dict(min=self.minimum, max=self.maximum)) - if self.maximum is not None and len(ivalue) > self.maximum: - return (ivalue, translate(self.error_message or - 'Maximum length is %(max)s') % dict(min=self.minimum, max=self.maximum)) - new_value = [] - other = self.other - if self.other: - if not isinstance(other, (list, tuple)): - other = [other] - for item in ivalue: - v = item - for validator in other: - (v, e) = validator(v) - if e: - return (ivalue, e) - new_value.append(v) - ivalue = new_value - return (ivalue, None) - - -class IS_LOWER(Validator): - """ - Converts to lowercase:: - - >>> IS_LOWER()('ABC') - ('abc', None) - >>> IS_LOWER()('Ñ') - ('\\xc3\\xb1', None) - - """ - - def __call__(self, value): - cast_back = lambda x: x - if isinstance(value, str): - cast_back = to_native - elif isinstance(value, bytes): - cast_back = to_bytes - value = to_unicode(value).lower() - return (cast_back(value), None) - - -class IS_UPPER(Validator): - """ - Converts to uppercase:: - - >>> IS_UPPER()('abc') - ('ABC', None) - >>> IS_UPPER()('ñ') - ('\\xc3\\x91', None) - - """ - - def __call__(self, value): - cast_back = lambda x: x - if isinstance(value, str): - cast_back = to_native - elif isinstance(value, bytes): - cast_back = to_bytes - value = to_unicode(value).upper() - return (cast_back(value), None) - - -def urlify(s, maxlen=80, keep_underscores=False): - """ - Converts incoming string to a simplified ASCII subset. - if (keep_underscores): underscores are retained in the string - else: underscores are translated to hyphens (default) - """ - s = to_unicode(s) # to unicode - s = s.lower() # to lowercase - s = unicodedata.normalize('NFKD', s) # replace special characters - s = to_native(s, charset='ascii', errors='ignore') # encode as ASCII - s = re.sub('&\w+?;', '', s) # strip html entities - if keep_underscores: - s = re.sub('\s+', '-', s) # whitespace to hyphens - s = re.sub('[^\w\-]', '', s) - # strip all but alphanumeric/underscore/hyphen - else: - s = re.sub('[\s_]+', '-', s) # whitespace & underscores to hyphens - s = re.sub('[^a-z0-9\-]', '', s) # strip all but alphanumeric/hyphen - s = re.sub('[-_][-_]+', '-', s) # collapse strings of hyphens - s = s.strip('-') # remove leading and trailing hyphens - return s[:maxlen] # enforce maximum length - - -class IS_SLUG(Validator): - """ - converts arbitrary text string to a slug:: - - >>> IS_SLUG()('abc123') - ('abc123', None) - >>> IS_SLUG()('ABC123') - ('abc123', None) - >>> IS_SLUG()('abc-123') - ('abc-123', None) - >>> IS_SLUG()('abc--123') - ('abc-123', None) - >>> IS_SLUG()('abc 123') - ('abc-123', None) - >>> IS_SLUG()('abc\t_123') - ('abc-123', None) - >>> IS_SLUG()('-abc-') - ('abc', None) - >>> IS_SLUG()('--a--b--_ -c--') - ('a-b-c', None) - >>> IS_SLUG()('abc&123') - ('abc123', None) - >>> IS_SLUG()('abc&123&def') - ('abc123def', None) - >>> IS_SLUG()('ñ') - ('n', None) - >>> IS_SLUG(maxlen=4)('abc123') - ('abc1', None) - >>> IS_SLUG()('abc_123') - ('abc-123', None) - >>> IS_SLUG(keep_underscores=False)('abc_123') - ('abc-123', None) - >>> IS_SLUG(keep_underscores=True)('abc_123') - ('abc_123', None) - >>> IS_SLUG(check=False)('abc') - ('abc', None) - >>> IS_SLUG(check=True)('abc') - ('abc', None) - >>> IS_SLUG(check=False)('a bc') - ('a-bc', None) - >>> IS_SLUG(check=True)('a bc') - ('a bc', 'must be slug') - """ - - @staticmethod - def urlify(value, maxlen=80, keep_underscores=False): - return urlify(value, maxlen, keep_underscores) - - def __init__(self, maxlen=80, check=False, error_message='Must be slug', keep_underscores=False): - self.maxlen = maxlen - self.check = check - self.error_message = error_message - self.keep_underscores = keep_underscores - - def __call__(self, value): - if self.check and value != urlify(value, self.maxlen, self.keep_underscores): - return (value, translate(self.error_message)) - return (urlify(value, self.maxlen, self.keep_underscores), None) - - -class ANY_OF(Validator): - """ - Tests if any of the validators in a list returns successfully:: - - >>> ANY_OF([IS_EMAIL(),IS_ALPHANUMERIC()])('a@b.co') - ('a@b.co', None) - >>> ANY_OF([IS_EMAIL(),IS_ALPHANUMERIC()])('abco') - ('abco', None) - >>> ANY_OF([IS_EMAIL(),IS_ALPHANUMERIC()])('@ab.co') - ('@ab.co', 'enter only letters, numbers, and underscore') - >>> ANY_OF([IS_ALPHANUMERIC(),IS_EMAIL()])('@ab.co') - ('@ab.co', 'enter a valid email address') - - """ - - def __init__(self, subs, error_message=None): - self.subs = subs - self.error_message = error_message - - def __call__(self, value): - for validator in self.subs: - value, error = validator(value) - if error is None: - break - if error is not None and self.error_message is not None: - error = translate(self.error_message) - return value, error - - def formatter(self, value): - # Use the formatter of the first subvalidator - # that validates the value and has a formatter - for validator in self.subs: - if hasattr(validator, 'formatter') and validator(value)[1] is None: - return validator.formatter(value) - - -class IS_EMPTY_OR(Validator): - """ - Dummy class for testing IS_EMPTY_OR:: - - >>> IS_EMPTY_OR(IS_EMAIL())('abc@def.com') - ('abc@def.com', None) - >>> IS_EMPTY_OR(IS_EMAIL())(' ') - (None, None) - >>> IS_EMPTY_OR(IS_EMAIL(), null='abc')(' ') - ('abc', None) - >>> IS_EMPTY_OR(IS_EMAIL(), null='abc', empty_regex='def')('def') - ('abc', None) - >>> IS_EMPTY_OR(IS_EMAIL())('abc') - ('abc', 'enter a valid email address') - >>> IS_EMPTY_OR(IS_EMAIL())(' abc ') - ('abc', 'enter a valid email address') - """ - - def __init__(self, other, null=None, empty_regex=None): - (self.other, self.null) = (other, null) - if empty_regex is not None: - self.empty_regex = re.compile(empty_regex) - else: - self.empty_regex = None - if hasattr(other, 'multiple'): - self.multiple = other.multiple - if hasattr(other, 'options'): - self.options = self._options - - def _options(self, *args, **kwargs): - options = self.other.options(*args, **kwargs) - if (not options or options[0][0] != '') and not self.multiple: - options.insert(0, ('', '')) - return options - - def set_self_id(self, id): - if isinstance(self.other, (list, tuple)): - for item in self.other: - if hasattr(item, 'set_self_id'): - item.set_self_id(id) - else: - if hasattr(self.other, 'set_self_id'): - self.other.set_self_id(id) - - def __call__(self, value): - value, empty = is_empty(value, empty_regex=self.empty_regex) - if empty: - return (self.null, None) - if isinstance(self.other, (list, tuple)): - error = None - for item in self.other: - value, error = item(value) - if error: - break - return value, error - else: - return self.other(value) - - def formatter(self, value): - if hasattr(self.other, 'formatter'): - return self.other.formatter(value) - return value - -IS_NULL_OR = IS_EMPTY_OR # for backward compatibility - - -class CLEANUP(Validator): - """ - Examples: - Use as:: - - INPUT(_type='text', _name='name', requires=CLEANUP()) - - removes special characters on validation - """ - REGEX_CLEANUP = re.compile('[^\x09\x0a\x0d\x20-\x7e]') - - def __init__(self, regex=None): - self.regex = self.REGEX_CLEANUP if regex is None \ - else re.compile(regex) - - def __call__(self, value): - v = self.regex.sub('', str(value).strip()) - return (v, None) - - -class LazyCrypt(object): - """ - Stores a lazy password hash - """ - - def __init__(self, crypt, password): - """ - crypt is an instance of the CRYPT validator, - password is the password as inserted by the user - """ - self.crypt = crypt - self.password = password - self.crypted = None - - def __str__(self): - """ - Encrypted self.password and caches it in self.crypted. - If self.crypt.salt the output is in the format $$ - - Try get the digest_alg from the key (if it exists) - else assume the default digest_alg. If not key at all, set key='' - - If a salt is specified use it, if salt is True, set salt to uuid - (this should all be backward compatible) - - Options: - key = 'uuid' - key = 'md5:uuid' - key = 'sha512:uuid' - ... - key = 'pbkdf2(1000,64,sha512):uuid' 1000 iterations and 64 chars length - """ - if self.crypted: - return self.crypted - if self.crypt.key: - if ':' in self.crypt.key: - digest_alg, key = self.crypt.key.split(':', 1) - else: - digest_alg, key = self.crypt.digest_alg, self.crypt.key - else: - digest_alg, key = self.crypt.digest_alg, '' - if self.crypt.salt: - if self.crypt.salt is True: - salt = str(web2py_uuid()).replace('-', '')[-16:] - else: - salt = self.crypt.salt - else: - salt = '' - hashed = simple_hash(self.password, key, salt, digest_alg) - self.crypted = '%s$%s$%s' % (digest_alg, salt, hashed) - return self.crypted - - def __eq__(self, stored_password): - """ - compares the current lazy crypted password with a stored password - """ - - # LazyCrypt objects comparison - if isinstance(stored_password, self.__class__): - return ((self is stored_password) or - ((self.crypt.key == stored_password.crypt.key) and - (self.password == stored_password.password))) - - if self.crypt.key: - if ':' in self.crypt.key: - key = self.crypt.key.split(':')[1] - else: - key = self.crypt.key - else: - key = '' - if stored_password is None: - return False - elif stored_password.count('$') == 2: - (digest_alg, salt, hash) = stored_password.split('$') - h = simple_hash(self.password, key, salt, digest_alg) - temp_pass = '%s$%s$%s' % (digest_alg, salt, h) - else: # no salting - # guess digest_alg - digest_alg = DIGEST_ALG_BY_SIZE.get(len(stored_password), None) - if not digest_alg: - return False - else: - temp_pass = simple_hash(self.password, key, '', digest_alg) - return temp_pass == stored_password - - def __ne__(self, other): - return not self.__eq__(other) - - -class CRYPT(object): - """ - Examples: - Use as:: - - INPUT(_type='text', _name='name', requires=CRYPT()) - - encodes the value on validation with a digest. - - If no arguments are provided CRYPT uses the MD5 algorithm. - If the key argument is provided the HMAC+MD5 algorithm is used. - If the digest_alg is specified this is used to replace the - MD5 with, for example, SHA512. The digest_alg can be - the name of a hashlib algorithm as a string or the algorithm itself. - - min_length is the minimal password length (default 4) - IS_STRONG for serious security - error_message is the message if password is too short - - Notice that an empty password is accepted but invalid. It will not allow login back. - Stores junk as hashed password. - - Specify an algorithm or by default we will use sha512. - - Typical available algorithms: - md5, sha1, sha224, sha256, sha384, sha512 - - If salt, it hashes a password with a salt. - If salt is True, this method will automatically generate one. - Either case it returns an encrypted password string in the following format: - - $$ - - Important: hashed password is returned as a LazyCrypt object and computed only if needed. - The LasyCrypt object also knows how to compare itself with an existing salted password - - Supports standard algorithms - - >>> for alg in ('md5','sha1','sha256','sha384','sha512'): - ... print(str(CRYPT(digest_alg=alg,salt=True)('test')[0])) - md5$...$... - sha1$...$... - sha256$...$... - sha384$...$... - sha512$...$... - - The syntax is always alg$salt$hash - - Supports for pbkdf2 - - >>> alg = 'pbkdf2(1000,20,sha512)' - >>> print(str(CRYPT(digest_alg=alg,salt=True)('test')[0])) - pbkdf2(1000,20,sha512)$...$... - - An optional hmac_key can be specified and it is used as salt prefix - - >>> a = str(CRYPT(digest_alg='md5',key='mykey',salt=True)('test')[0]) - >>> print(a) - md5$...$... - - Even if the algorithm changes the hash can still be validated - - >>> CRYPT(digest_alg='sha1',key='mykey',salt=True)('test')[0] == a - True - - If no salt is specified CRYPT can guess the algorithms from length: - - >>> a = str(CRYPT(digest_alg='sha1',salt=False)('test')[0]) - >>> a - 'sha1$$a94a8fe5ccb19ba61c4c0873d391e987982fbbd3' - >>> CRYPT(digest_alg='sha1',salt=False)('test')[0] == a - True - >>> CRYPT(digest_alg='sha1',salt=False)('test')[0] == a[6:] - True - >>> CRYPT(digest_alg='md5',salt=False)('test')[0] == a - True - >>> CRYPT(digest_alg='md5',salt=False)('test')[0] == a[6:] - True - """ - - def __init__(self, - key=None, - digest_alg='pbkdf2(1000,20,sha512)', - min_length=0, - error_message='Too short', salt=True, - max_length=1024): - """ - important, digest_alg='md5' is not the default hashing algorithm for - web2py. This is only an example of usage of this function. - - The actual hash algorithm is determined from the key which is - generated by web2py in tools.py. This defaults to hmac+sha512. - """ - self.key = key - self.digest_alg = digest_alg - self.min_length = min_length - self.max_length = max_length - self.error_message = error_message - self.salt = salt - - def __call__(self, value): - v = value and str(value)[:self.max_length] - if not v or len(v) < self.min_length: - return ('', translate(self.error_message)) - if isinstance(value, LazyCrypt): - return (value, None) - return (LazyCrypt(self, value), None) - -# entropy calculator for IS_STRONG -# -lowerset = frozenset(u'abcdefghijklmnopqrstuvwxyz') -upperset = frozenset(u'ABCDEFGHIJKLMNOPQRSTUVWXYZ') -numberset = frozenset(u'0123456789') -sym1set = frozenset(u'!@#$%^&*()') -sym2set = frozenset(u'~`-_=+[]{}\\|;:\'",.<>?/') -otherset = frozenset( - u'0123456789abcdefghijklmnopqrstuvwxyz') # anything else - - -def calc_entropy(string): - """ calculates a simple entropy for a given string """ - import math - alphabet = 0 # alphabet size - other = set() - seen = set() - lastset = None - string = to_unicode(string) - for c in string: - # classify this character - inset = otherset - for cset in (lowerset, upperset, numberset, sym1set, sym2set): - if c in cset: - inset = cset - break - # calculate effect of character on alphabet size - if inset not in seen: - seen.add(inset) - alphabet += len(inset) # credit for a new character set - elif c not in other: - alphabet += 1 # credit for unique characters - other.add(c) - if inset is not lastset: - alphabet += 1 # credit for set transitions - lastset = cset - entropy = len( - string) * math.log(alphabet) / 0.6931471805599453 # math.log(2) - return round(entropy, 2) - - -class IS_STRONG(object): - """ - Examples: - Use as:: - - INPUT(_type='password', _name='passwd', - requires=IS_STRONG(min=10, special=2, upper=2)) - - enforces complexity requirements on a field - - >>> IS_STRONG(es=True)('Abcd1234') - ('Abcd1234', - 'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|') - >>> IS_STRONG(es=True)('Abcd1234!') - ('Abcd1234!', None) - >>> IS_STRONG(es=True, entropy=1)('a') - ('a', None) - >>> IS_STRONG(es=True, entropy=1, min=2)('a') - ('a', 'Minimum length is 2') - >>> IS_STRONG(es=True, entropy=100)('abc123') - ('abc123', 'Entropy (32.35) less than required (100)') - >>> IS_STRONG(es=True, entropy=100)('and') - ('and', 'Entropy (14.57) less than required (100)') - >>> IS_STRONG(es=True, entropy=100)('aaa') - ('aaa', 'Entropy (14.42) less than required (100)') - >>> IS_STRONG(es=True, entropy=100)('a1d') - ('a1d', 'Entropy (15.97) less than required (100)') - >>> IS_STRONG(es=True, entropy=100)('añd') - ('a\\xc3\\xb1d', 'Entropy (18.13) less than required (100)') - - """ - - def __init__(self, min=None, max=None, upper=None, lower=None, number=None, - entropy=None, - special=None, specials=r'~!@#$%^&*()_+-=?<>,.:;{}[]|', - invalid=' "', error_message=None, es=False): - self.entropy = entropy - if entropy is None: - # enforce default requirements - self.min = 8 if min is None else min - self.max = max # was 20, but that doesn't make sense - self.upper = 1 if upper is None else upper - self.lower = 1 if lower is None else lower - self.number = 1 if number is None else number - self.special = 1 if special is None else special - else: - # by default, an entropy spec is exclusive - self.min = min - self.max = max - self.upper = upper - self.lower = lower - self.number = number - self.special = special - self.specials = specials - self.invalid = invalid - self.error_message = error_message - self.estring = es # return error message as string (for doctest) - - def __call__(self, value): - failures = [] - if value and len(value) == value.count('*') > 4: - return (value, None) - if self.entropy is not None: - entropy = calc_entropy(value) - if entropy < self.entropy: - failures.append(translate("Entropy (%(have)s) less than required (%(need)s)") - % dict(have=entropy, need=self.entropy)) - if isinstance(self.min, int) and self.min > 0: - if not len(value) >= self.min: - failures.append(translate("Minimum length is %s") % self.min) - if isinstance(self.max, int) and self.max > 0: - if not len(value) <= self.max: - failures.append(translate("Maximum length is %s") % self.max) - if isinstance(self.special, int): - all_special = [ch in value for ch in self.specials] - if self.special > 0: - if not all_special.count(True) >= self.special: - failures.append(translate("Must include at least %s of the following: %s") - % (self.special, self.specials)) - if self.invalid: - all_invalid = [ch in value for ch in self.invalid] - if all_invalid.count(True) > 0: - failures.append(translate("May not contain any of the following: %s") - % self.invalid) - if isinstance(self.upper, int): - all_upper = re.findall("[A-Z]", value) - if self.upper > 0: - if not len(all_upper) >= self.upper: - failures.append(translate("Must include at least %s uppercase") - % str(self.upper)) - else: - if len(all_upper) > 0: - failures.append( - translate("May not include any uppercase letters")) - if isinstance(self.lower, int): - all_lower = re.findall("[a-z]", value) - if self.lower > 0: - if not len(all_lower) >= self.lower: - failures.append(translate("Must include at least %s lowercase") - % str(self.lower)) - else: - if len(all_lower) > 0: - failures.append( - translate("May not include any lowercase letters")) - if isinstance(self.number, int): - all_number = re.findall("[0-9]", value) - if self.number > 0: - numbers = "number" - if self.number > 1: - numbers = "numbers" - if not len(all_number) >= self.number: - failures.append(translate("Must include at least %s %s") - % (str(self.number), numbers)) - else: - if len(all_number) > 0: - failures.append(translate("May not include any numbers")) - if len(failures) == 0: - return (value, None) - if not self.error_message: - if self.estring: - return (value, '|'.join(failures)) - from gluon.html import XML - return (value, XML('
'.join(failures))) - else: - return (value, translate(self.error_message)) - - -class IS_IMAGE(Validator): - """ - Checks if file uploaded through file input was saved in one of selected - image formats and has dimensions (width and height) within given boundaries. - - Does *not* check for maximum file size (use IS_LENGTH for that). Returns - validation failure if no data was uploaded. - - Supported file formats: BMP, GIF, JPEG, PNG. - - Code parts taken from - http://mail.python.org/pipermail/python-list/2007-June/617126.html - - Args: - extensions: iterable containing allowed *lowercase* image file extensions - ('jpg' extension of uploaded file counts as 'jpeg') - maxsize: iterable containing maximum width and height of the image - minsize: iterable containing minimum width and height of the image - - Use (-1, -1) as minsize to pass image size check. - - Examples: - Check if uploaded file is in any of supported image formats: - - INPUT(_type='file', _name='name', requires=IS_IMAGE()) - - Check if uploaded file is either JPEG or PNG: - - INPUT(_type='file', _name='name', - requires=IS_IMAGE(extensions=('jpeg', 'png'))) - - Check if uploaded file is PNG with maximum size of 200x200 pixels: - - INPUT(_type='file', _name='name', - requires=IS_IMAGE(extensions=('png'), maxsize=(200, 200))) - """ - - def __init__(self, - extensions=('bmp', 'gif', 'jpeg', 'png'), - maxsize=(10000, 10000), - minsize=(0, 0), - error_message='Invalid image'): - - self.extensions = extensions - self.maxsize = maxsize - self.minsize = minsize - self.error_message = error_message - - def __call__(self, value): - try: - extension = value.filename.rfind('.') - assert extension >= 0 - extension = value.filename[extension + 1:].lower() - if extension == 'jpg': - extension = 'jpeg' - assert extension in self.extensions - if extension == 'bmp': - width, height = self.__bmp(value.file) - elif extension == 'gif': - width, height = self.__gif(value.file) - elif extension == 'jpeg': - width, height = self.__jpeg(value.file) - elif extension == 'png': - width, height = self.__png(value.file) - else: - width = -1 - height = -1 - assert self.minsize[0] <= width <= self.maxsize[0] \ - and self.minsize[1] <= height <= self.maxsize[1] - value.file.seek(0) - return (value, None) - except Exception as e: - return (value, translate(self.error_message)) - - def __bmp(self, stream): - if stream.read(2) == b'BM': - stream.read(16) - return struct.unpack("= 0xC0 and code <= 0xC3: - return tuple(reversed( - struct.unpack("!xHH", stream.read(5)))) - else: - stream.read(length - 2) - return (-1, -1) - - def __png(self, stream): - if stream.read(8) == b'\211PNG\r\n\032\n': - stream.read(4) - if stream.read(4) == b"IHDR": - return struct.unpack("!LL", stream.read(8)) - return (-1, -1) - - -class IS_UPLOAD_FILENAME(Validator): - """ - Checks if name and extension of file uploaded through file input matches - given criteria. - - Does *not* ensure the file type in any way. Returns validation failure - if no data was uploaded. - - Args: - filename: filename (before dot) regex - extension: extension (after dot) regex - lastdot: which dot should be used as a filename / extension separator: - True means last dot, eg. file.png -> file / png - False means first dot, eg. file.tar.gz -> file / tar.gz - case: 0 - keep the case, 1 - transform the string into lowercase (default), - 2 - transform the string into uppercase - - If there is no dot present, extension checks will be done against empty - string and filename checks against whole value. - - Examples: - Check if file has a pdf extension (case insensitive): - - INPUT(_type='file', _name='name', - requires=IS_UPLOAD_FILENAME(extension='pdf')) - - Check if file has a tar.gz extension and name starting with backup: - - INPUT(_type='file', _name='name', - requires=IS_UPLOAD_FILENAME(filename='backup.*', - extension='tar.gz', lastdot=False)) - - Check if file has no extension and name matching README - (case sensitive): - - INPUT(_type='file', _name='name', - requires=IS_UPLOAD_FILENAME(filename='^README$', - extension='^$', case=0) - - """ - - def __init__(self, filename=None, extension=None, lastdot=True, case=1, - error_message='Enter valid filename'): - if isinstance(filename, str): - filename = re.compile(filename) - if isinstance(extension, str): - extension = re.compile(extension) - self.filename = filename - self.extension = extension - self.lastdot = lastdot - self.case = case - self.error_message = error_message - - def __call__(self, value): - try: - string = value.filename - except: - return (value, translate(self.error_message)) - if self.case == 1: - string = string.lower() - elif self.case == 2: - string = string.upper() - if self.lastdot: - dot = string.rfind('.') - else: - dot = string.find('.') - if dot == -1: - dot = len(string) - if self.filename and not self.filename.match(string[:dot]): - return (value, translate(self.error_message)) - elif self.extension and not self.extension.match(string[dot + 1:]): - return (value, translate(self.error_message)) - else: - return (value, None) - - -class IS_IPV4(Validator): - """ - Checks if field's value is an IP version 4 address in decimal form. Can - be set to force addresses from certain range. - - IPv4 regex taken from: http://regexlib.com/REDetails.aspx?regexp_id=1411 - - Args: - - minip: lowest allowed address; accepts: - - - str, eg. 192.168.0.1 - - list or tuple of octets, eg. [192, 168, 0, 1] - maxip: highest allowed address; same as above - invert: True to allow addresses only from outside of given range; note - that range boundaries are not matched this way - is_localhost: localhost address treatment: - - - None (default): indifferent - - True (enforce): query address must match localhost address (127.0.0.1) - - False (forbid): query address must not match localhost address - is_private: same as above, except that query address is checked against - two address ranges: 172.16.0.0 - 172.31.255.255 and - 192.168.0.0 - 192.168.255.255 - is_automatic: same as above, except that query address is checked against - one address range: 169.254.0.0 - 169.254.255.255 - - Minip and maxip may also be lists or tuples of addresses in all above - forms (str, int, list / tuple), allowing setup of multiple address ranges:: - - minip = (minip1, minip2, ... minipN) - | | | - | | | - maxip = (maxip1, maxip2, ... maxipN) - - Longer iterable will be truncated to match length of shorter one. - - Examples: - Check for valid IPv4 address: - - INPUT(_type='text', _name='name', requires=IS_IPV4()) - - Check for valid IPv4 address belonging to specific range: - - INPUT(_type='text', _name='name', - requires=IS_IPV4(minip='100.200.0.0', maxip='100.200.255.255')) - - Check for valid IPv4 address belonging to either 100.110.0.0 - - 100.110.255.255 or 200.50.0.0 - 200.50.0.255 address range: - - INPUT(_type='text', _name='name', - requires=IS_IPV4(minip=('100.110.0.0', '200.50.0.0'), - maxip=('100.110.255.255', '200.50.0.255'))) - - Check for valid IPv4 address belonging to private address space: - - INPUT(_type='text', _name='name', requires=IS_IPV4(is_private=True)) - - Check for valid IPv4 address that is not a localhost address: - - INPUT(_type='text', _name='name', requires=IS_IPV4(is_localhost=False)) - - >>> IS_IPV4()('1.2.3.4') - ('1.2.3.4', None) - >>> IS_IPV4()('255.255.255.255') - ('255.255.255.255', None) - >>> IS_IPV4()('1.2.3.4 ') - ('1.2.3.4 ', 'enter valid IPv4 address') - >>> IS_IPV4()('1.2.3.4.5') - ('1.2.3.4.5', 'enter valid IPv4 address') - >>> IS_IPV4()('123.123') - ('123.123', 'enter valid IPv4 address') - >>> IS_IPV4()('1111.2.3.4') - ('1111.2.3.4', 'enter valid IPv4 address') - >>> IS_IPV4()('0111.2.3.4') - ('0111.2.3.4', 'enter valid IPv4 address') - >>> IS_IPV4()('256.2.3.4') - ('256.2.3.4', 'enter valid IPv4 address') - >>> IS_IPV4()('300.2.3.4') - ('300.2.3.4', 'enter valid IPv4 address') - >>> IS_IPV4(minip='1.2.3.4', maxip='1.2.3.4')('1.2.3.4') - ('1.2.3.4', None) - >>> IS_IPV4(minip='1.2.3.5', maxip='1.2.3.9', error_message='Bad ip')('1.2.3.4') - ('1.2.3.4', 'bad ip') - >>> IS_IPV4(maxip='1.2.3.4', invert=True)('127.0.0.1') - ('127.0.0.1', None) - >>> IS_IPV4(maxip='1.2.3.4', invert=True)('1.2.3.4') - ('1.2.3.4', 'enter valid IPv4 address') - >>> IS_IPV4(is_localhost=True)('127.0.0.1') - ('127.0.0.1', None) - >>> IS_IPV4(is_localhost=True)('1.2.3.4') - ('1.2.3.4', 'enter valid IPv4 address') - >>> IS_IPV4(is_localhost=False)('127.0.0.1') - ('127.0.0.1', 'enter valid IPv4 address') - >>> IS_IPV4(maxip='100.0.0.0', is_localhost=True)('127.0.0.1') - ('127.0.0.1', 'enter valid IPv4 address') - - """ - - regex = re.compile( - '^(([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.){3}([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])$') - numbers = (16777216, 65536, 256, 1) - localhost = 2130706433 - private = ((2886729728, 2886795263), (3232235520, 3232301055)) - automatic = (2851995648, 2852061183) - - def __init__( - self, - minip='0.0.0.0', - maxip='255.255.255.255', - invert=False, - is_localhost=None, - is_private=None, - is_automatic=None, - error_message='Enter valid IPv4 address'): - for n, value in enumerate((minip, maxip)): - temp = [] - if isinstance(value, str): - temp.append(value.split('.')) - elif isinstance(value, (list, tuple)): - if len(value) == len(list(filter(lambda item: isinstance(item, int), value))) == 4: - temp.append(value) - else: - for item in value: - if isinstance(item, str): - temp.append(item.split('.')) - elif isinstance(item, (list, tuple)): - temp.append(item) - numbers = [] - for item in temp: - number = 0 - for i, j in zip(self.numbers, item): - number += i * int(j) - numbers.append(number) - if n == 0: - self.minip = numbers - else: - self.maxip = numbers - self.invert = invert - self.is_localhost = is_localhost - self.is_private = is_private - self.is_automatic = is_automatic - self.error_message = error_message - - def __call__(self, value): - if self.regex.match(value): - number = 0 - for i, j in zip(self.numbers, value.split('.')): - number += i * int(j) - ok = False - for bottom, top in zip(self.minip, self.maxip): - if self.invert != (bottom <= number <= top): - ok = True - if ok and self.is_localhost is not None and \ - self.is_localhost != (number == self.localhost): - ok = False - if ok and self.is_private is not None and (self.is_private != - any([private_number[0] <= number <= private_number[1] - for private_number in self.private])): - ok = False - if ok and self.is_automatic is not None and (self.is_automatic != - (self.automatic[0] <= number <= self.automatic[1])): - ok = False - if ok: - return (value, None) - return (value, translate(self.error_message)) - - -class IS_IPV6(Validator): - """ - Checks if field's value is an IP version 6 address. - - Uses the ipaddress from the Python 3 standard library - and its Python 2 backport (in contrib/ipaddress.py). - - Args: - is_private: None (default): indifferent - True (enforce): address must be in fc00::/7 range - False (forbid): address must NOT be in fc00::/7 range - is_link_local: Same as above but uses fe80::/10 range - is_reserved: Same as above but uses IETF reserved range - is_multicast: Same as above but uses ff00::/8 range - is_routeable: Similar to above but enforces not private, link_local, - reserved or multicast - is_6to4: Same as above but uses 2002::/16 range - is_teredo: Same as above but uses 2001::/32 range - subnets: value must be a member of at least one from list of subnets - - Examples: - Check for valid IPv6 address: - - INPUT(_type='text', _name='name', requires=IS_IPV6()) - - Check for valid IPv6 address is a link_local address: - - INPUT(_type='text', _name='name', requires=IS_IPV6(is_link_local=True)) - - Check for valid IPv6 address that is Internet routeable: - - INPUT(_type='text', _name='name', requires=IS_IPV6(is_routeable=True)) - - Check for valid IPv6 address in specified subnet: - - INPUT(_type='text', _name='name', requires=IS_IPV6(subnets=['2001::/32']) - - >>> IS_IPV6()('fe80::126c:8ffa:fe22:b3af') - ('fe80::126c:8ffa:fe22:b3af', None) - >>> IS_IPV6()('192.168.1.1') - ('192.168.1.1', 'enter valid IPv6 address') - >>> IS_IPV6(error_message='Bad ip')('192.168.1.1') - ('192.168.1.1', 'bad ip') - >>> IS_IPV6(is_link_local=True)('fe80::126c:8ffa:fe22:b3af') - ('fe80::126c:8ffa:fe22:b3af', None) - >>> IS_IPV6(is_link_local=False)('fe80::126c:8ffa:fe22:b3af') - ('fe80::126c:8ffa:fe22:b3af', 'enter valid IPv6 address') - >>> IS_IPV6(is_link_local=True)('2001::126c:8ffa:fe22:b3af') - ('2001::126c:8ffa:fe22:b3af', 'enter valid IPv6 address') - >>> IS_IPV6(is_multicast=True)('2001::126c:8ffa:fe22:b3af') - ('2001::126c:8ffa:fe22:b3af', 'enter valid IPv6 address') - >>> IS_IPV6(is_multicast=True)('ff00::126c:8ffa:fe22:b3af') - ('ff00::126c:8ffa:fe22:b3af', None) - >>> IS_IPV6(is_routeable=True)('2001::126c:8ffa:fe22:b3af') - ('2001::126c:8ffa:fe22:b3af', None) - >>> IS_IPV6(is_routeable=True)('ff00::126c:8ffa:fe22:b3af') - ('ff00::126c:8ffa:fe22:b3af', 'enter valid IPv6 address') - >>> IS_IPV6(subnets='2001::/32')('2001::8ffa:fe22:b3af') - ('2001::8ffa:fe22:b3af', None) - >>> IS_IPV6(subnets='fb00::/8')('2001::8ffa:fe22:b3af') - ('2001::8ffa:fe22:b3af', 'enter valid IPv6 address') - >>> IS_IPV6(subnets=['fc00::/8','2001::/32'])('2001::8ffa:fe22:b3af') - ('2001::8ffa:fe22:b3af', None) - >>> IS_IPV6(subnets='invalidsubnet')('2001::8ffa:fe22:b3af') - ('2001::8ffa:fe22:b3af', 'invalid subnet provided') - - """ - - def __init__( - self, - is_private=None, - is_link_local=None, - is_reserved=None, - is_multicast=None, - is_routeable=None, - is_6to4=None, - is_teredo=None, - subnets=None, - error_message='Enter valid IPv6 address'): - self.is_private = is_private - self.is_link_local = is_link_local - self.is_reserved = is_reserved - self.is_multicast = is_multicast - self.is_routeable = is_routeable - self.is_6to4 = is_6to4 - self.is_teredo = is_teredo - self.subnets = subnets - self.error_message = error_message - - def __call__(self, value): - from gluon._compat import ipaddress - - try: - ip = ipaddress.IPv6Address(to_unicode(value)) - ok = True - except ipaddress.AddressValueError: - return (value, translate(self.error_message)) - - if self.subnets: - # iterate through self.subnets to see if value is a member - ok = False - if isinstance(self.subnets, str): - self.subnets = [self.subnets] - for network in self.subnets: - try: - ipnet = ipaddress.IPv6Network(to_unicode(network)) - except (ipaddress.NetmaskValueError, ipaddress.AddressValueError): - return (value, translate('invalid subnet provided')) - if ip in ipnet: - ok = True - - if self.is_routeable: - self.is_private = False - self.is_reserved = False - self.is_multicast = False - - if ok and self.is_private is not None and \ - self.is_private != ip.is_private: - ok = False - if ok and self.is_link_local is not None and \ - self.is_link_local != ip.is_link_local: - ok = False - if ok and self.is_reserved is not None and \ - self.is_reserved != ip.is_reserved: - ok = False - if ok and self.is_multicast is not None and \ - self.is_multicast != ip.is_multicast: - ok = False - if ok and self.is_6to4 is not None and \ - self.is_6to4 != bool(ip.sixtofour): - ok = False - if ok and self.is_teredo is not None and \ - self.is_teredo != bool(ip.teredo): - ok = False - - if ok: - return (value, None) - - return (value, translate(self.error_message)) - - -class IS_IPADDRESS(Validator): - """ - Checks if field's value is an IP Address (v4 or v6). Can be set to force - addresses from within a specific range. Checks are done with the correct - IS_IPV4 and IS_IPV6 validators. - - Uses the ipaddress from the Python 3 standard library - and its Python 2 backport (in contrib/ipaddress.py). - - Args: - minip: lowest allowed address; accepts: - str, eg. 192.168.0.1 - list or tuple of octets, eg. [192, 168, 0, 1] - maxip: highest allowed address; same as above - invert: True to allow addresses only from outside of given range; note - that range boundaries are not matched this way - - IPv4 specific arguments: - - - is_localhost: localhost address treatment: - - - None (default): indifferent - - True (enforce): query address must match localhost address - (127.0.0.1) - - False (forbid): query address must not match localhost address - - is_private: same as above, except that query address is checked against - two address ranges: 172.16.0.0 - 172.31.255.255 and - 192.168.0.0 - 192.168.255.255 - - is_automatic: same as above, except that query address is checked against - one address range: 169.254.0.0 - 169.254.255.255 - - is_ipv4: either: - - - None (default): indifferent - - True (enforce): must be an IPv4 address - - False (forbid): must NOT be an IPv4 address - - IPv6 specific arguments: - - - is_link_local: Same as above but uses fe80::/10 range - - is_reserved: Same as above but uses IETF reserved range - - is_multicast: Same as above but uses ff00::/8 range - - is_routeable: Similar to above but enforces not private, link_local, - reserved or multicast - - is_6to4: Same as above but uses 2002::/16 range - - is_teredo: Same as above but uses 2001::/32 range - - subnets: value must be a member of at least one from list of subnets - - is_ipv6: either: - - - None (default): indifferent - - True (enforce): must be an IPv6 address - - False (forbid): must NOT be an IPv6 address - - Minip and maxip may also be lists or tuples of addresses in all above - forms (str, int, list / tuple), allowing setup of multiple address ranges:: - - minip = (minip1, minip2, ... minipN) - | | | - | | | - maxip = (maxip1, maxip2, ... maxipN) - - Longer iterable will be truncated to match length of shorter one. - - >>> IS_IPADDRESS()('192.168.1.5') - ('192.168.1.5', None) - >>> IS_IPADDRESS(is_ipv6=False)('192.168.1.5') - ('192.168.1.5', None) - >>> IS_IPADDRESS()('255.255.255.255') - ('255.255.255.255', None) - >>> IS_IPADDRESS()('192.168.1.5 ') - ('192.168.1.5 ', 'enter valid IP address') - >>> IS_IPADDRESS()('192.168.1.1.5') - ('192.168.1.1.5', 'enter valid IP address') - >>> IS_IPADDRESS()('123.123') - ('123.123', 'enter valid IP address') - >>> IS_IPADDRESS()('1111.2.3.4') - ('1111.2.3.4', 'enter valid IP address') - >>> IS_IPADDRESS()('0111.2.3.4') - ('0111.2.3.4', 'enter valid IP address') - >>> IS_IPADDRESS()('256.2.3.4') - ('256.2.3.4', 'enter valid IP address') - >>> IS_IPADDRESS()('300.2.3.4') - ('300.2.3.4', 'enter valid IP address') - >>> IS_IPADDRESS(minip='192.168.1.0', maxip='192.168.1.255')('192.168.1.100') - ('192.168.1.100', None) - >>> IS_IPADDRESS(minip='1.2.3.5', maxip='1.2.3.9', error_message='Bad ip')('1.2.3.4') - ('1.2.3.4', 'bad ip') - >>> IS_IPADDRESS(maxip='1.2.3.4', invert=True)('127.0.0.1') - ('127.0.0.1', None) - >>> IS_IPADDRESS(maxip='192.168.1.4', invert=True)('192.168.1.4') - ('192.168.1.4', 'enter valid IP address') - >>> IS_IPADDRESS(is_localhost=True)('127.0.0.1') - ('127.0.0.1', None) - >>> IS_IPADDRESS(is_localhost=True)('192.168.1.10') - ('192.168.1.10', 'enter valid IP address') - >>> IS_IPADDRESS(is_localhost=False)('127.0.0.1') - ('127.0.0.1', 'enter valid IP address') - >>> IS_IPADDRESS(maxip='100.0.0.0', is_localhost=True)('127.0.0.1') - ('127.0.0.1', 'enter valid IP address') - - >>> IS_IPADDRESS()('fe80::126c:8ffa:fe22:b3af') - ('fe80::126c:8ffa:fe22:b3af', None) - >>> IS_IPADDRESS(is_ipv4=False)('fe80::126c:8ffa:fe22:b3af') - ('fe80::126c:8ffa:fe22:b3af', None) - >>> IS_IPADDRESS()('fe80::126c:8ffa:fe22:b3af ') - ('fe80::126c:8ffa:fe22:b3af ', 'enter valid IP address') - >>> IS_IPADDRESS(is_ipv4=True)('fe80::126c:8ffa:fe22:b3af') - ('fe80::126c:8ffa:fe22:b3af', 'enter valid IP address') - >>> IS_IPADDRESS(is_ipv6=True)('192.168.1.1') - ('192.168.1.1', 'enter valid IP address') - >>> IS_IPADDRESS(is_ipv6=True, error_message='Bad ip')('192.168.1.1') - ('192.168.1.1', 'bad ip') - >>> IS_IPADDRESS(is_link_local=True)('fe80::126c:8ffa:fe22:b3af') - ('fe80::126c:8ffa:fe22:b3af', None) - >>> IS_IPADDRESS(is_link_local=False)('fe80::126c:8ffa:fe22:b3af') - ('fe80::126c:8ffa:fe22:b3af', 'enter valid IP address') - >>> IS_IPADDRESS(is_link_local=True)('2001::126c:8ffa:fe22:b3af') - ('2001::126c:8ffa:fe22:b3af', 'enter valid IP address') - >>> IS_IPADDRESS(is_multicast=True)('2001::126c:8ffa:fe22:b3af') - ('2001::126c:8ffa:fe22:b3af', 'enter valid IP address') - >>> IS_IPADDRESS(is_multicast=True)('ff00::126c:8ffa:fe22:b3af') - ('ff00::126c:8ffa:fe22:b3af', None) - >>> IS_IPADDRESS(is_routeable=True)('2001::126c:8ffa:fe22:b3af') - ('2001::126c:8ffa:fe22:b3af', None) - >>> IS_IPADDRESS(is_routeable=True)('ff00::126c:8ffa:fe22:b3af') - ('ff00::126c:8ffa:fe22:b3af', 'enter valid IP address') - >>> IS_IPADDRESS(subnets='2001::/32')('2001::8ffa:fe22:b3af') - ('2001::8ffa:fe22:b3af', None) - >>> IS_IPADDRESS(subnets='fb00::/8')('2001::8ffa:fe22:b3af') - ('2001::8ffa:fe22:b3af', 'enter valid IP address') - >>> IS_IPADDRESS(subnets=['fc00::/8','2001::/32'])('2001::8ffa:fe22:b3af') - ('2001::8ffa:fe22:b3af', None) - >>> IS_IPADDRESS(subnets='invalidsubnet')('2001::8ffa:fe22:b3af') - ('2001::8ffa:fe22:b3af', 'invalid subnet provided') - """ - - def __init__( - self, - minip='0.0.0.0', - maxip='255.255.255.255', - invert=False, - is_localhost=None, - is_private=None, - is_automatic=None, - is_ipv4=None, - is_link_local=None, - is_reserved=None, - is_multicast=None, - is_routeable=None, - is_6to4=None, - is_teredo=None, - subnets=None, - is_ipv6=None, - error_message='Enter valid IP address'): - self.minip = minip, - self.maxip = maxip, - self.invert = invert - self.is_localhost = is_localhost - self.is_private = is_private - self.is_automatic = is_automatic - self.is_ipv4 = is_ipv4 or is_ipv6 is False - self.is_private = is_private - self.is_link_local = is_link_local - self.is_reserved = is_reserved - self.is_multicast = is_multicast - self.is_routeable = is_routeable - self.is_6to4 = is_6to4 - self.is_teredo = is_teredo - self.subnets = subnets - self.is_ipv6 = is_ipv6 or is_ipv4 is False - self.error_message = error_message - - def __call__(self, value): - from gluon._compat import ipaddress - IPAddress = ipaddress.ip_address - IPv6Address = ipaddress.IPv6Address - IPv4Address = ipaddress.IPv4Address - - try: - ip = IPAddress(to_unicode(value)) - except ValueError: - return (value, translate(self.error_message)) - - if self.is_ipv4 and isinstance(ip, IPv6Address): - retval = (value, translate(self.error_message)) - elif self.is_ipv6 and isinstance(ip, IPv4Address): - retval = (value, translate(self.error_message)) - elif self.is_ipv4 or isinstance(ip, IPv4Address): - retval = IS_IPV4( - minip=self.minip, - maxip=self.maxip, - invert=self.invert, - is_localhost=self.is_localhost, - is_private=self.is_private, - is_automatic=self.is_automatic, - error_message=self.error_message - )(value) - elif self.is_ipv6 or isinstance(ip, IPv6Address): - retval = IS_IPV6( - is_private=self.is_private, - is_link_local=self.is_link_local, - is_reserved=self.is_reserved, - is_multicast=self.is_multicast, - is_routeable=self.is_routeable, - is_6to4=self.is_6to4, - is_teredo=self.is_teredo, - subnets=self.subnets, - error_message=self.error_message - )(value) - else: - retval = (value, translate(self.error_message)) - - return retval +from pydal.validators import * +from pydal.validators import simple_hash, get_digest, Validator, ValidationError, translate, __all__ diff --git a/gluon/widget.py b/gluon/widget.py index fbd522ae..b41974ff 100644 --- a/gluon/widget.py +++ b/gluon/widget.py @@ -1,110 +1,92 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- +# vim: set ts=4 sw=4 et ai: """ | This file is part of the web2py Web Framework | Copyrighted by Massimo Di Pierro | License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) -The widget is called from web2py ----------------------------------- +GUI widget and services start function +-------------------------------------- """ + from __future__ import print_function -import datetime -import sys -from gluon._compat import StringIO, thread, xrange, PY2 import time -import threading +import sys import os -import copy +from collections import OrderedDict import socket -import signal +import threading import math import logging +import signal import getpass -from gluon import main, newcron - -from gluon.fileutils import read_file, write_file, create_welcome_w2p +from gluon.fileutils import read_file, create_welcome_w2p +from gluon.shell import die, run, test +from gluon._compat import PY2, xrange +from gluon.utils import (getipaddrinfo, is_loopback_ip_address, + is_valid_ip_address) +from gluon.console import is_appdir, console +from gluon import newcron +from gluon import main from gluon.settings import global_settings -from gluon.shell import run, test -from gluon.utils import is_valid_ip_address, is_loopback_ip_address, getipaddrinfo ProgramName = 'web2py Web Framework' ProgramAuthor = 'Created by Massimo Di Pierro, Copyright 2007-' + str( - datetime.datetime.now().year) -ProgramVersion = read_file('VERSION').strip() + time.localtime().tm_year) +ProgramVersion = read_file('VERSION').rstrip() -ProgramInfo = '''%s - %s - %s''' % (ProgramName, ProgramAuthor, ProgramVersion) - -if sys.version_info < (2, 7) and (3, 0) < sys.version_info < (3, 5): - msg = 'Warning: web2py requires at least Python 2.7/3.5 but you are running:\n%s' - msg = msg % sys.version - sys.stderr.write(msg) - -logger = logging.getLogger("web2py") +if sys.version_info < (2, 7) or (3, 0) < sys.version_info < (3, 5): + from platform import python_version + sys.stderr.write("Warning: web2py requires at least Python 2.7/3.5" + " but you are running %s\n" % python_version()) def run_system_tests(options): """ Runs unittests for gluon.tests """ - import subprocess - major_version = sys.version_info[0] - call_args = [sys.executable, '-m', 'unittest', '-v', 'gluon.tests'] - if major_version == 2: - sys.stderr.write("Python 2.7\n") - else: - sys.stderr.write("Experimental Python 3.x.\n") + # see "python -m unittest -h" for unittest options help + # NOTE: someone might be interested either in using the + # -f (--failfast) option to stop testing on first failure, or + # in customizing the test selection, for example to run only + # 'gluon.tests.', 'gluon.tests..' (this + # could be shortened as 'gluon.tests.'), or even + # 'gluon.tests...' (or + # the shorter 'gluon.tests..') + call_args = ['-m', 'unittest', '-c', 'gluon.tests'] + if options.verbose: + call_args.insert(-1, '-v') if options.with_coverage: - has_coverage = False - coverage_exec = 'coverage2' if major_version == 2 else 'coverage3' try: import coverage - has_coverage = True except: - sys.stderr.write('Coverage was not installed, skipping\n') + die('Coverage not installed') + if not PY2: + sys.stderr.write('Experimental ') + sys.stderr.write("Python %s\n" % sys.version) + if options.with_coverage: + coverage_exec = 'coverage2' if PY2 else 'coverage3' coverage_config_file = os.path.join('gluon', 'tests', 'coverage.ini') coverage_config = os.environ.setdefault("COVERAGE_PROCESS_START", coverage_config_file) - call_args = [coverage_exec, 'run', '--rcfile=%s' % - coverage_config, '-m', 'unittest', '-v', 'gluon.tests'] - if has_coverage: - ret = subprocess.call(call_args) - else: - ret = 256 + run_args = [coverage_exec, 'run', '--rcfile=%s' % coverage_config] + # replace the current process + os.execvpe(run_args[0], run_args + call_args, os.environ) else: - ret = subprocess.call(call_args) - sys.exit(ret and 1) - - -class IO(object): - """ """ - - def __init__(self): - """ """ - - self.buffer = StringIO() - - def write(self, data): - """ """ - - sys.__stdout__.write(data) - if hasattr(self, 'callback'): - self.callback(data) - else: - self.buffer.write(data) + run_args = [sys.executable] + # replace the current process + os.execv(run_args[0], run_args + call_args) def get_url(host, path='/', proto='http', port=80): if ':' in host: host = '[%s]' % host - else: - host = host.replace('0.0.0.0', '127.0.0.1') + elif host == '0.0.0.0': + host = '127.0.0.1' if path.startswith('/'): path = path[1:] if proto.endswith(':'): @@ -119,7 +101,7 @@ def get_url(host, path='/', proto='http', port=80): def start_browser(url, startup=False): if startup: print('please visit:') - print('\t', url) + print('\t' + url) print('starting browser...') try: import webbrowser @@ -141,24 +123,27 @@ class web2pyDialog(object): import tkinter from tkinter import messagebox - - bg_color = 'white' root.withdraw() + bg_color = 'white' self.root = tkinter.Toplevel(root, bg=bg_color) self.root.resizable(0, 0) self.root.title(ProgramName) self.options = options - self.scheduler_processes = {} - self.menu = tkinter.Menu(self.root) - servermenu = tkinter.Menu(self.menu, tearoff=0) - httplog = os.path.join(self.options.folder, self.options.log_filename) + self.scheduler_processes_lock = threading.RLock() + self.scheduler_processes = OrderedDict() + iconphoto = os.path.join('extras', 'icons', 'web2py.gif') if os.path.exists(iconphoto): img = tkinter.PhotoImage(file=iconphoto) self.root.tk.call('wm', 'iconphoto', self.root._w, img) + # Building the Menu + self.menu = tkinter.Menu(self.root) + servermenu = tkinter.Menu(self.menu, tearoff=0) + + httplog = os.path.join(options.folder, options.log_filename) item = lambda: start_browser(httplog) servermenu.add_command(label='View httpserver.log', command=item) @@ -171,10 +156,9 @@ class web2pyDialog(object): self.pagesmenu = tkinter.Menu(self.menu, tearoff=0) self.menu.add_cascade(label='Pages', menu=self.pagesmenu) - #scheduler menu self.schedmenu = tkinter.Menu(self.menu, tearoff=0) self.menu.add_cascade(label='Scheduler', menu=self.schedmenu) - #start and register schedulers from options + # register and start schedulers self.update_schedulers(start=True) helpmenu = tkinter.Menu(self.menu, tearoff=0) @@ -185,6 +169,9 @@ class web2pyDialog(object): command=item) # About + ProgramInfo = """%s + %s + %s""" % (ProgramName, ProgramAuthor, ProgramVersion) item = lambda: messagebox.showinfo('About web2py', ProgramInfo) helpmenu.add_command(label='About', command=item) @@ -235,6 +222,14 @@ class web2pyDialog(object): self.bannerarea.after(1000, self.update_canvas) # IP + # retrieves the list of server IP addresses + try: + if_ips = list(set( # no duplicates + [addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn()) + if not is_loopback_ip_address(addrinfo=addrinfo)])) + except socket.gaierror: + if_ips = [] + tkinter.Label(self.root, text='Server IP:', bg=bg_color, justify=tkinter.RIGHT).grid(row=4, @@ -245,7 +240,7 @@ class web2pyDialog(object): row = 4 ips = [('127.0.0.1', 'Local (IPv4)')] + \ ([('::1', 'Local (IPv6)')] if socket.has_ipv6 else []) + \ - [(ip, 'Public') for ip in options.ips] + \ + [(ip, 'Public') for ip in if_ips] + \ [('0.0.0.0', 'Public')] for ip, legend in ips: self.ips[ip] = tkinter.Radiobutton( @@ -268,7 +263,7 @@ class web2pyDialog(object): sticky=sticky) self.port_number = tkinter.Entry(self.root) - self.port_number.insert(tkinter.END, self.options.port) + self.port_number.insert(tkinter.END, options.port) self.port_number.grid(row=shift, column=2, sticky=sticky, pady=10) # Password @@ -326,109 +321,117 @@ class web2pyDialog(object): def update_schedulers(self, start=False): applications_folder = os.path.join(self.options.folder, 'applications') - apps = [] available_apps = [ arq for arq in os.listdir(applications_folder) - if os.path.exists(os.path.join(applications_folder, arq, 'models', 'scheduler.py')) + if os.path.isdir(os.path.join(applications_folder, arq)) ] - if start: - # the widget takes care of starting the scheduler - if self.options.scheduler and self.options.with_scheduler: - apps = [app.strip() for app - in self.options.scheduler.split(',') - if app in available_apps] + with self.scheduler_processes_lock: + # reset the menu + # since applications can disappear (be disinstalled) must + # clear the menu (should use tkinter.END or tkinter.LAST) + self.schedmenu.delete(0, 'end') + for arq in available_apps: + if arq not in self.scheduler_processes: + item = lambda a = arq: self.try_start_scheduler(a) + self.schedmenu.add_command(label="start %s" % arq, + command=item) + if arq in self.scheduler_processes: + item = lambda a = arq: self.try_stop_scheduler(a) + self.schedmenu.add_command(label="stop %s" % arq, + command=item) + + if start and self.options.with_scheduler and self.options.schedulers: + # the widget takes care of starting the schedulers + apps = [ag.split(':', 1)[0] for ag in self.options.schedulers] + else: + apps = [] for app in apps: self.try_start_scheduler(app) - # reset the menu - self.schedmenu.delete(0, len(available_apps)) - - for arq in available_apps: - if arq not in self.scheduler_processes: - item = lambda u = arq: self.try_start_scheduler(u) - self.schedmenu.add_command(label="start %s" % arq, - command=item) - if arq in self.scheduler_processes: - item = lambda u = arq: self.try_stop_scheduler(u) - self.schedmenu.add_command(label="stop %s" % arq, - command=item) - def start_schedulers(self, app): - try: - from multiprocessing import Process - except: - sys.stderr.write('Sorry, -K only supported for python 2.6-2.7\n') - return + from multiprocessing import Process code = "from gluon.globals import current;current._scheduler.loop()" print('starting scheduler from widget for "%s"...' % app) - args = (app, True, True, None, False, code) - logging.getLogger().setLevel(self.options.debuglevel) + args = (app, True, True, None, False, code, False, True) p = Process(target=run, args=args) - self.scheduler_processes[app] = p - self.update_schedulers() - print("Currently running %s scheduler processes" % ( - len(self.scheduler_processes))) + with self.scheduler_processes_lock: + self.scheduler_processes[app] = p + self.update_schedulers() + print("Currently running %s scheduler processes" % ( + len(self.scheduler_processes))) p.start() print("Processes started") - def try_stop_scheduler(self, app): - if app in self.scheduler_processes: - p = self.scheduler_processes[app] - del self.scheduler_processes[app] + def try_stop_scheduler(self, app, skip_update=False): + p = None + with self.scheduler_processes_lock: + if app in self.scheduler_processes: + p = self.scheduler_processes[app] + del self.scheduler_processes[app] + if p is not None: p.terminate() p.join() - self.update_schedulers() + if not skip_update: + self.update_schedulers() def try_start_scheduler(self, app): - if app not in self.scheduler_processes: - t = threading.Thread(target=self.start_schedulers, args=(app,)) + t = None + with self.scheduler_processes_lock: + if not is_appdir(self.options.folder, app): + self.schedmenu.delete("start %s" % app) + return + if app not in self.scheduler_processes: + t = threading.Thread(target=self.start_schedulers, args=(app,)) + if t is not None: t.start() def checkTaskBar(self): """ Checks taskbar status """ - - if self.tb.status: - if self.tb.status[0] == self.tb.EnumStatus.QUIT: + tb = self.tb + if tb.status: + st0 = tb.status[0] + EnumStatus = tb.EnumStatus + if st0 == EnumStatus.QUIT: self.quit() - elif self.tb.status[0] == self.tb.EnumStatus.TOGGLE: + elif st0 == EnumStatus.TOGGLE: if self.root.state() == 'withdrawn': self.root.deiconify() else: self.root.withdraw() - elif self.tb.status[0] == self.tb.EnumStatus.STOP: + elif st0 == EnumStatus.STOP: self.stop() - elif self.tb.status[0] == self.tb.EnumStatus.START: + elif st0 == EnumStatus.START: self.start() - elif self.tb.status[0] == self.tb.EnumStatus.RESTART: + elif st0 == EnumStatus.RESTART: self.stop() self.start() - del self.tb.status[0] + del tb.status[0] self.root.after(1000, self.checkTaskBar) - def update(self, text): - """ Updates app text """ - - try: - self.text.configure(state='normal') - self.text.insert('end', text) - self.text.configure(state='disabled') - except: - pass # ## this should only happen in case app is destroyed - def connect_pages(self): """ Connects pages """ - # reset the menu + # reset the menu, + # since applications can disappear (be disinstalled) must + # clear the menu (should use tkinter.END or tkinter.LAST) + self.pagesmenu.delete(0, 'end') applications_folder = os.path.join(self.options.folder, 'applications') available_apps = [ arq for arq in os.listdir(applications_folder) if os.path.exists(os.path.join(applications_folder, arq, '__init__.py')) ] - self.pagesmenu.delete(0, len(available_apps)) for arq in available_apps: url = self.url + arq + item = lambda a = arq: self.try_start_browser(a) self.pagesmenu.add_command( - label=url, command=lambda u=url: start_browser(u)) + label=url, command=item) + + def try_start_browser(self, app): + url = self.url + app + if not is_appdir(self.options.folder, app): + self.pagesmenu.delete(url) + return + start_browser(url) def quit(self, justHide=False): """ Finishes the program execution """ @@ -436,16 +439,20 @@ class web2pyDialog(object): self.root.withdraw() else: try: - scheds = self.scheduler_processes.keys() + with self.scheduler_processes_lock: + scheds = list(self.scheduler_processes.keys()) for t in scheds: - self.try_stop_scheduler(t) - except: - pass - try: - newcron.stopcron() + self.try_stop_scheduler(t, skip_update=True) except: pass + if self.options.with_cron and not self.options.soft_cron: + # shutting down hardcron + try: + newcron.stopcron() + except: + pass try: + # HttpServer.stop takes care of stopping softcron self.server.stop() except: pass @@ -463,36 +470,40 @@ class web2pyDialog(object): import tkMessageBox as messagebox else: from tkinter import messagebox - messagebox.showerror('web2py start server', message) def start(self): """ Starts web2py server """ - password = self.password.get() - if not password: self.error('no password, no web admin interface') ip = self.selected_ip.get() - if not is_valid_ip_address(ip): return self.error('invalid host ip address') - try: port = int(self.port_number.get()) - except: + except ValueError: return self.error('invalid port number') - # Check for non default value for ssl inputs - if (len(self.options.ssl_certificate) > 0 or - len(self.options.ssl_private_key) > 0): + if self.options.server_key and self.options.server_cert: proto = 'https' else: proto = 'http' - self.url = get_url(ip, proto=proto, port=port) + self.connect_pages() + self.update_schedulers() + + # softcron is stopped with HttpServer, thus if starting again + # need to reset newcron._stopping to re-enable cron + if self.options.soft_cron: + newcron.reset() + + # FIXME: if the HttpServer is stopped, then started again, + # does not start because of following error: + # WARNING:Rocket.Errors.Port8000:Listener started when not ready. + self.button_start.configure(state='disabled') try: @@ -505,11 +516,11 @@ class web2pyDialog(object): pid_filename=options.pid_filename, log_filename=options.log_filename, profiler_dir=options.profiler_dir, - ssl_certificate=options.ssl_certificate, - ssl_private_key=options.ssl_private_key, - ssl_ca_certificate=options.ssl_ca_certificate, - min_threads=options.minthreads, - max_threads=options.maxthreads, + ssl_certificate=options.server_cert, + ssl_private_key=options.server_key, + ssl_ca_certificate=options.ca_cert, + min_threads=options.min_threads, + max_threads=options.max_threads, server_name=options.server_name, request_queue_size=req_queue_size, timeout=options.timeout, @@ -517,7 +528,7 @@ class web2pyDialog(object): path=options.folder, interfaces=options.interfaces) - thread.start_new_thread(self.server.start, ()) + threading.Thread(target=self.server.start).start() except Exception as e: self.button_start.configure(state='normal') return self.error(str(e)) @@ -529,11 +540,14 @@ class web2pyDialog(object): self.button_stop.configure(state='normal') if not options.taskbar: - thread.start_new_thread( - start_browser, (get_url(ip, proto=proto, port=port), True)) + cpt = threading.Thread(target=start_browser, + args=(get_url(ip, proto=proto, port=port), True)) + cpt.setDaemon(True) + cpt.start() self.password.configure(state='readonly') - [ip.configure(state='disabled') for ip in self.ips.values()] + for ip in self.ips.values(): + ip.configure(state='disabled') self.port_number.configure(state='readonly') if self.tb: @@ -543,16 +557,15 @@ class web2pyDialog(object): for listener in self.server.server.listeners: if listener.ready: return True - return False def stop(self): """ Stops web2py server """ - self.button_start.configure(state='normal') self.button_stop.configure(state='disabled') self.password.configure(state='normal') - [ip.configure(state='normal') for ip in self.ips.values()] + for ip in self.ips.values(): + ip.configure(state='normal') self.port_number.configure(state='normal') self.server.stop() @@ -561,518 +574,60 @@ class web2pyDialog(object): def update_canvas(self): """ Updates canvas """ - httplog = os.path.join(self.options.folder, self.options.log_filename) + canvas = self.canvas try: t1 = os.path.getsize(httplog) - except: - self.canvas.after(1000, self.update_canvas) + except OSError: + canvas.after(1000, self.update_canvas) return + points = 400 try: - fp = open(httplog, 'r') - fp.seek(self.t0) - data = fp.read(t1 - self.t0) - fp.close() - value = self.p0[1:] + [10 + 90.0 / math.sqrt(1 + data.count('\n'))] - self.p0 = value + pvalues = self.p0[1:] + with open(httplog, 'r') as fp: + fp.seek(self.t0) + data = fp.read(t1 - self.t0) + self.p0 = pvalues + [10 + 90.0 / math.sqrt(1 + data.count('\n'))] - for i in xrange(len(self.p0) - 1): - c = self.canvas.coords(self.q0[i]) - self.canvas.coords(self.q0[i], - (c[0], - self.p0[i], - c[2], - self.p0[i + 1])) + for i in xrange(points - 1): + c = canvas.coords(self.q0[i]) + canvas.coords(self.q0[i], + (c[0], self.p0[i], + c[2], self.p0[i + 1])) self.t0 = t1 - except BaseException: + except AttributeError: self.t0 = time.time() self.t0 = t1 - self.p0 = [100] * 400 - self.q0 = [self.canvas.create_line(i, 100, i + 1, 100, - fill='green') for i in xrange(len(self.p0) - 1)] + self.p0 = [100] * points + self.q0 = [canvas.create_line(i, 100, i + 1, 100, + fill='green') for i in xrange(points - 1)] - self.canvas.after(1000, self.update_canvas) + canvas.after(1000, self.update_canvas) -def console(): - """ Defines the behavior of the console web2py execution """ - import optparse - import textwrap - - usage = "python web2py.py" - - description = """\ - web2py Web Framework startup script. - ATTENTION: unless a password is specified (-a 'passwd') web2py will - attempt to run a GUI. In this case command line options are ignored.""" - - description = textwrap.dedent(description) - - parser = optparse.OptionParser( - usage, None, optparse.Option, ProgramVersion) - - parser.description = description - - msg = ('IP address of the server (e.g., 127.0.0.1 or ::1); ' - 'Note: This value is ignored when using the \'interfaces\' option.') - parser.add_option('-i', - '--ip', - default='127.0.0.1', - dest='ip', - help=msg) - - parser.add_option('-p', - '--port', - default='8000', - dest='port', - type='int', - help='port of server (8000)') - - parser.add_option('-G', - '--GAE', - default=None, - dest='gae', - help="'-G configure' will create app.yaml and gaehandler.py") - - msg = ('password to be used for administration ' - '(use -a "" to reuse the last password))') - parser.add_option('-a', - '--password', - default='', - dest='password', - help=msg) - - parser.add_option('-c', - '--ssl_certificate', - default='', - dest='ssl_certificate', - help='file that contains ssl certificate') - - parser.add_option('-k', - '--ssl_private_key', - default='', - dest='ssl_private_key', - help='file that contains ssl private key') - - msg = ('Use this file containing the CA certificate to validate X509 ' - 'certificates from clients') - parser.add_option('--ca-cert', - action='store', - dest='ssl_ca_certificate', - default=None, - help=msg) - - parser.add_option('-d', - '--pid_filename', - default='httpserver.pid', - dest='pid_filename', - help='file to store the pid of the server') - - parser.add_option('-l', - '--log_filename', - default='httpserver.log', - dest='log_filename', - help='file to log connections') - - parser.add_option('-n', - '--numthreads', - default=None, - type='int', - dest='numthreads', - help='number of threads (deprecated)') - - parser.add_option('--minthreads', - default=None, - type='int', - dest='minthreads', - help='minimum number of server threads') - - parser.add_option('--maxthreads', - default=None, - type='int', - dest='maxthreads', - help='maximum number of server threads') - - parser.add_option('-s', - '--server_name', - default=socket.gethostname(), - dest='server_name', - help='server name for the web server') - - msg = 'max number of queued requests when server unavailable' - parser.add_option('-q', - '--request_queue_size', - default='5', - type='int', - dest='request_queue_size', - help=msg) - - parser.add_option('-o', - '--timeout', - default='10', - type='int', - dest='timeout', - help='timeout for individual request (10 seconds)') - - parser.add_option('-z', - '--shutdown_timeout', - default='5', - type='int', - dest='shutdown_timeout', - help='timeout on shutdown of server (5 seconds)') - - parser.add_option('--socket-timeout', - default=5, - type='int', - dest='socket_timeout', - help='timeout for socket (5 second)') - - parser.add_option('-f', - '--folder', - default=os.getcwd(), - dest='folder', - help='folder from which to run web2py') - - parser.add_option('-v', - '--verbose', - action='store_true', - dest='verbose', - default=False, - help='increase --test verbosity') - - parser.add_option('-Q', - '--quiet', - action='store_true', - dest='quiet', - default=False, - help='disable all output') - - parser.add_option('-e', - '--errors_to_console', - action='store_true', - dest='print_errors', - default=False, - help='log all errors to console') - - msg = ('set debug output level (0-100, 0 means all, 100 means none; ' - 'default is 30)') - parser.add_option('-D', - '--debug', - dest='debuglevel', - default=30, - type='int', - help=msg) - - msg = ('run web2py in interactive shell or IPython (if installed) with ' - 'specified appname (if app does not exist it will be created). ' - 'APPNAME like a/c/f?x=y (c,f and vars x,y optional)') - parser.add_option('-S', - '--shell', - dest='shell', - metavar='APPNAME', - help=msg) - - msg = ('run web2py in interactive shell or bpython (if installed) with ' - 'specified appname (if app does not exist it will be created).\n' - 'Use combined with --shell') - parser.add_option('-B', - '--bpython', - action='store_true', - default=False, - dest='bpython', - help=msg) - - msg = 'only use plain python shell; should be used with --shell option' - parser.add_option('-P', - '--plain', - action='store_true', - default=False, - dest='plain', - help=msg) - - msg = ('auto import model files; default is False; should be used ' - 'with --shell option') - parser.add_option('-M', - '--import_models', - action='store_true', - default=False, - dest='import_models', - help=msg) - - msg = ('run PYTHON_FILE in web2py environment; ' - 'should be used with --shell option') - parser.add_option('-R', - '--run', - dest='run', - metavar='PYTHON_FILE', - default='', - help=msg) - - msg = ('run scheduled tasks for the specified apps: expects a list of ' - 'app names as -K app1,app2,app3 ' - 'or a list of app:groups as -K app1:group1:group2,app2:group1 ' - 'to override specific group_names. (only strings, no spaces ' - 'allowed. Requires a scheduler defined in the models') - parser.add_option('-K', - '--scheduler', - dest='scheduler', - default=None, - help=msg) - - msg = 'run schedulers alongside webserver, needs -K app1 and -a too' - parser.add_option('-X', - '--with-scheduler', - action='store_true', - default=False, - dest='with_scheduler', - help=msg) - - msg = ('run doctests in web2py environment; ' - 'TEST_PATH like a/c/f (c,f optional)') - parser.add_option('-T', - '--test', - dest='test', - metavar='TEST_PATH', - default=None, - help=msg) - - msg = 'trigger a cron run manually; usually invoked from a system crontab' - parser.add_option('-C', - '--cron', - action='store_true', - dest='extcron', - default=False, - help=msg) - - msg = 'triggers the use of softcron' - parser.add_option('--softcron', - action='store_true', - dest='softcron', - default=False, - help=msg) - - parser.add_option('-Y', - '--run-cron', - action='store_true', - dest='runcron', - default=False, - help='start the background cron process') - - parser.add_option('-J', - '--cronjob', - action='store_true', - dest='cronjob', - default=False, - help='identify cron-initiated command') - - parser.add_option('-L', - '--config', - dest='config', - default='', - help='config file') - - parser.add_option('-F', - '--profiler', - dest='profiler_dir', - default=None, - help='profiler dir') - - parser.add_option('-t', - '--taskbar', - action='store_true', - dest='taskbar', - default=False, - help='use web2py gui and run in taskbar (system tray)') - - parser.add_option('', - '--nogui', - action='store_true', - default=False, - dest='nogui', - help='text-only, no GUI') - - msg = ('should be followed by a list of arguments to be passed to script, ' - 'to be used with -S, -A must be the last option') - parser.add_option('-A', - '--args', - action='store', - dest='args', - default=None, - help=msg) - - parser.add_option('--no-banner', - action='store_true', - default=False, - dest='nobanner', - help='Do not print header banner') - - msg = ('listen on multiple addresses: ' - '"ip1:port1:key1:cert1:ca_cert1;ip2:port2:key2:cert2:ca_cert2;..." ' - '(:key:cert:ca_cert optional; no spaces; IPv6 addresses must be in ' - 'square [] brackets)') - parser.add_option('--interfaces', - action='store', - dest='interfaces', - default=None, - help=msg) - - msg = 'runs web2py tests' - parser.add_option('--run_system_tests', - action='store_true', - dest='run_system_tests', - default=False, - help=msg) - - msg = ('adds coverage reporting (needs --run_system_tests), ' - 'python 2.7 and the coverage module installed. ' - 'You can alter the default path setting the environmental ' - 'var "COVERAGE_PROCESS_START". ' - 'By default it takes gluon/tests/coverage.ini') - parser.add_option('--with_coverage', - action='store_true', - dest='with_coverage', - default=False, - help=msg) - - if '-A' in sys.argv: - k = sys.argv.index('-A') - elif '--args' in sys.argv: - k = sys.argv.index('--args') - else: - k = len(sys.argv) - sys.argv, other_args = sys.argv[:k], sys.argv[k + 1:] - (options, args) = parser.parse_args() - options.args = [options.run] + other_args - - copy_options = copy.deepcopy(options) - copy_options.password = '******' - global_settings.cmd_options = copy_options - global_settings.cmd_args = args - - if options.gae: - if not os.path.exists('app.yaml'): - name = raw_input("Your GAE app name: ") - content = open(os.path.join('examples', 'app.example.yaml'), 'rb').read() - open('app.yaml', 'wb').write(content.replace("yourappname", name)) - else: - print("app.yaml alreday exists in the web2py folder") - if not os.path.exists('gaehandler.py'): - content = open(os.path.join('handlers', 'gaehandler.py'), 'rb').read() - open('gaehandler.py', 'wb').write(content) - else: - print("gaehandler.py alreday exists in the web2py folder") - sys.exit(0) - - try: - options.ips = list(set( # no duplicates - [addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn()) - if not is_loopback_ip_address(addrinfo=addrinfo)])) - except socket.gaierror: - options.ips = [] - - if options.run_system_tests: - run_system_tests(options) - - if options.quiet: - capture = StringIO() - sys.stdout = capture - logger.setLevel(logging.CRITICAL + 1) - else: - logger.setLevel(options.debuglevel) - - if options.config[-3:] == '.py': - options.config = options.config[:-3] - - if options.cronjob: - global_settings.cronjob = True # tell the world - options.plain = True # cronjobs use a plain shell - options.nobanner = True - options.nogui = True - - options.folder = os.path.abspath(options.folder) - - # accept --interfaces in the form - # "ip1:port1:key1:cert1:ca_cert1;[ip2]:port2;ip3:port3:key3:cert3" - # (no spaces; optional key:cert indicate SSL) - if isinstance(options.interfaces, str): - interfaces = options.interfaces.split(';') - options.interfaces = [] - for interface in interfaces: - if interface.startswith('['): # IPv6 - ip, if_remainder = interface.split(']', 1) - ip = ip[1:] - if_remainder = if_remainder[1:].split(':') - if_remainder[0] = int(if_remainder[0]) # numeric port - options.interfaces.append(tuple([ip] + if_remainder)) - else: # IPv4 - interface = interface.split(':') - interface[1] = int(interface[1]) # numeric port - options.interfaces.append(tuple(interface)) - - # accepts --scheduler in the form - # "app:group1,group2,app2:group1" - scheduler = [] - options.scheduler_groups = None - if isinstance(options.scheduler, str): - if ':' in options.scheduler: - for opt in options.scheduler.split(','): - scheduler.append(opt.split(':')) - options.scheduler = ','.join([app[0] for app in scheduler]) - options.scheduler_groups = scheduler - - if options.numthreads is not None and options.minthreads is None: - options.minthreads = options.numthreads # legacy - - create_welcome_w2p() - - if not options.cronjob: - # If we have the applications package or if we should upgrade - if not os.path.exists('applications/__init__.py'): - write_file('applications/__init__.py', '') - - return options, args - - -def check_existent_app(options, appname): - if os.path.isdir(os.path.join(options.folder, 'applications', appname)): - return True - - -def get_code_for_scheduler(app, options): - if len(app) == 1 or app[1] is None: - code = "from gluon.globals import current;current._scheduler.loop()" - else: - code = "from gluon.globals import current;current._scheduler.group_names = ['%s'];" - code += "current._scheduler.loop()" - code = code % ("','".join(app[1:])) - app_ = app[0] - if not check_existent_app(options, app_): - print("Application '%s' doesn't exist, skipping" % app_) +def get_code_for_scheduler(applications_parent, app_groups): + app = app_groups[0] + if not is_appdir(applications_parent, app): + print("Application '%s' doesn't exist, skipping" % app) return None, None - return app_, code + code = 'from gluon.globals import current;' + if len(app_groups) > 1: + code += "current._scheduler.group_names=['%s'];" % "','".join( + app_groups[1:]) + code += "current._scheduler.loop()" + return app, code def start_schedulers(options): - try: - from multiprocessing import Process - except: - sys.stderr.write('Sorry, -K only supported for python 2.6-2.7\n') - return - processes = [] - apps = [(app.strip(), None) for app in options.scheduler.split(',')] - if options.scheduler_groups: - apps = options.scheduler_groups - code = "from gluon.globals import current;current._scheduler.loop()" - logging.getLogger().setLevel(options.debuglevel) - if options.folder: - os.chdir(options.folder) - if len(apps) == 1 and not options.with_scheduler: - app_, code = get_code_for_scheduler(apps[0], options) - if not app_: + from multiprocessing import Process + apps = [ag.split(':') for ag in options.schedulers] + if not options.with_scheduler and len(apps) == 1: + app, code = get_code_for_scheduler(options.folder, apps[0]) + if not app: return - print('starting single-scheduler for "%s"...' % app_) - run(app_, True, True, None, False, code) + print('starting single-scheduler for "%s"...' % app) + run(app, True, True, None, False, code, False, True) return # Work around OS X problem: http://bugs.python.org/issue9405 @@ -1082,12 +637,13 @@ def start_schedulers(options): import urllib.request as urllib urllib.getproxies() - for app in apps: - app_, code = get_code_for_scheduler(app, options) - if not app_: + processes = [] + for app_groups in apps: + app, code = get_code_for_scheduler(options.folder, app_groups) + if not app: continue - print('starting scheduler for "%s"...' % app_) - args = (app_, True, True, None, False, code) + print('starting scheduler for "%s"...' % app) + args = (app, True, True, None, False, code, False, True) p = Process(target=run, args=args) processes.append(p) print("Currently running %s scheduler processes" % (len(processes))) @@ -1105,119 +661,140 @@ def start_schedulers(options): p.join() -def start(cron=True): - """ Starts server """ +def start(): + """ Starts server and other services """ - # ## get command line arguments + # get command line arguments + options = console(version=ProgramVersion) - (options, args) = console() + if options.with_scheduler or len(options.schedulers) > 1: + try: + from multiprocessing import Process + except: + die('Sorry, -K/--scheduler only supported for Python 2.6+') - if not options.nobanner: + if options.gae: + # write app.yaml, gaehandler.py, and exit + if not os.path.exists('app.yaml'): + name = options.gae + # for backward compatibility + if name == 'configure': + if PY2: input = raw_input + name = input("Your GAE app name: ") + content = open(os.path.join('examples', 'app.example.yaml'), 'rb').read() + open('app.yaml', 'wb').write(content.replace("yourappname", name)) + else: + print("app.yaml alreday exists in the web2py folder") + if not os.path.exists('gaehandler.py'): + content = open(os.path.join('handlers', 'gaehandler.py'), 'rb').read() + open('gaehandler.py', 'wb').write(content) + else: + print("gaehandler.py alreday exists in the web2py folder") + return + + logger = logging.getLogger("web2py") + logger.setLevel(options.log_level) + logging.getLogger().setLevel(options.log_level) # root logger + + # on new installation build the scaffolding app + create_welcome_w2p() + + if options.run_system_tests: + # run system test and exit + run_system_tests(options) + + if options.quiet: + # to prevent writes on stdout set a null stream + class NullFile(object): + def write(self, x): + pass + sys.stdout = NullFile() + # but still has to mute existing loggers, to do that iterate + # over all existing loggers (root logger included) and remove + # all attached logging.StreamHandler instances currently + # streaming on sys.stdout or sys.stderr + loggers = [logging.getLogger()] + loggers.extend(logging.Logger.manager.loggerDict.values()) + for l in loggers: + if isinstance(l, logging.PlaceHolder): continue + for h in l.handlers[:]: + if isinstance(h, logging.StreamHandler) and \ + h.stream in (sys.stdout, sys.stderr): + l.removeHandler(h) + # NOTE: stderr.write() is still working + + if not options.no_banner: + # banner print(ProgramName) print(ProgramAuthor) print(ProgramVersion) - - from pydal.drivers import DRIVERS - if not options.nobanner: + from pydal.drivers import DRIVERS print('Database drivers available: %s' % ', '.join(DRIVERS)) - # ## if -L load options from options.config file - if options.config: - try: - options2 = __import__(options.config, {}, {}, '') - except Exception: - try: - # Jython doesn't like the extra stuff - options2 = __import__(options.config) - except Exception: - print('Cannot import config file [%s]' % options.config) - sys.exit(1) - for key in dir(options2): - if hasattr(options, key): - setattr(options, key, getattr(options2, key)) - - # ## if -T run doctests (no cron) - if hasattr(options, 'test') and options.test: - test(options.test, verbose=options.verbose) + if options.run_doctests: + # run doctests and exit + test(options.run_doctests, verbose=options.verbose) return - # ## if -S start interactive shell (also no cron) if options.shell: - if options.folder: - os.chdir(options.folder) - if not options.args is None: - sys.argv[:] = options.args + # run interactive shell and exit + sys.argv = [options.run or ''] + options.args run(options.shell, plain=options.plain, bpython=options.bpython, import_models=options.import_models, startfile=options.run, - cronjob=options.cronjob) + cron_job=options.cron_job, force_migrate=options.force_migrate) return - # ## if -C start cron run (extcron) and exit - # ## -K specifies optional apps list (overloading scheduler) - if options.extcron: - logger.debug('Starting extcron...') + # set size of cron thread pools + newcron.dancer_size(options.min_threads) + newcron.launcher_size(options.cron_threads) + + if options.cron_run: + # run cron (extcron) and exit + logger.debug('Running extcron...') global_settings.web2py_crontype = 'external' - if options.scheduler: # -K - apps = [app.strip() for app in options.scheduler.split( - ',') if check_existent_app(options, app.strip())] - else: - apps = None - extcron = newcron.extcron(options.folder, apps=apps) - extcron.start() - extcron.join() + newcron.extcron(options.folder, apps=options.crontabs) return - # ## if -K - if options.scheduler and not options.with_scheduler: + if not options.with_scheduler and options.schedulers: + # run schedulers and exit try: start_schedulers(options) except KeyboardInterrupt: pass return - # ## if -H cron is enabled in this *process* - # ## if --softcron use softcron - # ## use hardcron in all other cases - if cron and options.runcron and options.softcron: - print('Using softcron (but this is not very efficient)') - global_settings.web2py_crontype = 'soft' - elif cron and options.runcron: - logger.debug('Starting hardcron...') - global_settings.web2py_crontype = 'hard' - newcron.hardcron(options.folder).start() - - # ## if no password provided and havetk start Tk interface - # ## or start interface if we want to put in taskbar (system tray) - - try: - options.taskbar - except: - options.taskbar = False - - if options.taskbar and os.name != 'nt': - print('Error: taskbar not supported on this platform') - sys.exit(1) + if options.with_cron: + if options.soft_cron: + print('Using cron software emulation (but this is not very efficient)') + global_settings.web2py_crontype = 'soft' + else: + # start hardcron thread + logger.debug('Starting hardcron...') + global_settings.web2py_crontype = 'hard' + newcron.hardcron(options.folder, apps=options.crontabs).start() + # if no password provided and have Tk library start GUI (when not + # explicitly disabled), we also need a GUI to put in taskbar (system tray) + # when requested root = None - if not options.nogui and options.password == '': + if (not options.no_gui and options.password == '') or options.taskbar: try: if PY2: import Tkinter as tkinter else: import tkinter - havetk = True - try: - root = tkinter.Tk() - except: - pass + root = tkinter.Tk() except (ImportError, OSError): logger.warn( 'GUI not available because Tk library is not installed') - havetk = False - options.nogui = True + options.no_gui = True + except: + logger.exception('cannot get Tk root window, GUI disabled') + options.no_gui = True if root: + # run GUI and exit root.focus_force() # Mac OS X - make the GUI window rise to the top @@ -1230,6 +807,7 @@ end tell """ % (os.getpid()) os.system("/usr/bin/osascript -e '%s'" % applescript) + # web2pyDialog takes care of schedulers master = web2pyDialog(root, options) signal.signal(signal.SIGTERM, lambda a, b: master.quit()) @@ -1240,44 +818,45 @@ end tell sys.exit() - # ## if no tk and no password, ask for a password + spt = None - if not root and options.password == '': + if options.with_scheduler and options.schedulers: + # start schedulers in a separate thread + spt = threading.Thread(target=start_schedulers, args=(options,)) + spt.start() + + # start server + + if options.password == '': options.password = getpass.getpass('choose a password:') - if not options.password and not options.nobanner: - print('no password, no admin interface') - - # ##-X (if no tk, the widget takes care of it himself) - if not root and options.scheduler and options.with_scheduler: - t = threading.Thread(target=start_schedulers, args=(options,)) - t.start() - - # ## start server + if not options.password and not options.no_banner: + print('no password, no web admin interface') # Use first interface IP and port if interfaces specified, since the # interfaces option overrides the IP (and related) options. if not options.interfaces: - (ip, port) = (options.ip, int(options.port)) + ip = options.ip + port = options.port else: first_if = options.interfaces[0] - (ip, port) = first_if[0], first_if[1] + ip = first_if[0] + port = first_if[1] - # Check for non default value for ssl inputs - if (len(options.ssl_certificate) > 0) or (len(options.ssl_private_key) > 0): + if options.server_key and options.server_cert: proto = 'https' else: proto = 'http' url = get_url(ip, proto=proto, port=port) - if not options.nobanner: - message = '\nplease visit:\n\t%s\n' % url + if not options.no_banner: + message = '\nplease visit:\n\t%s\n' if sys.platform.startswith('win'): - message += 'use "taskkill /f /pid %i" to shutdown the web2py server\n\n' % os.getpid() + message += 'use "taskkill /f /pid %i" to shutdown the web2py server\n\n' else: - message += 'use "kill -SIGTERM %i" to shutdown the web2py server\n\n' % os.getpid() - print(message) + message += 'use "kill -SIGTERM %i" to shutdown the web2py server\n\n' + print(message % (url, os.getpid())) # enhance linecache.getline (used by debugger) to look at the source file # if the line was not found (under py2exe & when file was modified) @@ -1288,17 +867,15 @@ end tell line = py2exe_getline(filename, lineno, *args, **kwargs) if not line: try: - f = open(filename, "r") - try: + with open(filename, "rb") as f: for i, line in enumerate(f): + line = line.decode('utf-8') if lineno == i + 1: break else: - line = None - finally: - f.close() + line = '' except (IOError, OSError): - line = None + line = '' return line linecache.getline = getline @@ -1308,11 +885,11 @@ end tell pid_filename=options.pid_filename, log_filename=options.log_filename, profiler_dir=options.profiler_dir, - ssl_certificate=options.ssl_certificate, - ssl_private_key=options.ssl_private_key, - ssl_ca_certificate=options.ssl_ca_certificate, - min_threads=options.minthreads, - max_threads=options.maxthreads, + ssl_certificate=options.server_cert, + ssl_private_key=options.server_key, + ssl_ca_certificate=options.ca_cert, + min_threads=options.min_threads, + max_threads=options.max_threads, server_name=options.server_name, request_queue_size=options.request_queue_size, timeout=options.timeout, @@ -1325,8 +902,9 @@ end tell server.start() except KeyboardInterrupt: server.stop() - try: - t.join() - except: - pass + if spt is not None: + try: + spt.join() + except: + logger.exception('error terminating schedulers') logging.shutdown() diff --git a/gluon/xmlrpc.py b/gluon/xmlrpc.py index 4957e995..f163b79a 100644 --- a/gluon/xmlrpc.py +++ b/gluon/xmlrpc.py @@ -7,8 +7,12 @@ | License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) """ -from SimpleXMLRPCServer import SimpleXMLRPCDispatcher +from gluon._compat import PY2 +if PY2: + from SimpleXMLRPCServer import SimpleXMLRPCDispatcher +else: + from xmlrpc.server import SimpleXMLRPCDispatcher def handler(request, response, methods): response.session_id = None # no sessions for xmlrpc diff --git a/scripts/autoroutes.py b/scripts/autoroutes.py old mode 100644 new mode 100755 diff --git a/scripts/check_lang_progress.py b/scripts/check_lang_progress.py old mode 100644 new mode 100755 diff --git a/scripts/cpdb.py b/scripts/cpdb.py old mode 100644 new mode 100755 diff --git a/scripts/cpplugin.py b/scripts/cpplugin.py old mode 100644 new mode 100755 diff --git a/scripts/dict_diff.py b/scripts/dict_diff.py old mode 100644 new mode 100755 diff --git a/scripts/drop-pgsql-tables.sh b/scripts/drop-pgsql-tables.sh old mode 100644 new mode 100755 diff --git a/scripts/extract_mssql_models.py b/scripts/extract_mssql_models.py old mode 100644 new mode 100755 index e6fd807e..7633a6fa --- a/scripts/extract_mssql_models.py +++ b/scripts/extract_mssql_models.py @@ -47,7 +47,7 @@ import sys import re # This is from pydal/helpers/regex.py as of 2016-06-16 # Use this to recognize if a field name need to have an rname representation -REGEX_VALID_TB_FLD = re.compile(r'^[^\d_][_0-9a-zA-Z]*\Z') +REGEX_VALID_TB_FLD = re.compile(r'^[a-zA-Z]\w*\Z') # For replacing invalid characters in field names INVALID_CHARS = re.compile(r'[^a-zA-Z0-9_]') diff --git a/scripts/extract_mysql_models.py b/scripts/extract_mysql_models.py old mode 100644 new mode 100755 diff --git a/scripts/extract_oracle_models.py b/scripts/extract_oracle_models.py old mode 100644 new mode 100755 diff --git a/scripts/extract_pgsql_models.py b/scripts/extract_pgsql_models.py old mode 100644 new mode 100755 diff --git a/scripts/extract_sqlite_models.py b/scripts/extract_sqlite_models.py old mode 100644 new mode 100755 diff --git a/scripts/import_static.py b/scripts/import_static.py old mode 100644 new mode 100755 diff --git a/scripts/make_min_web2py.py b/scripts/make_min_web2py.py old mode 100644 new mode 100755 index 2fef642c..587820d6 --- a/scripts/make_min_web2py.py +++ b/scripts/make_min_web2py.py @@ -48,7 +48,7 @@ def main(): global REQUIRED, IGNORED if len(sys.argv) < 2: - print USAGE + print(USAGE) # make target folder target = sys.argv[1] diff --git a/scripts/migrator.py b/scripts/migrator.py new file mode 100644 index 00000000..d94c5fa6 --- /dev/null +++ b/scripts/migrator.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +''' +To use, e.g. python .\web2py.py -S APPNAME --force_migrate +''' + +import logging + +logger = logging.getLogger("web2py") + + +def get_databases(request): + dbs = {} + global_env = globals() + for (key, value) in global_env.items(): + try: + cond = isinstance(value, GQLDB) + except: + cond = isinstance(value, SQLDB) + if cond: + dbs[key] = value + return dbs + + +logger.debug('Getting all databases') +databases = get_databases(None) +logger.debug('databases = %s', databases) +for db_name in databases: + logger.debug('Migrating %s', db_name) + db = databases[db_name] + tables = db.tables + for table_name in tables: + # Force migration of lazy tables + logger.debug("Ensuring migration of table '%s'", table_name) + db(db[table_name]).isempty() + db.commit() diff --git a/scripts/parse_top_level_domains.py b/scripts/parse_top_level_domains.py old mode 100644 new mode 100755 diff --git a/scripts/rmorphans.py b/scripts/rmorphans.py old mode 100644 new mode 100755 diff --git a/scripts/setup-scheduler-centos.sh b/scripts/setup-scheduler-centos.sh old mode 100644 new mode 100755 diff --git a/scripts/setup-web2py-centos7.sh b/scripts/setup-web2py-centos7.sh old mode 100644 new mode 100755 index 6b74400a..e8c65e96 --- a/scripts/setup-web2py-centos7.sh +++ b/scripts/setup-web2py-centos7.sh @@ -1,6 +1,6 @@ echo "This script will: 1) Install modules needed to run web2py on Fedora and CentOS/RHEL -2) Install Python 2.6 to /opt and recompile wsgi if not provided +2) Install Python 3.7 to /opt and recompile wsgi if not provided 2) Install web2py in /opt/web-apps/ 3) Configure SELinux and iptables 5) Create a self signed ssl certificate @@ -56,7 +56,7 @@ echo yum update # Install required packages -yum install httpd mod_ssl mod_wsgi wget python unzip +yum install httpd mod_ssl mod_wsgi wget python3 unzip ### ### Phase 2 - Install web2py diff --git a/scripts/setup-web2py-debian-sid.sh b/scripts/setup-web2py-debian-sid.sh old mode 100644 new mode 100755 index cf9b1a92..88b8431f --- a/scripts/setup-web2py-debian-sid.sh +++ b/scripts/setup-web2py-debian-sid.sh @@ -38,8 +38,8 @@ apt-get -y install libapache2-mod-wsgi apt-get -y install python-psycopg2 apt-get -y install postfix apt-get -y install wget -apt-get -y install python-matplotlib -apt-get -y install python-reportlab +apt-get -y install python3-matplotlib +apt-get -y install python3-reportlab apt-get -y install mercurial /etc/init.d/postgresql restart diff --git a/scripts/setup-web2py-fedora-ami.sh b/scripts/setup-web2py-fedora-ami.sh index 5ef2acaf..d726796b 100755 --- a/scripts/setup-web2py-fedora-ami.sh +++ b/scripts/setup-web2py-fedora-ami.sh @@ -1,6 +1,6 @@ echo "This script will: 1) Install modules needed to run web2py on Fedora and CentOS/RHEL -2) Install Python 2.6 to /opt and recompile wsgi if not provided +2) Install Python 3.7 to /opt and recompile wsgi if not provided 2) Install web2py in /opt/web-apps/ 3) Configure SELinux and iptables 5) Create a self signed ssl certificate @@ -54,7 +54,7 @@ echo yum update # Install required packages -yum install httpd mod_ssl mod_wsgi wget python +yum install httpd mod_ssl mod_wsgi wget python3 # Verify we have at least Python 2.5 typeset -i version_major diff --git a/scripts/setup-web2py-fedora.sh b/scripts/setup-web2py-fedora.sh old mode 100644 new mode 100755 index d7d6d01c..f0f3b0ed --- a/scripts/setup-web2py-fedora.sh +++ b/scripts/setup-web2py-fedora.sh @@ -1,7 +1,7 @@ #!/bin/bash echo "This script will: 1) Install modules needed to run web2py on Fedora and CentOS/RHEL -2) Install Python 2.6 to /opt and recompile wsgi if not provided +2) Install Python 3.7 to /opt and recompile wsgi if not provided 2) Install web2py in /opt/web-apps/ 3) Configure SELinux and iptables 5) Create a self signed ssl certificate @@ -55,7 +55,7 @@ echo yum update # Install required packages -yum install httpd mod_ssl mod_wsgi wget python +yum install httpd mod_ssl mod_wsgi wget python3 # Verify we have at least Python 2.5 typeset -i version_major diff --git a/scripts/setup-web2py-heroku.sh b/scripts/setup-web2py-heroku.sh old mode 100644 new mode 100755 diff --git a/scripts/setup-web2py-nginx-uwsgi-centos64.sh b/scripts/setup-web2py-nginx-uwsgi-centos64.sh old mode 100644 new mode 100755 diff --git a/scripts/setup-web2py-nginx-uwsgi-centos7.sh b/scripts/setup-web2py-nginx-uwsgi-centos7.sh old mode 100644 new mode 100755 diff --git a/scripts/setup-web2py-nginx-uwsgi-on-centos.sh b/scripts/setup-web2py-nginx-uwsgi-on-centos.sh old mode 100644 new mode 100755 diff --git a/scripts/setup-web2py-nginx-uwsgi-opensuse.sh b/scripts/setup-web2py-nginx-uwsgi-opensuse.sh old mode 100644 new mode 100755 diff --git a/scripts/setup-web2py-nginx-uwsgi-ubuntu.sh b/scripts/setup-web2py-nginx-uwsgi-ubuntu.sh old mode 100644 new mode 100755 diff --git a/scripts/setup-web2py-ubuntu.sh b/scripts/setup-web2py-ubuntu.sh old mode 100644 new mode 100755 index 8de3050a..2864f281 --- a/scripts/setup-web2py-ubuntu.sh +++ b/scripts/setup-web2py-ubuntu.sh @@ -31,18 +31,17 @@ apt-get -y install zip unzip apt-get -y install tar apt-get -y install openssh-server apt-get -y install build-essential -apt-get -y install python -#apt-get -y install python2.5 -apt-get -y install ipython -apt-get -y install python-dev +apt-get -y install python3 +apt-get -y install ipython3 +apt-get -y install python3-dev apt-get -y install postgresql apt-get -y install apache2 apt-get -y install libapache2-mod-wsgi -apt-get -y install python2.5-psycopg2 +apt-get -y install python3-psycopg2 apt-get -y install postfix apt-get -y install wget -apt-get -y install python-matplotlib -apt-get -y install python-reportlab +apt-get -y install python3-matplotlib +apt-get -y install python3-reportlab apt-get -y install mercurial /etc/init.d/postgresql restart diff --git a/scripts/standalone_exe_cxfreeze.py b/scripts/standalone_exe_cxfreeze.py old mode 100644 new mode 100755 diff --git a/scripts/update_languages.py b/scripts/update_languages.py old mode 100644 new mode 100755 index a918379b..a92e851a --- a/scripts/update_languages.py +++ b/scripts/update_languages.py @@ -14,15 +14,14 @@ parentdir = os.path.dirname(currentdir) sys.path.insert(0, parentdir) from gluon.cfs import getcfs -from gluon.utf8 import Utf8 from gluon._compat import copyreg, PY2, maketrans, iterkeys, unicodeT, to_unicode, to_bytes, iteritems, to_native, pjoin -from gluon.languages import findT +from gluon.languages import findT, sort_function # This script can be run with no arguments (which sets the application folder to the current working directory, and default language to English), one argument (which sets the default language), or two arguments (application folder path and default language). # When run, it will update the default language, as well as strip all of the strings found in the non-default languages but not in the default language, and add the strings found in the default language to the non-default languages it is not, making sure translators don't do additional work that will never be used. def read_dict_aux(filename): - lang_text = open(filename, 'r').read().replace(b'\r\n', b'\n') + lang_text = open(filename, 'r').read().replace('\r\n', '\n') try: return safe_eval(to_native(lang_text)) or {} except Exception: @@ -41,14 +40,11 @@ def safe_eval(text): return eval(text, {}, {}) return None -def sort_function(x, y): - return cmp(unicode(x, 'utf-8').lower(), unicode(y, 'utf-8').lower()) - def write_file(file, contents): file.write('# -*- coding: utf-8 -*-\n{\n') - for key in sorted(contents, sort_function): - file.write('%s: %s,\n' % (repr(Utf8(key)), - repr(Utf8(contents[key])))) + for key in sorted(contents, key = sort_function): + file.write('%s: %s,\n' % (repr(to_unicode(key)), + repr(to_unicode(contents[key])))) file.write('}\n') file.close() @@ -67,7 +63,7 @@ def update_languages(cwd, default_lang): if phrase in default: new_dict[phrase] = i18n[phrase] write_file(open(os.path.join(cwd, "languages", lang), 'w'), new_dict) - print lang + print(lang) if __name__ == "__main__": cwd = os.getcwd() diff --git a/scripts/web2py-lock.sh b/scripts/web2py-lock.sh old mode 100644 new mode 100755 diff --git a/scripts/web2py-scheduler.conf b/scripts/web2py-scheduler.conf old mode 100755 new mode 100644 diff --git a/scripts/web2py.fedora.sh b/scripts/web2py.fedora.sh old mode 100644 new mode 100755 index bb3a2534..6a334565 --- a/scripts/web2py.fedora.sh +++ b/scripts/web2py.fedora.sh @@ -29,7 +29,7 @@ cd $DAEMON_DIR start() { echo -n $"Starting $DESC ($NAME): " - daemon --check $NAME $PYTHON $DAEMON_DIR/web2py.py -Q --nogui -a $ADMINPASS -d $PIDFILE -p $PORT & + daemon --check $NAME $PYTHON $DAEMON_DIR/web2py.py -Q --no_gui -a $ADMINPASS -d $PIDFILE -p $PORT & RETVAL=$? if [ $RETVAL -eq 0 ]; then touch /var/lock/subsys/$NAME diff --git a/scripts/zip_static_files.py b/scripts/zip_static_files.py old mode 100644 new mode 100755 diff --git a/web2py.py b/web2py.py index ced8ff22..875642da 100755 --- a/web2py.py +++ b/web2py.py @@ -1,28 +1,49 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +from __future__ import print_function + import os import sys from multiprocessing import freeze_support -# import gluon.import_all ##### This should be uncommented for py2exe.py if hasattr(sys, 'frozen'): - path = os.path.dirname(os.path.abspath(sys.executable)) # for py2exe + # py2exe + path = os.path.dirname(os.path.abspath(sys.executable)) elif '__file__' in globals(): path = os.path.dirname(os.path.abspath(__file__)) -else: # should never happen +else: + # should never happen path = os.getcwd() + +# process -f (--folder) option +if '-f' in sys.argv: + fi = sys.argv.index('-f') +elif '--folder' in sys.argv: + fi = sys.argv.index('--folder') +else: + fi = None +if fi and fi < len(sys.argv): + fi += 1 + folder = sys.argv[fi] + if not os.path.isdir(os.path.join(folder, 'gluon')): + print("%s: error: bad folder %s" % (sys.argv[0], folder), file=sys.stderr) + sys.exit(1) + path = sys.argv[fi] = os.path.abspath(folder) + os.chdir(path) sys.path = [path] + [p for p in sys.path if not p == path] # important that this import is after the os.chdir +# import gluon.import_all # NOTE: should this be uncommented for py2exe.py ? import gluon.widget -# Start Web2py and Web2py cron service! if __name__ == '__main__': freeze_support() + # support for sub-process coverage, + # see https://coverage.readthedocs.io/en/coverage-4.3.4/subprocess.html if 'COVERAGE_PROCESS_START' in os.environ: try: import coverage @@ -30,4 +51,5 @@ if __name__ == '__main__': except: print('Coverage is not available') pass - gluon.widget.start(cron=True) + # start services + gluon.widget.start()