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 74198cea..83b3d7f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,11 +4,24 @@ sudo: required cache: pip +dist: "xenial" + +services: + - mysql + python: - '2.7' - - 'pypy' - '3.5' - '3.6' + - '3.6-dev' + - '3.7' + - '3.7-dev' + - 'pypy3.5' + +matrix: + allow_failures: + - python: '3.6-dev' + - python: '3.7-dev' install: - pip install -e . @@ -16,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 @@ -30,6 +41,3 @@ after_success: notifications: email: true - -addons: - postgresql: "9.4" diff --git a/CHANGELOG b/CHANGELOG index 22406bdd..47b185a4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,68 @@ +## 2.19.0 +- new command line options (Thanks Paolo Pastori) -## 2.15.x -- web2py does not support python 2.6 anymore -- py3.5 syntax compatible (see #1353 for details) -- dropped web shell from admin +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 + +## 2.16.1 +- pydal 17.11 +- bootstrap 4 +- better welcome examples +- many bug fixes + +## 2.15.1-4 +- pydal 17.08 +- dropped support for python 2.6 +- dropped web shell +- experimental python 3 support +- experimental authapi for service login +- allow ajax file uploads +- more tests +- more pep8 compliance +- d3.js model visulization +- improved scheduler +- is_email support for internationalized Domain Names +- improved used of cookies with CookieJar +- SQLFORM.grid(showblobs=True) +- import JS events (added w2p.componentBegin event) +- added support for CASv3 +- allow first_name and last_name placeholders in verify_email message +- added three-quote support in markmin +- updated pg8000 driver (but we still recommend psycopg2) +- compiled views use . separator not _ separator (must recompile code) +- better serbian, french, and catalan translations +- speed improvements (refactor of compileapp and pyc caching) +- removed web shell (never worked as intended) +- allow Expose(..., follow_symlink_out=False). +- Updated fpdf to latest version +- JWT support +- import fabfile for remote deployment - scheduler new feature: you can now specify intervals with cron - gluon/* removed from sys.path. Applications relying on statements like e.g. "from storage import Storage" @@ -10,8 +70,18 @@ "from gluon.storage import Storage" - tests can only be run with the usual web2py.py --run_system_tests OR with python -m unittest -v gluon.tests on the root dir -- updated pymysql driver - +- jQuery 3.2.1 +- PyDAL 17.07 including: + allow jsonb support for postgres + correctly configure adapters that need connection for configuration + better caching + updated IMAP adapter methods to new API + experimental suport for joinable subselects + improved Teradata support + improved mongodb support + overall refactoring + experimental support for Google Cloud SQL v2 + new pymysql driver ## 2.14.6 diff --git a/Makefile b/Makefile index 82f1020a..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 @@ -30,11 +31,7 @@ update: wget -O gluon/contrib/feedparser.py http://feedparser.googlecode.com/svn/trunk/feedparser/feedparser.py wget -O gluon/contrib/simplejsonrpc.py http://rad2py.googlecode.com/hg/ide2py/simplejsonrpc.py echo "remember that pymysql was tweaked" -src: - ### Use semantic versioning - echo 'Version 2.14.6-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION - ### rm -f all junk files - make clean +rmfiles: ### clean up baisc apps rm -f routes.py rm -rf applications/*/sessions/* @@ -46,6 +43,12 @@ src: rm -rf applications/admin/uploads/* rm -rf applications/welcome/uploads/* rm -rf applications/examples/uploads/* +src: + ### Use semantic versioning + echo 'Version 2.18.5-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION + ### rm -f all junk files + make clean + # make rmfiles ### make welcome layout and appadmin the default cp applications/welcome/views/appadmin.html applications/admin/views cp applications/welcome/views/appadmin.html applications/examples/views @@ -54,7 +57,7 @@ src: ### build web2py_src.zip echo '' > NEWINSTALL mv web2py_src.zip web2py_src_old.zip | echo 'no old' - cd ..; zip -r web2py/web2py_src.zip web2py/web2py.py web2py/anyserver.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 @@ -97,6 +100,8 @@ win: cp -r applications/welcome ../web2py_win/web2py/applications cp -r applications/examples ../web2py_win/web2py/applications cp applications/__init__.py ../web2py_win/web2py/applications + # per https://github.com/web2py/web2py/issues/1716 + mv ../web2py_win/web2py/_ssl.pyd ../web2py_win/web2py/_ssl.pyd.legacy | echo 'done' cd ../web2py_win; zip -r web2py_win.zip web2py mv ../web2py_win/web2py_win.zip . run: diff --git a/VERSION b/VERSION index 3760b00f..67432273 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.14.6-stable+timestamp.2016.05.09.19.18.48 +Version 2.18.5-stable+timestamp.2019.04.07.21.13.59 diff --git a/anyserver.py b/anyserver.py index a500922a..261dd422 100644 --- a/anyserver.py +++ b/anyserver.py @@ -43,8 +43,8 @@ class Servers: @staticmethod def cherrypy(app, address, **options): - from cherrypy import wsgiserver - server = wsgiserver.CherryPyWSGIServer(address, app) + from cheroot.wsgi import Server as WSGIServer + server = WSGIServer(address, app) server.start() @staticmethod @@ -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 e72b3dab..005285a5 100644 --- a/applications/admin/controllers/appadmin.py +++ b/applications/admin/controllers/appadmin.py @@ -12,12 +12,6 @@ import gluon.contenttype import gluon.fileutils from gluon._compat import iteritems -# d3_graph_model added but leaving pygraphviz code as is for initial tests. -try: - import pygraphviz as pgv -except ImportError: - pgv = None - is_gae = request.env.web2py_runtime_gae or False # ## critical --- make a copy of the environment @@ -191,9 +185,9 @@ def select(): 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: @@ -230,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 '', + _name='query', _value=request.vars.query or '', _class="form-control", requires=IS_NOT_EMPTY( 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 '')), 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')))), + 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') @@ -280,7 +274,7 @@ def select(): 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'))) + INPUT(_type='submit', _value=T('import'), _class="btn btn-primary")) else: formcsv = None if formcsv and formcsv.process().accepted: @@ -400,10 +394,9 @@ def ccache(): redirect(URL(r=request)) try: - from guppy import hpy - hp = hpy() + from pympler.asizeof import asizeof except ImportError: - hp = False + asizeof = False import shelve import os @@ -457,9 +450,9 @@ def ccache(): ram['ratio'] = 0 for key, value in iteritems(cache.ram.storage): - if hp: - ram['bytes'] += hp.iso(value[1]).size - ram['objects'] += hp.iso(value[1]).count + if asizeof: + ram['bytes'] += asizeof(value[1]) + ram['objects'] += 1 ram['entries'] += 1 if value[0] < ram['oldest']: ram['oldest'] = value[0] @@ -475,9 +468,9 @@ def ccache(): except (KeyError, ZeroDivisionError): disk['ratio'] = 0 else: - if hp: - disk['bytes'] += hp.iso(value[1]).size - disk['objects'] += hp.iso(value[1]).count + if asizeof: + disk['bytes'] += asizeof(value[1]) + disk['objects'] += 1 disk['entries'] += 1 if value[0] < disk['oldest']: disk['oldest'] = value[0] @@ -517,7 +510,7 @@ def ccache(): total['keys'] = key_table(total['keys']) return dict(form=form, total=total, - ram=ram, disk=disk, object_stats=hp != False) + ram=ram, disk=disk, object_stats=asizeof != False) def table_template(table): @@ -566,60 +559,6 @@ def table_template(table): _cellborder=0, _cellspacing=0) ).xml() - -# d3_graph_model added but leaving pygraphviz code as is for initial tests. -# The Graph Model button in admin app views/default/design.html has been redirected -# to the d3_graph_model function. -def bg_graph_model(): - graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR') - - subgraphs = dict() - for tablename in db.tables: - if hasattr(db[tablename],'_meta_graphmodel'): - meta_graphmodel = db[tablename]._meta_graphmodel - else: - meta_graphmodel = dict(group=request.application, color='#ECECEC') - - group = meta_graphmodel['group'].replace(' ', '') - if group not in subgraphs: - subgraphs[group] = dict(meta=meta_graphmodel, tables=[]) - subgraphs[group]['tables'].append(tablename) - - graph.add_node(tablename, name=tablename, shape='plaintext', - label=table_template(tablename)) - - for n, key in enumerate(subgraphs.iterkeys()): - graph.subgraph(nbunch=subgraphs[key]['tables'], - name='cluster%d' % n, - style='filled', - color=subgraphs[key]['meta']['color'], - label=subgraphs[key]['meta']['group']) - - for tablename in db.tables: - for field in db[tablename]: - f_type = field.type - if isinstance(f_type,str) and ( - f_type.startswith('reference') or - f_type.startswith('list:reference')): - referenced_table = f_type.split()[1].split('.')[0] - n1 = graph.get_node(tablename) - n2 = graph.get_node(referenced_table) - graph.add_edge(n1, n2, color="#4C4C4C", label='') - - graph.layout() - if not request.args: - response.headers['Content-Type'] = 'image/png' - return graph.draw(format='png', prog='dot') - else: - response.headers['Content-Disposition']='attachment;filename=graph.%s'%request.args(0) - if request.args(0) == 'dot': - return graph.string() - else: - return graph.draw(format=request.args(0), prog='dot') - -def graph_model(): - return dict(databases=databases, pgv=pgv) - def manage(): tables = manager_action['tables'] if isinstance(tables[0], str): @@ -649,7 +588,7 @@ def manage(): auth.table_permission().group_id.label = T('Role') auth.table_permission().name.label = T('Permission') if table == auth.table_user(): - linked_tables=[auth.settings.table_membership_name] + linked_tables = [auth.settings.table_membership_name] elif table == auth.table_group(): orderby = 'role' if not request.args(3) or '.group_id' not in request.args(3) else None elif table == auth.table_permission(): @@ -665,13 +604,13 @@ def manage(): def hooks(): import functools import inspect - list_op=['_%s_%s' %(h,m) for h in ['before', 'after'] for m in ['insert','update','delete']] - tables=[] - with_build_it=False + list_op = ['_%s_%s' %(h,m) for h in ['before', 'after'] for m in ['insert','update','delete']] + tables = [] + with_build_it = False for db_str in sorted(databases): db = databases[db_str] for t in db.tables: - method_hooks=[] + method_hooks = [] for op in list_op: functions = [] for f in getattr(db[t], op): @@ -691,16 +630,16 @@ def hooks(): except: pass if len(functions): - method_hooks.append({'name':op, 'functions':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') for op in t['method_hooks']: - ul_t.append(LI (op['name'])) + 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_main.append(ul_t) return ul_main @@ -713,43 +652,42 @@ def hooks(): def d3_graph_model(): """ 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" """ - - data = {} + nodes = [] links = [] - subgraphs = dict() + for database in databases: + db = eval_in_global_env(database) + for tablename in db.tables: + fields = [] + for field in db[tablename]: + f_type = field.type + if not isinstance(f_type, str): + disp = ' ' + elif f_type == 'string': + disp = field.length + elif f_type == 'id': + disp = "PK" + elif f_type.startswith('reference') or \ + f_type.startswith('list:reference'): + disp = "FK" + else: + disp = ' ' + fields.append(dict(name=field.name, type=field.type, disp=disp)) - for tablename in db.tables: - fields = [] - for field in db[tablename]: - f_type = field.type - if not isinstance(f_type,str): - disp = ' ' - elif f_type == 'string': - disp = field.length - elif f_type == 'id': - disp = "PK" - elif f_type.startswith('reference') or \ - f_type.startswith('list:reference'): - disp = "FK" - else: - disp = ' ' - fields.append(dict(name= field.name, type=field.type, disp = disp)) + if isinstance(f_type, str) and ( + f_type.startswith('reference') or + f_type.startswith('list:reference')): + referenced_table = f_type.split()[1].split('.')[0] - if isinstance(f_type,str) and ( - f_type.startswith('reference') or - f_type.startswith('list:reference')): - referenced_table = f_type.split()[1].split('.')[0] + links.append(dict(source=tablename, target = referenced_table)) - 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('static','js/d3.min.js')) - response.files.append(URL('static','js/d3_graph.js')) - return dict(nodes=nodes, links=links) \ No newline at end of file + response.files.append(URL('admin','static','js/d3.min.js')) + response.files.append(URL('admin','static','js/d3_graph.js')) + return dict(databases=databases, nodes=nodes, links=links) diff --git a/applications/admin/controllers/debug.py b/applications/admin/controllers/debug.py index 447948d6..8306bebe 100644 --- a/applications/admin/controllers/debug.py +++ b/applications/admin/controllers/debug.py @@ -6,6 +6,7 @@ import gluon.validators import code from gluon.debug import communicate, web_debugger, dbg_debugger from gluon._compat import thread +from gluon.fileutils import open_file import pydoc @@ -54,7 +55,7 @@ def interact(): if filename: # prevent IOError 2 on some circuntances (EAFP instead of os.access) try: - lines = open(filename).readlines() + lines = open_file(filename, 'r').readlines() except: lines = "" lines = dict([(i + 1, l) for (i, l) in enumerate( diff --git a/applications/admin/controllers/default.py b/applications/admin/controllers/default.py index 94fc91ce..bf06d401 100644 --- a/applications/admin/controllers/default.py +++ b/applications/admin/controllers/default.py @@ -16,6 +16,7 @@ from gluon.tools import Config from gluon.compileapp import find_exposed_functions from glob import glob from gluon._compat import iteritems, PY2, pickle, xrange, urlopen, to_bytes, StringIO, to_native +import gluon.rewrite import shutil import platform @@ -165,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]) @@ -249,6 +250,7 @@ def site(): db.app.insert(name=appname, owner=auth.user.id) log_progress(appname) session.flash = T('new application "%s" created', appname) + gluon.rewrite.load() redirect(URL('design', args=appname)) else: session.flash = \ @@ -266,6 +268,7 @@ def site(): new_repo = git.Repo.clone_from(form_update.vars.url, target) session.flash = T('new application "%s" imported', form_update.vars.name) + gluon.rewrite.load() except git.GitCommandError as err: session.flash = T('Invalid git repository specified.') redirect(URL(r=request)) @@ -302,6 +305,7 @@ def site(): log_progress(appname) session.flash = T(msg, dict(appname=appname, digest=md5_hash(installed))) + gluon.rewrite.load() else: msg = 'unable to install application "%(appname)s"' session.flash = T(msg, dict(appname=form_update.vars.name)) @@ -558,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') @@ -638,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): @@ -803,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) @@ -959,8 +973,7 @@ def edit_language(): form = SPAN(strings['__corrupted__'], _class='error') return dict(filename=filename, form=form) - keys = sorted(strings.keys(), lambda x, y: cmp( - unicode(x, 'utf-8').lower(), unicode(y, 'utf-8').lower())) + keys = sorted(strings.keys(), key=lambda x: to_native(x).lower()) rows = [] rows.append(H2(T('Original/Translation'))) @@ -1107,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 @@ -1117,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( @@ -1128,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 @@ -1255,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( @@ -1266,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 @@ -1861,7 +1880,6 @@ def user(): def reload_routes(): """ Reload routes.py """ - import gluon.rewrite gluon.rewrite.load() redirect(URL('site')) diff --git a/applications/admin/controllers/pythonanywhere.py b/applications/admin/controllers/pythonanywhere.py index e61b06ab..d9989cee 100644 --- a/applications/admin/controllers/pythonanywhere.py +++ b/applications/admin/controllers/pythonanywhere.py @@ -5,7 +5,7 @@ import re import gzip import tarfile from gluon.contrib.simplejsonrpc import ServerProxy -from gluon._compat import StringIO, ProtocolError +from gluon._compat import StringIO, ProtocolError, urlencode, urllib2 def deploy(): response.title = T('Deploy to pythonanywhere') @@ -26,9 +26,8 @@ def create_account(): except ProtocolError as error: pass - import urllib, urllib2 url = 'https://www.pythonanywhere.com/api/web2py/create_account' - data = urllib.urlencode(request.vars) + data = urlencode(request.vars) req = urllib2.Request(url, data) try: 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/models/access.py b/applications/admin/models/access.py index 7e24ac58..ded471c4 100644 --- a/applications/admin/models/access.py +++ b/applications/admin/models/access.py @@ -52,7 +52,7 @@ def verify_password(password): if DEMO_MODE: ret = True elif not _config.get('password'): - ret - False + ret = False elif _config['password'].startswith('pam_user:'): session.pam_user = _config['password'][9:].strip() import gluon.contrib.pam diff --git a/applications/welcome/static/css/d3_graph.css b/applications/admin/static/css/d3_graph.css similarity index 100% rename from applications/welcome/static/css/d3_graph.css rename to applications/admin/static/css/d3_graph.css 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/welcome/static/js/d3.min.js b/applications/admin/static/js/d3.min.js similarity index 100% rename from applications/welcome/static/js/d3.min.js rename to applications/admin/static/js/d3.min.js diff --git a/applications/welcome/static/js/d3_graph.js b/applications/admin/static/js/d3_graph.js similarity index 100% rename from applications/welcome/static/js/d3_graph.js rename to applications/admin/static/js/d3_graph.js diff --git a/applications/admin/static/js/jquery.js b/applications/admin/static/js/jquery.js index 5cb0acfa..644d35e2 100644 --- a/applications/admin/static/js/jquery.js +++ b/applications/admin/static/js/jquery.js @@ -1,5 +1,4 @@ -/*! jQuery v1.12.2 | (c) jQuery Foundation | jquery.org/license */ -!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="1.12.2",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(n.isPlainObject(c)||(b=n.isArray(c)))?(b?(b=!1,f=a&&n.isArray(a)?a:[]):f=a&&n.isPlainObject(a)?a:{},g[d]=n.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray||function(a){return"array"===n.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;try{if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(!l.ownFirst)for(b in a)return k.call(a,b);for(b in a);return void 0===b||k.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(b){b&&n.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(h)return h.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(f=a[b],b=a,a=f),n.isFunction(a)?(c=e.call(arguments,2),d=function(){return a.apply(b||this,c.concat(e.call(arguments)))},d.guid=a.guid=a.guid||n.guid++,d):void 0},now:function(){return+new Date},support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return n.inArray(a,b)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;e>b;b++)if(n.contains(d[b],this))return!0}));for(b=0;e>b;b++)n.find(a,d[b],c);return c=this.pushStack(e>1?n.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}if(f=d.getElementById(e[2]),f&&f.parentNode){if(f.id!==e[2])return A.find(a);this.length=1,this[0]=f}return this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b,c=n(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(n.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?n.inArray(this[0],n(a)):n.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return n.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||(e=n.uniqueSort(e)),D.test(a)&&(e=e.reverse())),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=!0,c||j.disable(),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.addEventListener?(d.removeEventListener("DOMContentLoaded",K),a.removeEventListener("load",K)):(d.detachEvent("onreadystatechange",K),a.detachEvent("onload",K))}function K(){(d.addEventListener||"load"===a.event.type||"complete"===d.readyState)&&(J(),n.ready())}n.ready.promise=function(b){if(!I)if(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll)a.setTimeout(n.ready);else if(d.addEventListener)d.addEventListener("DOMContentLoaded",K),a.addEventListener("load",K);else{d.attachEvent("onreadystatechange",K),a.attachEvent("onload",K);var c=!1;try{c=null==a.frameElement&&d.documentElement}catch(e){}c&&c.doScroll&&!function f(){if(!n.isReady){try{c.doScroll("left")}catch(b){return a.setTimeout(f,50)}J(),n.ready()}}()}return I.promise(b)},n.ready.promise();var L;for(L in n(l))break;l.ownFirst="0"===L,l.inlineBlockNeedsLayout=!1,n(function(){var a,b,c,e;c=d.getElementsByTagName("body")[0],c&&c.style&&(b=d.createElement("div"),e=d.createElement("div"),e.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(e).appendChild(b),"undefined"!=typeof b.style.zoom&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",l.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(e))}),function(){var a=d.createElement("div");l.deleteExpando=!0;try{delete a.test}catch(b){l.deleteExpando=!1}a=null}();var M=function(a){var b=n.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b},N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(O,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}n.data(a,b,c)}else c=void 0; -}return c}function Q(a){var b;for(b in a)if(("data"!==b||!n.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function R(a,b,d,e){if(M(a)){var f,g,h=n.expando,i=a.nodeType,j=i?n.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||n.guid++:h),j[k]||(j[k]=i?{}:{toJSON:n.noop}),"object"!=typeof b&&"function"!=typeof b||(e?j[k]=n.extend(j[k],b):j[k].data=n.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[n.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[n.camelCase(b)])):f=g,f}}function S(a,b,c){if(M(a)){var d,e,f=a.nodeType,g=f?n.cache:a,h=f?a[n.expando]:n.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){n.isArray(b)?b=b.concat(n.map(b,n.camelCase)):b in d?b=[b]:(b=n.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!Q(d):!n.isEmptyObject(d))return}(c||(delete g[h].data,Q(g[h])))&&(f?n.cleanData([a],!0):l.deleteExpando||g!=g.window?delete g[h]:g[h]=void 0)}}}n.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?n.cache[a[n.expando]]:a[n.expando],!!a&&!Q(a)},data:function(a,b,c){return R(a,b,c)},removeData:function(a,b){return S(a,b)},_data:function(a,b,c){return R(a,b,c,!0)},_removeData:function(a,b){return S(a,b,!0)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=n.data(f),1===f.nodeType&&!n._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));n._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){n.data(this,a)}):arguments.length>1?this.each(function(){n.data(this,a,b)}):f?P(f,a,n.data(f,a)):void 0},removeData:function(a){return this.each(function(){n.removeData(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=n._data(a,b),c&&(!d||n.isArray(c)?d=n._data(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return n._data(a,c)||n._data(a,c,{empty:n.Callbacks("once memory").add(function(){n._removeData(a,b+"queue"),n._removeData(a,c)})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},Z=/^(?:checkbox|radio)$/i,$=/<([\w:-]+)/,_=/^$|\/(?:java|ecma)script/i,aa=/^\s+/,ba="abbr|article|aside|audio|bdi|canvas|data|datalist|details|dialog|figcaption|figure|footer|header|hgroup|main|mark|meter|nav|output|picture|progress|section|summary|template|time|video";function ca(a){var b=ba.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}!function(){var a=d.createElement("div"),b=d.createDocumentFragment(),c=d.createElement("input");a.innerHTML="
a",l.leadingWhitespace=3===a.firstChild.nodeType,l.tbody=!a.getElementsByTagName("tbody").length,l.htmlSerialize=!!a.getElementsByTagName("link").length,l.html5Clone="<:nav>"!==d.createElement("nav").cloneNode(!0).outerHTML,c.type="checkbox",c.checked=!0,b.appendChild(c),l.appendChecked=c.checked,a.innerHTML="",l.noCloneChecked=!!a.cloneNode(!0).lastChild.defaultValue,b.appendChild(a),c=d.createElement("input"),c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),a.appendChild(c),l.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,l.noCloneEvent=!!a.addEventListener,a[n.expando]=1,l.attributes=!a.getAttribute(n.expando)}();var da={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:l.htmlSerialize?[0,"",""]:[1,"X
","
"]};da.optgroup=da.option,da.tbody=da.tfoot=da.colgroup=da.caption=da.thead,da.th=da.td;function ea(a,b){var c,d,e=0,f="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||n.nodeName(d,b)?f.push(d):n.merge(f,ea(d,b));return void 0===b||b&&n.nodeName(a,b)?n.merge([a],f):f}function fa(a,b){for(var c,d=0;null!=(c=a[d]);d++)n._data(c,"globalEval",!b||n._data(b[d],"globalEval"))}var ga=/<|&#?\w+;/,ha=/r;r++)if(g=a[r],g||0===g)if("object"===n.type(g))n.merge(q,g.nodeType?[g]:g);else if(ga.test(g)){i=i||p.appendChild(b.createElement("div")),j=($.exec(g)||["",""])[1].toLowerCase(),m=da[j]||da._default,i.innerHTML=m[1]+n.htmlPrefilter(g)+m[2],f=m[0];while(f--)i=i.lastChild;if(!l.leadingWhitespace&&aa.test(g)&&q.push(b.createTextNode(aa.exec(g)[0])),!l.tbody){g="table"!==j||ha.test(g)?""!==m[1]||ha.test(g)?0:i:i.firstChild,f=g&&g.childNodes.length;while(f--)n.nodeName(k=g.childNodes[f],"tbody")&&!k.childNodes.length&&g.removeChild(k)}n.merge(q,i.childNodes),i.textContent="";while(i.firstChild)i.removeChild(i.firstChild);i=p.lastChild}else q.push(b.createTextNode(g));i&&p.removeChild(i),l.appendChecked||n.grep(ea(q,"input"),ia),r=0;while(g=q[r++])if(d&&n.inArray(g,d)>-1)e&&e.push(g);else if(h=n.contains(g.ownerDocument,g),i=ea(p.appendChild(g),"script"),h&&fa(i),c){f=0;while(g=i[f++])_.test(g.type||"")&&c.push(g)}return i=null,p}!function(){var b,c,e=d.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(l[b]=c in a)||(e.setAttribute(c,"t"),l[b]=e.attributes[c].expando===!1);e=null}();var ka=/^(?:input|select|textarea)$/i,la=/^key/,ma=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,na=/^(?:focusinfocus|focusoutblur)$/,oa=/^([^.]*)(?:\.(.+)|)/;function pa(){return!0}function qa(){return!1}function ra(){try{return d.activeElement}catch(a){}}function sa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)sa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=qa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=n.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return"undefined"==typeof n||a&&n.event.triggered===a.type?void 0:n.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(G)||[""],h=b.length;while(h--)f=oa.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=n.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=n.event.special[o]||{},l=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},i),(m=g[o])||(m=g[o]=[],m.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,l):m.push(l),n.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n.hasData(a)&&n._data(a);if(r&&(k=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=oa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=m.length;while(f--)g=m[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(m.splice(f,1),g.selector&&m.delegateCount--,l.remove&&l.remove.call(a,g));i&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(k)&&(delete r.handle,n._removeData(a,"events"))}},trigger:function(b,c,e,f){var g,h,i,j,l,m,o,p=[e||d],q=k.call(b,"type")?b.type:b,r=k.call(b,"namespace")?b.namespace.split("."):[];if(i=m=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!na.test(q+n.event.triggered)&&(q.indexOf(".")>-1&&(r=q.split("."),q=r.shift(),r.sort()),h=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=r.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:n.makeArray(c,[b]),l=n.event.special[q]||{},f||!l.trigger||l.trigger.apply(e,c)!==!1)){if(!f&&!l.noBubble&&!n.isWindow(e)){for(j=l.delegateType||q,na.test(j+q)||(i=i.parentNode);i;i=i.parentNode)p.push(i),m=i;m===(e.ownerDocument||d)&&p.push(m.defaultView||m.parentWindow||a)}o=0;while((i=p[o++])&&!b.isPropagationStopped())b.type=o>1?j:l.bindType||q,g=(n._data(i,"events")||{})[b.type]&&n._data(i,"handle"),g&&g.apply(i,c),g=h&&i[h],g&&g.apply&&M(i)&&(b.result=g.apply(i,c),b.result===!1&&b.preventDefault());if(b.type=q,!f&&!b.isDefaultPrevented()&&(!l._default||l._default.apply(p.pop(),c)===!1)&&M(e)&&h&&e[q]&&!n.isWindow(e)){m=e[h],m&&(e[h]=null),n.event.triggered=q;try{e[q]()}catch(s){}n.event.triggered=void 0,m&&(e[h]=m)}return b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(n._data(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]","i"),va=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,wa=/\s*$/g,Aa=ca(d),Ba=Aa.appendChild(d.createElement("div"));function Ca(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function Da(a){return a.type=(null!==n.find.attr(a,"type"))+"/"+a.type,a}function Ea(a){var b=ya.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Fa(a,b){if(1===b.nodeType&&n.hasData(a)){var c,d,e,f=n._data(a),g=n._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)n.event.add(b,c,h[c][d])}g.data&&(g.data=n.extend({},g.data))}}function Ga(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!l.noCloneEvent&&b[n.expando]){e=n._data(b);for(d in e.events)n.removeEvent(b,d,e.handle);b.removeAttribute(n.expando)}"script"===c&&b.text!==a.text?(Da(b).text=a.text,Ea(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),l.html5Clone&&a.innerHTML&&!n.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&Z.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}}function Ha(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&xa.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),Ha(f,b,c,d)});if(o&&(k=ja(b,a[0].ownerDocument,!1,a,d),e=k.firstChild,1===k.childNodes.length&&(k=e),e||d)){for(i=n.map(ea(k,"script"),Da),h=i.length;o>m;m++)g=k,m!==p&&(g=n.clone(g,!0,!0),h&&n.merge(i,ea(g,"script"))),c.call(a[m],g,m);if(h)for(j=i[i.length-1].ownerDocument,n.map(i,Ea),m=0;h>m;m++)g=i[m],_.test(g.type||"")&&!n._data(g,"globalEval")&&n.contains(j,g)&&(g.src?n._evalUrl&&n._evalUrl(g.src):n.globalEval((g.text||g.textContent||g.innerHTML||"").replace(za,"")));k=e=null}return a}function Ia(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(ea(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&fa(ea(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(va,"<$1>")},clone:function(a,b,c){var d,e,f,g,h,i=n.contains(a.ownerDocument,a);if(l.html5Clone||n.isXMLDoc(a)||!ua.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(Ba.innerHTML=a.outerHTML,Ba.removeChild(f=Ba.firstChild)),!(l.noCloneEvent&&l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(d=ea(f),h=ea(a),g=0;null!=(e=h[g]);++g)d[g]&&Ga(e,d[g]);if(b)if(c)for(h=h||ea(a),d=d||ea(f),g=0;null!=(e=h[g]);g++)Fa(e,d[g]);else Fa(a,f);return d=ea(f,"script"),d.length>0&&fa(d,!i&&ea(a,"script")),d=h=e=null,f},cleanData:function(a,b){for(var d,e,f,g,h=0,i=n.expando,j=n.cache,k=l.attributes,m=n.event.special;null!=(d=a[h]);h++)if((b||M(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)m[e]?n.event.remove(d,e):n.removeEvent(d,e,g.handle);j[f]&&(delete j[f],k||"undefined"==typeof d.removeAttribute?d[i]=void 0:d.removeAttribute(i),c.push(f))}}}),n.fn.extend({domManip:Ha,detach:function(a){return Ia(this,a,!0)},remove:function(a){return Ia(this,a)},text:function(a){return Y(this,function(a){return void 0===a?n.text(this):this.empty().append((this[0]&&this[0].ownerDocument||d).createTextNode(a))},null,a,arguments.length)},append:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.appendChild(a)}})},prepend:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&n.cleanData(ea(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&n.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return Y(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(ta,""):void 0;if("string"==typeof a&&!wa.test(a)&&(l.htmlSerialize||!ua.test(a))&&(l.leadingWhitespace||!aa.test(a))&&!da[($.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ea(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ha(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(ea(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=0,e=[],f=n(a),h=f.length-1;h>=d;d++)c=d===h?this:this.clone(!0),n(f[d])[b](c),g.apply(e,c.get());return this.pushStack(e)}});var Ja,Ka={HTML:"block",BODY:"block"};function La(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function Ma(a){var b=d,c=Ka[a];return c||(c=La(a,b),"none"!==c&&c||(Ja=(Ja||n("
') -# -# -# 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): @@ -248,7 +233,6 @@ class TestAuthJWT(unittest.TestCase): self.user_data['password'])[0])) self.jwtauth = AuthJWT(self.auth, secret_key='secret', verify_expiration=True) - def test_jwt_token_manager(self): import gluon.serializers self.request.vars.update(self.user_data) @@ -260,7 +244,6 @@ class TestAuthJWT(unittest.TestCase): self.token = self.jwtauth.jwt_token_manager() self.assertIsNotNone(self.token) - def test_allows_jwt(self): import gluon.serializers self.request.vars.update(self.user_data) @@ -270,11 +253,13 @@ class TestAuthJWT(unittest.TestCase): del self.request.vars['password'] self.token = self.jwtauth.jwt_token_manager() self.request.vars._token = gluon.serializers.json_parser.loads(self.token)['token'] + @self.jwtauth.allows_jwt() def optional_auth(): self.assertEqual(self.user_data['username'], self.auth.user.username) optional_auth() + @unittest.skipIf(IS_IMAP, "TODO: Imap raises 'Connection refused'") # class TestAuth(unittest.TestCase): # @@ -286,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 @@ -449,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 @@ -495,8 +480,6 @@ class TestAuthJWT(unittest.TestCase): # # impersonate_form = auth.impersonate(user_id=omer_id) # # self.assertTrue(auth.is_impersonating()) # # self.assertEqual(impersonate_form, 'test') - - class TestAuth(unittest.TestCase): def myassertRaisesRegex(self, *args, **kwargs): @@ -512,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 @@ -904,7 +887,7 @@ class TestAuth(unittest.TestCase): self.assertEqual(count_log_event_test_after, count_log_event_test_before) def test_add_membership(self): - user = self.db(self.db.auth_user.username == 'bart').select().first() # bypass login_bare() + user = self.db(self.db.auth_user.username == 'bart').select().first() # bypass login_bare() user_id = user.id role_name = 'test_add_membership_group' group_id = self.auth.add_group(role_name) @@ -1174,6 +1157,7 @@ class TestToolsFunctions(unittest.TestCase): pjoin = os.path.join + def have_symlinks(): return os.name == 'posix' @@ -1181,18 +1165,20 @@ def have_symlinks(): class Test_Expose__in_base(unittest.TestCase): def test_in_base(self): - are_under = [ # (sub, base) + are_under = [ + # (sub, base) ('/foo/bar', '/foo'), ('/foo', '/foo'), ('/foo', '/'), ('/', '/'), ] for sub, base in are_under: - self.assertTrue( Expose._Expose__in_base(subdir=sub, basedir=base, sep='/'), - '%s is not under %s' % (sub, base) ) + self.assertTrue(Expose._Expose__in_base(subdir=sub, basedir=base, sep='/'), + '%s is not under %s' % (sub, base)) def test_not_in_base(self): - are_not_under = [ # (sub, base) + are_not_under = [ + # (sub, base) ('/foobar', '/foo'), ('/foo', '/foo/bar'), ('/bar', '/foo'), @@ -1200,8 +1186,8 @@ class Test_Expose__in_base(unittest.TestCase): ('/', '/x'), ] for sub, base in are_not_under: - self.assertFalse( Expose._Expose__in_base(subdir=sub, basedir=base, sep='/'), - '%s should not be under %s' % (sub, base) ) + self.assertFalse(Expose._Expose__in_base(subdir=sub, basedir=base, sep='/'), + '%s should not be under %s' % (sub, base)) class TestExpose(unittest.TestCase): @@ -1237,7 +1223,7 @@ class TestExpose(unittest.TestCase): shutil.rmtree(self.base_dir) def make_dirs(self): - """setup direcotry strucutre""" + """setup directory structure""" for d in (['inside'], ['inside', 'dir1'], ['inside', 'dir2'], @@ -1257,7 +1243,7 @@ class TestExpose(unittest.TestCase): f.write('README content') def make_symlinks(self): - """setup extenstion for posix systems""" + """setup extension for posix systems""" # inside links os.symlink( pjoin(self.base_dir, 'inside', 'dir1'), @@ -1394,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 77a7e6b5..00000000 --- a/gluon/tests/test_validators.py +++ /dev/null @@ -1,1128 +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 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)) - db.person.drop() - db.category.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()(' ') - 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], 'Enter between 10 and 100 values')) - rtn = IS_LIST_OF(IS_INT_IN_RANGE(0, 10), maximum=2)([1, 2, 3]) - self.assertEqual(rtn, ([1, 2, 3], 'Enter between 0 and 2 values')) - # regression test for issue 742 - rtn = IS_LIST_OF(minimum=1)('') - self.assertEqual(rtn, ([], 'Enter between 1 and 100 values')) - - 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')]) - - 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] else: token = current.request.vars.get(token_param) + if token is None: + raise HTTP(400, 'JWT header not found and JWT parameter {} missing in request'.format(token_param)) self.recvd_token = token return token @@ -1506,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={}, @@ -1538,7 +1412,8 @@ class Auth(AuthAPI): # ## these are messages that can be customized default_messages = dict(AuthAPI.default_messages, access_denied='Insufficient privileges', - bulk_invite_body='You have been invited to join %(site)s, click %(link)s to complete the process', + bulk_invite_body='You have been invited to join %(site)s, click %(link)s to complete ' + 'the process', bulk_invite_subject='Invitation to join %(site)s', delete_label='Check to delete', email_sent='Email sent', @@ -1763,7 +1638,7 @@ class Auth(AuthAPI): if auth.last_visit and auth.last_visit + delta > now: self.user = auth.user # this is a trick to speed up sessions to avoid many writes - if (now - auth.last_visit).seconds > (auth.expiration / 10): + if (now - auth.last_visit).seconds > (auth.expiration // 10): auth.last_visit = now else: self.user = None @@ -1793,6 +1668,7 @@ class Auth(AuthAPI): servicevalidate='serviceValidate', proxyvalidate='proxyValidate', logout='logout'), + cas_create_user=True, extra_fields={}, actions_disabled=[], controller=controller, @@ -1841,14 +1717,15 @@ class Auth(AuthAPI): # ## these are messages that can be customized messages = self.messages = Messages(current.T) messages.update(Auth.default_messages) - messages.update(ajax_failed_authentication=DIV(H4('NOT AUTHORIZED'), - 'Please ', - A('login', - _href=self.settings.login_url + - ('?_next=' + urllib_quote(current.request.env.http_web2py_component_location)) - if current.request.env.http_web2py_component_location else ''), - ' to view this content.', - _class='not-authorized alert alert-block')) + messages.update(ajax_failed_authentication= + DIV(H4('NOT AUTHORIZED'), + 'Please ', + A('login', + _href=self.settings.login_url + + ('?_next=' + urllib_quote(current.request.env.http_web2py_component_location)) + if current.request.env.http_web2py_component_location else ''), + ' to view this content.', + _class='not-authorized alert alert-block')) messages.lock_keys = True # for "remember me" option @@ -1877,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 @@ -2009,8 +1886,7 @@ class Auth(AuthAPI): items.append({'name': T('Lost password?'), 'href': href('request_reset_password'), 'icon': 'icon-lock'}) - if (self.settings.use_username and not - 'retrieve_username' in self.settings.actions_disabled): + if self.settings.use_username and 'retrieve_username' not in self.settings.actions_disabled: items.append({'name': T('Forgot username?'), 'href': href('retrieve_username'), 'icon': 'icon-edit'}) @@ -2182,9 +2058,7 @@ class Auth(AuthAPI): current_record.replace('_', ' ').title()) for table in tables: fieldnames = table.fields() - if ('id' in fieldnames and - 'modified_on' in fieldnames and - not current_record in fieldnames): + if 'id' in fieldnames and 'modified_on' in fieldnames and current_record not in fieldnames: table._enable_record_versioning(archive_db=archive_db, archive_name=archive_names, current_record=current_record, @@ -2214,7 +2088,8 @@ class Auth(AuthAPI): fake_migrate = db._fake_migrate settings = self.settings settings.enable_tokens = enable_tokens - super(Auth, self).define_tables(username, signature, migrate, fake_migrate) + signature_list = \ + super(Auth, self).define_tables(username, signature, migrate, fake_migrate)._table_signature_list now = current.request.now reference_table_user = 'reference %s' % settings.table_user_name @@ -2286,10 +2161,14 @@ class Auth(AuthAPI): If the user doesn't yet exist, then they are created. """ table_user = self.table_user() + create_user = self.settings.cas_create_user user = None checks = [] # make a guess about who this user is - for fieldname in ['registration_id', 'username', 'email']: + guess_fields = ['registration_id', 'username', 'email'] + if self.settings.login_userfield: + guess_fields.append(self.settings.login_userfield) + for fieldname in guess_fields: if fieldname in table_user.fields() and \ keys.get(fieldname, None): checks.append(fieldname) @@ -2318,6 +2197,11 @@ class Auth(AuthAPI): update_keys[key] = keys[key] user.update_record(**update_keys) elif checks: + if create_user is False: + # Remove current open session a send message + self.logout(next=None, onlogout=None, log=None) + raise HTTP(403, "Forbidden. User need to be created first.") + if 'first_name' not in keys and 'first_name' in table_user.fields: guess = keys.get('email', 'anonymous').split('@')[0] keys['first_name'] = keys.get('username', guess) @@ -2360,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) + 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 @@ -2377,8 +2261,9 @@ class Auth(AuthAPI): def _get_login_settings(self): table_user = self.table_user() - userfield = self.settings.login_userfield or 'username' \ - if 'username' in table_user.fields else 'email' + userfield = self.settings.login_userfield or ('username' \ + if self.settings.login_userfield or 'username' \ + in table_user.fields else 'email') passfield = self.settings.password_field return Storage({'table_user': table_user, 'userfield': userfield, @@ -2463,7 +2348,7 @@ class Auth(AuthAPI): _href=service + query_sep + "ticket=" + ticket) else: redirect(service + query_sep + "ticket=" + ticket) - if self.is_logged_in() and not 'renew' in request.vars: + if self.is_logged_in() and 'renew' not in request.vars: return allow_access() elif not self.is_logged_in() and 'gateway' in request.vars: redirect(session._cas_service) @@ -2493,9 +2378,9 @@ class Auth(AuthAPI): success = True def build_response(body): - return '\n' +\ - TAG['cas:serviceResponse']( - body, **{'_xmlns:cas': 'http://www.yale.edu/tp/cas'}).xml() + xml_body = to_native(TAG['cas:serviceResponse']( + body, **{'_xmlns:cas': 'http://www.yale.edu/tp/cas'}).xml()) + return '\n' + xml_body if success: if version == 1: message = 'yes\n%s' % user[userfield] @@ -2684,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, @@ -2699,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 @@ -2748,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 @@ -2780,7 +2668,7 @@ class Auth(AuthAPI): # If auth.settings.auth_two_factor_enabled it will enable two factor # for all the app. Another way to anble two factor is that the user # must be part of a group that is called auth.settings.two_factor_authentication_group - if user and self.settings.auth_two_factor_enabled == True: + if user and self.settings.auth_two_factor_enabled is True: session.auth_two_factor_enabled = True elif user and self.settings.two_factor_authentication_group: role = self.settings.two_factor_authentication_group @@ -2810,7 +2698,7 @@ class Auth(AuthAPI): # Set the way we generate the code or we send the code. For example using SMS... two_factor_methods = self.settings.two_factor_methods - if two_factor_methods == []: + if not two_factor_methods: # TODO: Add some error checking to handle cases where email cannot be sent self.settings.mailer.send( to=user.email, @@ -2833,47 +2721,49 @@ class Auth(AuthAPI): hideerror=settings.hideerror): accepted_form = True - ''' + """ The lists is executed after form validation for each of the corresponding action. For example, in your model: In your models copy and paste: - #Before define tables, we add some extra field to auth_user + # Before define tables, we add some extra field to auth_user auth.settings.extra_fields['auth_user'] = [ Field('motp_secret', 'password', length=512, default='', label='MOTP Secret'), Field('motp_pin', 'string', length=128, default='', label='MOTP PIN')] - OFFSET = 60 #Be sure is the same in your OTP Client + OFFSET = 60 # Be sure is the same in your OTP Client - #Set session.auth_two_factor to None. Because the code is generated by external app. + # Set session.auth_two_factor to None. Because the code is generated by external app. # This will avoid to use the default setting and send a code by email. def _set_two_factor(user, auth_two_factor): return None def verify_otp(user, otp): - import time - from hashlib import md5 - epoch_time = int(time.time()) - time_start = int(str(epoch_time - OFFSET)[:-1]) - time_end = int(str(epoch_time + OFFSET)[:-1]) - for t in range(time_start - 1, time_end + 1): - to_hash = str(t) + user.motp_secret + user.motp_pin - hash = md5(to_hash).hexdigest()[:6] - if otp == hash: - return hash + import time + from hashlib import md5 + epoch_time = int(time.time()) + time_start = int(str(epoch_time - OFFSET)[:-1]) + time_end = int(str(epoch_time + OFFSET)[:-1]) + for t in range(time_start - 1, time_end + 1): + to_hash = str(t) + user.motp_secret + user.motp_pin + hash = md5(to_hash).hexdigest()[:6] + if otp == hash: + return hash auth.settings.auth_two_factor_enabled = True auth.messages.two_factor_comment = "Verify your OTP Client for the code." - auth.settings.two_factor_methods = [lambda user, auth_two_factor: _set_two_factor(user, auth_two_factor)] + auth.settings.two_factor_methods = [lambda user, + auth_two_factor: _set_two_factor(user, auth_two_factor)] auth.settings.two_factor_onvalidation = [lambda user, otp: verify_otp(user, otp)] - ''' - if self.settings.two_factor_onvalidation != []: + """ + if self.settings.two_factor_onvalidation: for two_factor_onvalidation in self.settings.two_factor_onvalidation: try: - session.auth_two_factor = two_factor_onvalidation(session.auth_two_factor_user, form.vars['authentication_code']) + session.auth_two_factor = \ + two_factor_onvalidation(session.auth_two_factor_user, form.vars['authentication_code']) except: pass else: @@ -3044,7 +2934,7 @@ class Auth(AuthAPI): if self.settings.register_verify_password: if self.settings.register_fields is None: - self.settings.register_fields = [f.name for f in table_user if f.writable] + self.settings.register_fields = [f.name for f in table_user if f.writable and not f.compute] k = self.settings.register_fields.index(passfield) self.settings.register_fields.insert(k + 1, "password_two") extra_fields = [ @@ -3562,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), @@ -3570,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)] @@ -3656,6 +3547,16 @@ class Auth(AuthAPI): if not self.is_logged_in(): redirect(self.settings.login_url, client_side=self.settings.client_side) + + # Go to external link to change the password + if self.settings.login_form != self: + cas = self.settings.login_form + # To prevent error if change_password_url function is not defined in alternate login + if hasattr(cas, 'change_password_url'): + next = cas.change_password_url(next) + if next is not None: + redirect(next) + db = self.db table_user = self.table_user() s = db(table_user.id == self.user.id) @@ -3674,9 +3575,10 @@ 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].min_length = 0 + 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 form = SQLFORM.factory( Field('old_password', 'password', requires=requires, label=self.messages.old_password), @@ -3729,6 +3631,7 @@ class Auth(AuthAPI): client_side=self.settings.client_side) passfield = self.settings.password_field table_user[passfield].writable = False + table_user['email'].writable = False request = current.request session = current.session if next is DEFAULT: @@ -3739,6 +3642,7 @@ class Auth(AuthAPI): onaccept = self.settings.profile_onaccept if log is DEFAULT: log = self.messages['profile_log'] + form = SQLFORM( table_user, self.user.id, @@ -3757,15 +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) - 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() @@ -4148,7 +4052,7 @@ class Auth(AuthAPI): archive_table = table._db[archive_table_name] new_record = {current_record: form.vars.id} for fieldname in archive_table.fields: - if not fieldname in ['id', current_record]: + if fieldname not in ['id', current_record]: if archive_current and fieldname in form.vars: new_record[fieldname] = form.vars[fieldname] elif form.record and fieldname in form.record: @@ -4220,15 +4124,15 @@ class Auth(AuthAPI): class Crud(object): # pragma: no cover default_messages = dict( - submit_button = 'Submit', - delete_label = 'Check to delete', - record_created = 'Record Created', - record_updated = 'Record Updated', - record_deleted = 'Record Deleted', - update_log = 'Record %(id)s updated', - create_log = 'Record %(id)s created', - read_log = 'Record %(id)s read', - delete_log = 'Record %(id)s deleted', + submit_button='Submit', + delete_label='Check to delete', + record_created='Record Created', + record_updated='Record Updated', + record_deleted='Record Deleted', + update_log='Record %(id)s updated', + create_log='Record %(id)s created', + read_log='Record %(id)s read', + delete_log='Record %(id)s deleted', ) def url(self, f=None, args=None, vars=None): @@ -4712,7 +4616,6 @@ class Crud(object): # pragma: no cover results = None return form, results - urllib2.install_opener(urllib2.build_opener(urllib2.HTTPCookieProcessor())) @@ -4721,7 +4624,7 @@ def fetch(url, data=None, headers=None, user_agent='Mozilla/5.0'): headers = headers or {} if data is not None: - data = urllib.urlencode(data) + data = urlencode(data) if user_agent: headers['User-agent'] = user_agent headers['Cookie'] = ' '.join( @@ -4730,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: @@ -4809,7 +4712,7 @@ class Service(object): def __init__(self, environment=None, check_args=False): self.check_args = check_args - + self.run_procedures = {} self.csv_procedures = {} self.xml_procedures = {} @@ -4958,7 +4861,10 @@ class Service(object): Then call it with: - wget --post-data '{"jsonrpc": "2.0", "id": 1, "method": "myfunction", "params": {"a": 1, "b": 2}}' http://..../app/default/call/jsonrpc2 + wget --post-data '{"jsonrpc": "2.0", + "id": 1, + "method": "myfunction", + "params": {"a": 1, "b": 2}}' http://..../app/default/call/jsonrpc2 """ self.jsonrpc2_procedures[f.__name__] = f @@ -5033,7 +4939,7 @@ class Service(object): return f return _amfrpc3 - def soap(self, name=None, returns=None, args=None, doc=None): + def soap(self, name=None, returns=None, args=None, doc=None, response_element_name=None): """ Example: Use as:: @@ -5057,7 +4963,7 @@ class Service(object): """ def _soap(f): - self.soap_procedures[name or f.__name__] = f, returns, args, doc + self.soap_procedures[name or f.__name__] = f, returns, args, doc, response_element_name return f return _soap @@ -5067,7 +4973,7 @@ class Service(object): args = request.args if args and args[0] in self.run_procedures: return str(self.call_service_function(self.run_procedures[args[0]], - *args[1:], **dict(request.vars))) + *args[1:], **dict(request.vars))) self.error() def serve_csv(self, args=None): @@ -5088,14 +4994,14 @@ class Service(object): if args and args[0] in self.csv_procedures: import types r = self.call_service_function(self.csv_procedures[args[0]], - *args[1:], **dict(request.vars)) + *args[1:], **dict(request.vars)) s = StringIO() if hasattr(r, 'export_to_csv_file'): r.export_to_csv_file(s) 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()]) @@ -5115,7 +5021,7 @@ class Service(object): args = request.args if args and args[0] in self.xml_procedures: s = self.call_service_function(self.xml_procedures[args[0]], - *args[1:], **dict(request.vars)) + *args[1:], **dict(request.vars)) if hasattr(s, 'as_list'): s = s.as_list() return serializers.xml(s, quote=False) @@ -5128,7 +5034,7 @@ class Service(object): args = request.args if args and args[0] in self.rss_procedures: feed = self.call_service_function(self.rss_procedures[args[0]], - *args[1:], **dict(request.vars)) + *args[1:], **dict(request.vars)) else: self.error() response.headers['Content-Type'] = 'application/rss+xml' @@ -5312,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): @@ -5363,8 +5269,8 @@ class Service(object): prefix='pys', documentation=documentation, ns=True) - for method, (function, returns, args, doc) in iteritems(procedures): - dispatcher.register_function(method, function, returns, args, doc) + for method, (function, returns, args, doc, resp_elem_name) in iteritems(procedures): + dispatcher.register_function(method, function, returns, args, doc, resp_elem_name) if request.env.request_method == 'POST': fault = {} # Process normal Soap Operation @@ -5476,7 +5382,7 @@ class Service(object): def error(self): raise HTTP(404, "Object does not exist") - + # we make this a method so that subclasses can override it if they want to do more specific argument-checking # but the default implmentation is the simplest: just pass the arguments we got, with no checking def call_service_function(self, f, *a, **b): @@ -5484,7 +5390,8 @@ class Service(object): return universal_caller(f, *a, **b) else: return f(*a, **b) - + + def completion(callback): """ Executes a task on completion of the called action. @@ -5528,15 +5435,15 @@ def prettydate(d, T=lambda x: x, utc=False): else: suffix = ' ago' if dt.days >= 2 * 365: - return T('%d years' + suffix) % int(dt.days / 365) + return T('%d years' + suffix) % int(dt.days // 365) elif dt.days >= 365: return T('1 year' + suffix) elif dt.days >= 60: - return T('%d months' + suffix) % int(dt.days / 30) + return T('%d months' + suffix) % int(dt.days // 30) elif dt.days >= 27: # 4 weeks ugly return T('1 month' + suffix) elif dt.days >= 14: - return T('%d weeks' + suffix) % int(dt.days / 7) + return T('%d weeks' + suffix) % int(dt.days // 7) elif dt.days >= 7: return T('1 week' + suffix) elif dt.days > 1: @@ -5544,11 +5451,11 @@ def prettydate(d, T=lambda x: x, utc=False): elif dt.days == 1: return T('1 day' + suffix) elif dt.seconds >= 2 * 60 * 60: - return T('%d hours' + suffix) % int(dt.seconds / 3600) + return T('%d hours' + suffix) % int(dt.seconds // 3600) elif dt.seconds >= 60 * 60: return T('1 hour' + suffix) elif dt.seconds >= 2 * 60: - return T('%d minutes' + suffix) % int(dt.seconds / 60) + return T('%d minutes' + suffix) % int(dt.seconds // 60) elif dt.seconds >= 60: return T('1 minute' + suffix) elif dt.seconds > 1: @@ -5601,12 +5508,12 @@ class PluginManager(object): where the plugin is used:: - >>> print plugins.me.param1 + >>> print(plugins.me.param1) 3 - >>> print plugins.me.param2 + >>> print(plugins.me.param2) 6 >>> plugins.me.param3 = 8 - >>> print plugins.me.param3 + >>> print(plugins.me.param3) 8 Here are some tests:: @@ -5614,25 +5521,25 @@ class PluginManager(object): >>> a=PluginManager() >>> a.x=6 >>> b=PluginManager('check') - >>> print b.x + >>> print(b.x) 6 >>> b=PluginManager() # reset settings - >>> print b.x + >>> print(b.x) >>> b.x=7 - >>> print a.x + >>> print(a.x) 7 >>> a.y.z=8 - >>> print b.y.z + >>> print(b.y.z) 8 >>> test_thread_separation() 5 >>> plugins=PluginManager('me',db='mydb') - >>> print plugins.me.db + >>> print(plugins.me.db) mydb - >>> print 'me' in plugins + >>> print('me' in plugins) True - >>> print plugins.me.installed + >>> print(plugins.me.installed) True """ @@ -5666,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__ @@ -5781,11 +5688,14 @@ class Expose(object): return os.path.realpath(f) def issymlink_out(self, f): - "True if f is a symlink and is pointing outside of self.base" + """True if f is a symlink and is pointing outside of self.base""" return os.path.islink(f) and not self.in_base(f) @staticmethod def isprivate(f): + # remove '/private' prefix to deal with symbolic links on OSX + if f.startswith('/private/'): + f = f[8:] return 'private' in f or f.startswith('.') or f.endswith('~') @staticmethod @@ -5909,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 @@ -6005,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') @@ -6422,7 +6332,7 @@ class Wiki(object): query = (db.wiki_page.id == db.wiki_tag.wiki_page) &\ (db.wiki_tag.name.belongs(tags)) query = query | db.wiki_page.title.contains(request.vars.q) - if self.settings.restrict_search and not self.manage(): + if self.settings.restrict_search and not self.can_manage(): query = query & (db.wiki_page.created_by == self.auth.user_id) pages = db(query).select(count, *fields, **dict(orderby=orderby or ~count, diff --git a/gluon/utf8.py b/gluon/utf8.py index 21fd12c4..34bfa87f 100644 --- a/gluon/utf8.py +++ b/gluon/utf8.py @@ -11,7 +11,7 @@ Utilities and class for UTF8 strings managing ---------------------------------------------- """ from __future__ import print_function -from gluon._compat import builtin as __builtin__, unicodeT, iteritems, to_unicode, to_native +from gluon._compat import builtin as __builtin__, unicodeT, iteritems, to_unicode, to_native, reload __all__ = ['Utf8'] diff --git a/gluon/utils.py b/gluon/utils.py index 85d7d49f..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,8 @@ 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: return secure_loads_deprecated(data, encryption_key, hash_key, compression_level) @@ -229,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) @@ -252,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() @@ -273,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 @@ -364,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): @@ -438,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 7ace3c5c..c51f9014 100644 --- a/gluon/validators.py +++ b/gluon/validators.py @@ -1,3798 +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, long, basestring, unicodeT, to_unicode, urllib_unquote, unichr, to_bytes, PY2, to_unicode, to_native -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', SQLField('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, 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 not self.zero is 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, (int, long)) 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, (int, long)) or 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 (values, 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 [int, long]: - 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 == []: - 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'] - - -# This regex comes from RFC 2396, Appendix B. It's used to split a URL into -# its component parts -# Here are the regex groups that it extracts: -# scheme = group(2) -# authority = group(4) -# path = group(5) -# query = group(7) -# fragment = group(9) - -url_split_regex = \ - re.compile('^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?') - -# 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 = [] - try: - 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('') - except: - asciiLabels = [str(label) for label in labels] - # 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 - - groups = url_split_regex.match(url).groups() - # If no authority was found - if not groups[3]: - # Try appending a scheme to see if that fixes the problem - scheme_to_prepend = prepend_scheme or 'http' - groups = url_split_regex.match( - to_unicode(scheme_to_prepend) + u'://' + url).groups() - # if we still can't find the authority - if not groups[3]: - 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 = groups[1] - authority = groups[3] - path = groups[4] or '' - query = groups[5] or '' - fragment = groups[7] or '' - - if prepend_scheme: - scheme = str(scheme) + '://' - else: - scheme = '' - - return scheme + unicode_to_ascii_authority(authority) +\ - escape_unicode(path) + escape_unicode(query) + str(fragment) - - -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;/?:@&=+$,\-_\.!~*'\(\)%#]+$") - - 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 does not misuse the '%' character - if not self.GENERIC_URL.search(value): - # if the URL is only composed of valid characters - if self.GENERIC_URL_VALID.match(value): - # Then split up the URL into its components and check on - # the scheme - scheme = url_split_regex.match(value).group(2) - # Clean up the scheme before we check it - if not scheme is None: - scheme = urllib_unquote(scheme).lower() - # If the scheme really exists - if scheme in self.allowed_schemes: - # Then the URL is valid - return (value, None) - else: - # else, 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 value.find('://') < 0 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 URL is not valid - return (value, translate(self.error_message)) - -# Sources (obtained 2015-Feb-24): -# 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 - 'abogado', 'ac', 'academy', 'accountants', 'active', 'actor', - 'ad', 'adult', 'ae', 'aero', 'af', 'ag', 'agency', 'ai', - 'airforce', 'al', 'allfinanz', 'alsace', 'am', 'amsterdam', 'an', - 'android', 'ao', 'apartments', 'aq', 'aquarelle', 'ar', 'archi', - 'army', 'arpa', 'as', 'asia', 'associates', 'at', 'attorney', - 'au', 'auction', 'audio', 'autos', 'aw', 'ax', 'axa', 'az', - # b - 'ba', 'band', 'bank', 'bar', 'barclaycard', 'barclays', - 'bargains', 'bayern', 'bb', 'bd', 'be', 'beer', 'berlin', 'best', - 'bf', 'bg', 'bh', 'bi', 'bid', 'bike', 'bingo', 'bio', 'biz', - 'bj', 'black', 'blackfriday', 'bloomberg', 'blue', 'bm', 'bmw', - 'bn', 'bnpparibas', 'bo', 'boo', 'boutique', 'br', 'brussels', - 'bs', 'bt', 'budapest', 'build', 'builders', 'business', 'buzz', - 'bv', 'bw', 'by', 'bz', 'bzh', - # c - 'ca', 'cab', 'cal', 'camera', 'camp', 'cancerresearch', 'canon', - 'capetown', 'capital', 'caravan', 'cards', 'care', 'career', - 'careers', 'cartier', 'casa', 'cash', 'casino', 'cat', - 'catering', 'cbn', 'cc', 'cd', 'center', 'ceo', 'cern', 'cf', - 'cg', 'ch', 'channel', 'chat', 'cheap', 'christmas', 'chrome', - 'church', 'ci', 'citic', 'city', 'ck', 'cl', 'claims', - 'cleaning', 'click', 'clinic', 'clothing', 'club', 'cm', 'cn', - 'co', 'coach', 'codes', 'coffee', 'college', 'cologne', 'com', - 'community', 'company', 'computer', 'condos', 'construction', - 'consulting', 'contractors', 'cooking', 'cool', 'coop', - 'country', 'cr', 'credit', 'creditcard', 'cricket', 'crs', - 'cruises', 'cu', 'cuisinella', 'cv', 'cw', 'cx', 'cy', 'cymru', - 'cz', - # d - 'dabur', 'dad', 'dance', 'dating', 'day', 'dclk', 'de', 'deals', - 'degree', 'delivery', 'democrat', 'dental', 'dentist', 'desi', - 'design', 'dev', 'diamonds', 'diet', 'digital', 'direct', - 'directory', 'discount', 'dj', 'dk', 'dm', 'dnp', 'do', 'docs', - 'domains', 'doosan', 'durban', 'dvag', 'dz', - # e - 'eat', 'ec', 'edu', 'education', 'ee', 'eg', 'email', 'emerck', - 'energy', 'engineer', 'engineering', 'enterprises', 'equipment', - 'er', 'es', 'esq', 'estate', 'et', 'eu', 'eurovision', 'eus', - 'events', 'everbank', 'exchange', 'expert', 'exposed', - # f - 'fail', 'fans', 'farm', 'fashion', 'feedback', 'fi', 'finance', - 'financial', 'firmdale', 'fish', 'fishing', 'fit', 'fitness', - 'fj', 'fk', 'flights', 'florist', 'flowers', 'flsmidth', 'fly', - 'fm', 'fo', 'foo', 'football', 'forsale', 'foundation', 'fr', - 'frl', 'frogans', 'fund', 'furniture', 'futbol', - # g - 'ga', 'gal', 'gallery', 'garden', 'gb', 'gbiz', 'gd', 'gdn', - 'ge', 'gent', 'gf', 'gg', 'ggee', 'gh', 'gi', 'gift', 'gifts', - 'gives', 'gl', 'glass', 'gle', 'global', 'globo', 'gm', 'gmail', - 'gmo', 'gmx', 'gn', 'goldpoint', 'goog', 'google', 'gop', 'gov', - 'gp', 'gq', 'gr', 'graphics', 'gratis', 'green', 'gripe', 'gs', - 'gt', 'gu', 'guide', 'guitars', 'guru', 'gw', 'gy', - # h - 'hamburg', 'hangout', 'haus', 'healthcare', 'help', 'here', - 'hermes', 'hiphop', 'hiv', 'hk', 'hm', 'hn', 'holdings', - 'holiday', 'homes', 'horse', 'host', 'hosting', 'house', 'how', - 'hr', 'ht', 'hu', - # i - 'ibm', 'id', 'ie', 'ifm', 'il', 'im', 'immo', 'immobilien', 'in', - 'industries', 'info', 'ing', 'ink', 'institute', 'insure', 'int', - 'international', 'investments', 'io', 'iq', 'ir', 'irish', 'is', - 'it', 'iwc', - # j - 'jcb', 'je', 'jetzt', 'jm', 'jo', 'jobs', 'joburg', 'jp', - 'juegos', - # k - 'kaufen', 'kddi', 'ke', 'kg', 'kh', 'ki', 'kim', 'kitchen', - 'kiwi', 'km', 'kn', 'koeln', 'kp', 'kr', 'krd', 'kred', 'kw', - 'ky', 'kyoto', 'kz', - # l - 'la', 'lacaixa', 'land', 'lat', 'latrobe', 'lawyer', 'lb', 'lc', - 'lds', 'lease', 'legal', 'lgbt', 'li', 'lidl', 'life', - 'lighting', 'limited', 'limo', 'link', 'lk', 'loans', - 'localhost', 'london', 'lotte', 'lotto', 'lr', 'ls', 'lt', - 'ltda', 'lu', 'luxe', 'luxury', 'lv', 'ly', - # m - 'ma', 'madrid', 'maison', 'management', 'mango', 'market', - 'marketing', 'marriott', 'mc', 'md', 'me', 'media', 'meet', - 'melbourne', 'meme', 'memorial', 'menu', 'mg', 'mh', 'miami', - 'mil', 'mini', 'mk', 'ml', 'mm', 'mn', 'mo', 'mobi', 'moda', - 'moe', 'monash', 'money', 'mormon', 'mortgage', 'moscow', - 'motorcycles', 'mov', 'mp', 'mq', 'mr', 'ms', 'mt', 'mu', - 'museum', 'mv', 'mw', 'mx', 'my', 'mz', - # n - 'na', 'nagoya', 'name', 'navy', 'nc', 'ne', 'net', 'network', - 'neustar', 'new', 'nexus', 'nf', 'ng', 'ngo', 'nhk', 'ni', - 'nico', 'ninja', 'nl', 'no', 'np', 'nr', 'nra', 'nrw', 'ntt', - 'nu', 'nyc', 'nz', - # o - 'okinawa', 'om', 'one', 'ong', 'onl', 'ooo', 'org', 'organic', - 'osaka', 'otsuka', 'ovh', - # p - 'pa', 'paris', 'partners', 'parts', 'party', 'pe', 'pf', 'pg', - 'ph', 'pharmacy', 'photo', 'photography', 'photos', 'physio', - 'pics', 'pictures', 'pink', 'pizza', 'pk', 'pl', 'place', - 'plumbing', 'pm', 'pn', 'pohl', 'poker', 'porn', 'post', 'pr', - 'praxi', 'press', 'pro', 'prod', 'productions', 'prof', - 'properties', 'property', 'ps', 'pt', 'pub', 'pw', 'py', - # q - 'qa', 'qpon', 'quebec', - # r - 're', 'realtor', 'recipes', 'red', 'rehab', 'reise', 'reisen', - 'reit', 'ren', 'rentals', 'repair', 'report', 'republican', - 'rest', 'restaurant', 'reviews', 'rich', 'rio', 'rip', 'ro', - 'rocks', 'rodeo', 'rs', 'rsvp', 'ru', 'ruhr', 'rw', 'ryukyu', - # s - 'sa', 'saarland', 'sale', 'samsung', 'sarl', 'saxo', 'sb', 'sc', - 'sca', 'scb', 'schmidt', 'school', 'schule', 'schwarz', - 'science', 'scot', 'sd', 'se', 'services', 'sew', 'sexy', 'sg', - 'sh', 'shiksha', 'shoes', 'shriram', 'si', 'singles', 'sj', 'sk', - 'sky', 'sl', 'sm', 'sn', 'so', 'social', 'software', 'sohu', - 'solar', 'solutions', 'soy', 'space', 'spiegel', 'sr', 'st', - 'style', 'su', 'supplies', 'supply', 'support', 'surf', - 'surgery', 'suzuki', 'sv', 'sx', 'sy', 'sydney', 'systems', 'sz', - # t - 'taipei', 'tatar', 'tattoo', 'tax', 'tc', 'td', 'technology', - 'tel', 'temasek', 'tennis', 'tf', 'tg', 'th', 'tienda', 'tips', - 'tires', 'tirol', 'tj', 'tk', 'tl', 'tm', 'tn', 'to', 'today', - 'tokyo', 'tools', 'top', 'toshiba', 'town', 'toys', 'tp', 'tr', - 'trade', 'training', 'travel', 'trust', 'tt', 'tui', 'tv', 'tw', - 'tz', - # u - 'ua', 'ug', 'uk', 'university', 'uno', 'uol', 'us', 'uy', 'uz', - # v - 'va', 'vacations', 'vc', 've', 'vegas', 'ventures', - 'versicherung', 'vet', 'vg', 'vi', 'viajes', 'video', 'villas', - 'vision', 'vlaanderen', 'vn', 'vodka', 'vote', 'voting', 'voto', - 'voyage', 'vu', - # w - 'wales', 'wang', 'watch', 'webcam', 'website', 'wed', 'wedding', - 'wf', 'whoswho', 'wien', 'wiki', 'williamhill', 'wme', 'work', - 'works', 'world', 'ws', 'wtc', 'wtf', - # x - 'xn--1qqw23a', 'xn--3bst00m', 'xn--3ds443g', 'xn--3e0b707e', - 'xn--45brj9c', 'xn--45q11c', 'xn--4gbrim', 'xn--55qw42g', - 'xn--55qx5d', 'xn--6frz82g', 'xn--6qq986b3xl', 'xn--80adxhks', - 'xn--80ao21a', 'xn--80asehdb', 'xn--80aswg', 'xn--90a3ac', - 'xn--90ais', 'xn--b4w605ferd', 'xn--c1avg', 'xn--cg4bki', - 'xn--clchc0ea0b2g2a9gcd', 'xn--czr694b', 'xn--czrs0t', - 'xn--czru2d', 'xn--d1acj3b', 'xn--d1alf', 'xn--fiq228c5hs', - 'xn--fiq64b', 'xn--fiqs8s', 'xn--fiqz9s', 'xn--flw351e', - 'xn--fpcrj9c3d', 'xn--fzc2c9e2c', 'xn--gecrj9c', 'xn--h2brj9c', - 'xn--hxt814e', 'xn--i1b6b1a6a2e', 'xn--io0a7i', 'xn--j1amh', - 'xn--j6w193g', 'xn--kprw13d', 'xn--kpry57d', 'xn--kput3i', - 'xn--l1acc', 'xn--lgbbat1ad8j', 'xn--mgb9awbf', - 'xn--mgba3a4f16a', 'xn--mgbaam7a8h', 'xn--mgbab2bd', - 'xn--mgbayh7gpa', 'xn--mgbbh1a71e', 'xn--mgbc0a9azcg', - 'xn--mgberp4a5d4ar', 'xn--mgbx4cd0ab', 'xn--ngbc5azd', - 'xn--node', 'xn--nqv7f', 'xn--nqv7fs00ema', 'xn--o3cw4h', - 'xn--ogbpf8fl', 'xn--p1acf', 'xn--p1ai', 'xn--pgbs0dh', - 'xn--q9jyb4c', 'xn--qcka1pmc', 'xn--rhqv96g', 'xn--s9brj9c', - 'xn--ses554g', 'xn--unup4y', 'xn--vermgensberater-ctb', - 'xn--vermgensberatung-pwb', 'xn--vhquv', 'xn--wgbh1c', - 'xn--wgbl6a', 'xn--xhq521b', 'xn--xkc2al3hye2a', - 'xn--xkc2dl3a5ee0h', 'xn--yfro4i67o', 'xn--ygbi2ammx', - 'xn--zfr164b', 'xxx', 'xyz', - # y - 'yachts', 'yandex', 'ye', 'yodobashi', 'yoga', 'yokohama', - 'youtube', 'yt', - # z - 'za', 'zip', '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: - componentsMatch = url_split_regex.match(value) - authority = componentsMatch.group(4) - # 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 = componentsMatch.group(5) - # 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 value.find('://') < 0: - 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=0, maximum=100, - error_message=None): - self.other = other - self.minimum = minimum - self.maximum = maximum - self.error_message = error_message or "Enter between %(min)g and %(max)g values" - - 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) % dict(min=self.minimum, max=self.maximum)) - if self.maximum is not None and len(ivalue) > self.maximum: - return (ivalue, translate(self.error_message) % 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): - self.subs = subs - - def __call__(self, value): - for validator in self.subs: - value, error = validator(value) - if error == None: - break - 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 == 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 not (self.is_localhost is None or self.is_localhost == - (number == self.localhost)): - ok = False - if not (self.is_private is None or self.is_private == - (sum([private_number[0] <= number <= private_number[1] - for private_number in self.private]) > 0)): - ok = False - if not (self.is_automatic is None or 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. First attempts to - use the ipaddress library and falls back to contrib/ipaddr.py from Google - (https://code.google.com/p/ipaddr-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_mulicast: 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_link_local = False - self.is_reserved = False - self.is_multicast = False - - if not (self.is_private is None or self.is_private == - ip.is_private): - ok = False - if not (self.is_link_local is None or self.is_link_local == - ip.is_link_local): - ok = False - if not (self.is_reserved is None or self.is_reserved == - ip.is_reserved): - ok = False - if not (self.is_multicast is None or self.is_multicast == - ip.is_multicast): - ok = False - if not (self.is_6to4 is None or self.is_6to4 == - ip.is_6to4): - ok = False - if not (self.is_teredo is None or self.is_teredo == - ip.is_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 ipaddress library if found, falls back to PEP-3144 ipaddr.py from - Google (in contrib). - - 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_mulicast: 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 - 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 - 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 0804fa96..a3922b14 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 @@ -140,25 +122,28 @@ class web2pyDialog(object): else: 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,530 +574,76 @@ 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 (c,f 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 - import urllib + if PY2: + import urllib + else: + 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))) @@ -1102,119 +661,136 @@ 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...') + 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 @@ -1227,6 +803,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()) @@ -1237,44 +814,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) @@ -1285,17 +863,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 @@ -1305,11 +881,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, @@ -1322,8 +898,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/access.wsgi b/scripts/access.wsgi index 86285070..8cc6b40b 100644 --- a/scripts/access.wsgi +++ b/scripts/access.wsgi @@ -35,11 +35,17 @@ # URL_CHECK_ACCESS = 'http://127.0.0.1:8002/%(app)s/default/check_access' +PY2 = sys.version_info[0] == 2 def allow_access(environ,host): + if PY2: + import urllib2 + from urllib import urlencode + else: + from urllib import request as urllib2 + from urllib.parse import urlencode + import os - import urllib - import urllib2 import datetime header = '%s @ %s ' % (datetime.datetime.now(),host) + '='*20 pprint = '\n'.join('%s:%s' % item for item in environ.items()) @@ -56,7 +62,7 @@ def allow_access(environ,host): if key.startswith('HTTP_'): headers[key[5:]] = environ[key] # this passes the cookies through! try: - data = urllib.urlencode({'request_uri':environ['REQUEST_URI']}) + data = urlencode({'request_uri':environ['REQUEST_URI']}) request = urllib2.Request(URL_CHECK_ACCESS % dict(app=app),data,headers) response = urllib2.urlopen(request).read().strip().lower() if response.startswith('true'): return True 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 new file mode 100755 index 00000000..6c53bbd7 --- /dev/null +++ b/scripts/check_lang_progress.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Written by Vinyl Darkscratch, www.queengoob.org + +import sys +import os + +# TODO: add comments + +# This script can be run with no arguments (which sets the language folder to the current working directory, and default language to English), one argument (which sets the default language), or two arguments (language folder path and default language). +# When run, this script will compare the original language's strings to their assigned values and determine if they match, just like web2py's interface. While some phrases may be the exact same between both languages, this should provide a good idea of how far along translations are. + +def check_lang_progress(cwd, default_lang): + for x in os.listdir(cwd): + if x == default_lang or x.startswith("plural-"): continue + + data = eval(open(os.path.join(cwd, x)).read()) + + total = 0 + translated = 0 + + for key in data: + total += 1 + if key.replace('@markmin\x01', '') != data[key]: translated += 1 + + print "Translations for %s (%s): %d/%d Translated (%d Untranslated)" %(data['!langname!'], data['!langcode!'], translated, total, total-translated) + +if __name__ == "__main__": + cwd = os.getcwd() + default_lang = 'en' + + if len(sys.argv) > 2: + cwd = sys.argv[1] + default_lang = sys.argv[2] + elif len(sys.argv) > 1: + default_lang = sys.argv[1] + + check_lang_progress(cwd, default_lang) \ No newline at end of file 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 new file mode 100755 index 00000000..44e51972 --- /dev/null +++ b/scripts/drop-pgsql-tables.sh @@ -0,0 +1,14 @@ +# first, login with psql +psql -U USERNAME -d databasename + +# set output for all queries +\o FILENAME.sql + +# run this query +select 'drop table ' || tablename || ' cascade;' from pg_tables; + +# logout from psql +\q + +# run sql script from commandline +psql -U USERNAME -d databasename -f FILENAME.sql \ No newline at end of file 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/sessions2trash.py b/scripts/sessions2trash.py index f338b453..228cf73f 100755 --- a/scripts/sessions2trash.py +++ b/scripts/sessions2trash.py @@ -26,6 +26,14 @@ Typical usage: def delete_sessions(): single_loop(auth.settings.expiration) +Command lines options specific to session2trash.py: +NOTE: They should be preceeded by web2py command line option "-A" to be passed to script. + +-f, --force : Ignore session expiration. Force expiry based on -x option or auth.settings.expiration. +-o, --once : Delete sessions, then exit. Essential when trigger trash sessions from system CRON JOB +-s, --sleep : Number of seconds to sleep between executions. Default 300. +-v, --verbose : print verbose output, a second -v increases verbosity +-x, --expiration : Expiration value for sessions without expiration (in seconds) """ from __future__ import with_statement 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 index 9d7fcee8..e34959d3 --- a/scripts/setup-web2py-nginx-uwsgi-ubuntu.sh +++ b/scripts/setup-web2py-nginx-uwsgi-ubuntu.sh @@ -222,7 +222,7 @@ echo < 2: + cwd = sys.argv[1] + default_lang = sys.argv[2] + elif len(sys.argv) > 1: + default_lang = sys.argv[1] + + update_languages(cwd, default_lang) 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 index 57054e5b..170ef12a --- a/scripts/zip_static_files.py +++ b/scripts/zip_static_files.py @@ -14,7 +14,7 @@ def zip_static(filelist=[]): extension = os.path.splitext(fi) extension = len(extension) > 1 and extension[1] or None if not extension or extension not in ALLOWED_EXTS: - print 'skipping %s' % os.path.basename(fi) + print('skipping %s' % os.path.basename(fi)) continue fstats = os.stat(fi) atime, mtime = fstats.st_atime, fstats.st_mtime @@ -23,10 +23,10 @@ def zip_static(filelist=[]): zstats = os.stat(gfi) zatime, zmtime = zstats.st_atime, zstats.st_mtime if zatime == atime and zmtime == mtime: - print 'skipping %s, already gzipped to the latest version' % os.path.basename(fi) + print('skipping %s, already gzipped to the latest version' % os.path.basename(fi)) continue - print 'gzipping %s to %s' % ( - os.path.basename(fi), os.path.basename(gfi)) + print('gzipping %s to %s' % ( + os.path.basename(fi), os.path.basename(gfi))) f_in = open(fi, 'rb') f_out = gzip.open(gfi, 'wb') f_out.writelines(f_in) @@ -36,7 +36,7 @@ def zip_static(filelist=[]): saved = fstats.st_size - os.stat(gfi).st_size tsave += saved - print 'saved %s KB' % (int(tsave) / 1000.0) + print('saved %s KB' % (int(tsave) / 1000.0)) if __name__ == '__main__': ALLOWED_EXTS = ['.css', '.js'] diff --git a/setup.py b/setup.py index cdcf118a..1bb1f3b1 100644 --- a/setup.py +++ b/setup.py @@ -63,8 +63,10 @@ def start(): 'gluon/contrib/pymysql', 'gluon/contrib/pyrtf', 'gluon/contrib/pysimplesoap', - 'gluon/contrib/pg8000', 'gluon/contrib/plural_rules', + 'gluon/contrib/minify', + 'gluon/contrib/pyaes', + 'gluon/contrib/pyuca', 'gluon/tests', ], package_data={'gluon': ['env.tar']}, 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()