|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 155 KiB |
|
After Width: | Height: | Size: 149 KiB |
|
After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 24 KiB |
@ -1,311 +0,0 @@
|
||||
"""WebsocketProtocol76 from tornado 3.2.2 for tornado >= 4.0
|
||||
|
||||
The contents of this file are Copyright (c) Tornado
|
||||
Used under the Apache 2.0 license
|
||||
"""
|
||||
|
||||
|
||||
from __future__ import absolute_import, division, print_function, with_statement
|
||||
# Author: Jacob Kristhammar, 2010
|
||||
|
||||
import functools
|
||||
import hashlib
|
||||
import struct
|
||||
import time
|
||||
import tornado.escape
|
||||
import tornado.web
|
||||
|
||||
from tornado.log import gen_log, app_log
|
||||
from tornado.util import bytes_type, unicode_type
|
||||
|
||||
from tornado.websocket import WebSocketHandler, WebSocketProtocol13
|
||||
|
||||
class AllowDraftWebSocketHandler(WebSocketHandler):
|
||||
"""Restore Draft76 support for tornado 4
|
||||
|
||||
Remove when we can run tests without phantomjs + qt4
|
||||
"""
|
||||
|
||||
# get is unmodified except between the BEGIN/END PATCH lines
|
||||
@tornado.web.asynchronous
|
||||
def get(self, *args, **kwargs):
|
||||
self.open_args = args
|
||||
self.open_kwargs = kwargs
|
||||
|
||||
# Upgrade header should be present and should be equal to WebSocket
|
||||
if self.request.headers.get("Upgrade", "").lower() != 'websocket':
|
||||
self.set_status(400)
|
||||
self.finish("Can \"Upgrade\" only to \"WebSocket\".")
|
||||
return
|
||||
|
||||
# Connection header should be upgrade. Some proxy servers/load balancers
|
||||
# might mess with it.
|
||||
headers = self.request.headers
|
||||
connection = map(lambda s: s.strip().lower(), headers.get("Connection", "").split(","))
|
||||
if 'upgrade' not in connection:
|
||||
self.set_status(400)
|
||||
self.finish("\"Connection\" must be \"Upgrade\".")
|
||||
return
|
||||
|
||||
# Handle WebSocket Origin naming convention differences
|
||||
# The difference between version 8 and 13 is that in 8 the
|
||||
# client sends a "Sec-Websocket-Origin" header and in 13 it's
|
||||
# simply "Origin".
|
||||
if "Origin" in self.request.headers:
|
||||
origin = self.request.headers.get("Origin")
|
||||
else:
|
||||
origin = self.request.headers.get("Sec-Websocket-Origin", None)
|
||||
|
||||
|
||||
# If there was an origin header, check to make sure it matches
|
||||
# according to check_origin. When the origin is None, we assume it
|
||||
# did not come from a browser and that it can be passed on.
|
||||
if origin is not None and not self.check_origin(origin):
|
||||
self.set_status(403)
|
||||
self.finish("Cross origin websockets not allowed")
|
||||
return
|
||||
|
||||
self.stream = self.request.connection.detach()
|
||||
self.stream.set_close_callback(self.on_connection_close)
|
||||
|
||||
if self.request.headers.get("Sec-WebSocket-Version") in ("7", "8", "13"):
|
||||
self.ws_connection = WebSocketProtocol13(self)
|
||||
self.ws_connection.accept_connection()
|
||||
#--------------- BEGIN PATCH ----------------
|
||||
elif (self.allow_draft76() and
|
||||
"Sec-WebSocket-Version" not in self.request.headers):
|
||||
self.ws_connection = WebSocketProtocol76(self)
|
||||
self.ws_connection.accept_connection()
|
||||
#--------------- END PATCH ----------------
|
||||
else:
|
||||
if not self.stream.closed():
|
||||
self.stream.write(tornado.escape.utf8(
|
||||
"HTTP/1.1 426 Upgrade Required\r\n"
|
||||
"Sec-WebSocket-Version: 8\r\n\r\n"))
|
||||
self.stream.close()
|
||||
|
||||
# 3.2 methods removed in 4.0:
|
||||
def allow_draft76(self):
|
||||
"""Using this class allows draft76 connections by default"""
|
||||
return True
|
||||
|
||||
def get_websocket_scheme(self):
|
||||
"""Return the url scheme used for this request, either "ws" or "wss".
|
||||
This is normally decided by HTTPServer, but applications
|
||||
may wish to override this if they are using an SSL proxy
|
||||
that does not provide the X-Scheme header as understood
|
||||
by HTTPServer.
|
||||
Note that this is only used by the draft76 protocol.
|
||||
"""
|
||||
return "wss" if self.request.protocol == "https" else "ws"
|
||||
|
||||
|
||||
|
||||
# No modifications from tornado-3.2.2 below this line
|
||||
|
||||
class WebSocketProtocol(object):
|
||||
"""Base class for WebSocket protocol versions.
|
||||
"""
|
||||
def __init__(self, handler):
|
||||
self.handler = handler
|
||||
self.request = handler.request
|
||||
self.stream = handler.stream
|
||||
self.client_terminated = False
|
||||
self.server_terminated = False
|
||||
|
||||
def async_callback(self, callback, *args, **kwargs):
|
||||
"""Wrap callbacks with this if they are used on asynchronous requests.
|
||||
|
||||
Catches exceptions properly and closes this WebSocket if an exception
|
||||
is uncaught.
|
||||
"""
|
||||
if args or kwargs:
|
||||
callback = functools.partial(callback, *args, **kwargs)
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
return callback(*args, **kwargs)
|
||||
except Exception:
|
||||
app_log.error("Uncaught exception in %s",
|
||||
self.request.path, exc_info=True)
|
||||
self._abort()
|
||||
return wrapper
|
||||
|
||||
def on_connection_close(self):
|
||||
self._abort()
|
||||
|
||||
def _abort(self):
|
||||
"""Instantly aborts the WebSocket connection by closing the socket"""
|
||||
self.client_terminated = True
|
||||
self.server_terminated = True
|
||||
self.stream.close() # forcibly tear down the connection
|
||||
self.close() # let the subclass cleanup
|
||||
|
||||
|
||||
class WebSocketProtocol76(WebSocketProtocol):
|
||||
"""Implementation of the WebSockets protocol, version hixie-76.
|
||||
|
||||
This class provides basic functionality to process WebSockets requests as
|
||||
specified in
|
||||
http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
|
||||
"""
|
||||
def __init__(self, handler):
|
||||
WebSocketProtocol.__init__(self, handler)
|
||||
self.challenge = None
|
||||
self._waiting = None
|
||||
|
||||
def accept_connection(self):
|
||||
try:
|
||||
self._handle_websocket_headers()
|
||||
except ValueError:
|
||||
gen_log.debug("Malformed WebSocket request received")
|
||||
self._abort()
|
||||
return
|
||||
|
||||
scheme = self.handler.get_websocket_scheme()
|
||||
|
||||
# draft76 only allows a single subprotocol
|
||||
subprotocol_header = ''
|
||||
subprotocol = self.request.headers.get("Sec-WebSocket-Protocol", None)
|
||||
if subprotocol:
|
||||
selected = self.handler.select_subprotocol([subprotocol])
|
||||
if selected:
|
||||
assert selected == subprotocol
|
||||
subprotocol_header = "Sec-WebSocket-Protocol: %s\r\n" % selected
|
||||
|
||||
# Write the initial headers before attempting to read the challenge.
|
||||
# This is necessary when using proxies (such as HAProxy), which
|
||||
# need to see the Upgrade headers before passing through the
|
||||
# non-HTTP traffic that follows.
|
||||
self.stream.write(tornado.escape.utf8(
|
||||
"HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
|
||||
"Upgrade: WebSocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Server: TornadoServer/%(version)s\r\n"
|
||||
"Sec-WebSocket-Origin: %(origin)s\r\n"
|
||||
"Sec-WebSocket-Location: %(scheme)s://%(host)s%(uri)s\r\n"
|
||||
"%(subprotocol)s"
|
||||
"\r\n" % (dict(
|
||||
version=tornado.version,
|
||||
origin=self.request.headers["Origin"],
|
||||
scheme=scheme,
|
||||
host=self.request.host,
|
||||
uri=self.request.uri,
|
||||
subprotocol=subprotocol_header))))
|
||||
self.stream.read_bytes(8, self._handle_challenge)
|
||||
|
||||
def challenge_response(self, challenge):
|
||||
"""Generates the challenge response that's needed in the handshake
|
||||
|
||||
The challenge parameter should be the raw bytes as sent from the
|
||||
client.
|
||||
"""
|
||||
key_1 = self.request.headers.get("Sec-Websocket-Key1")
|
||||
key_2 = self.request.headers.get("Sec-Websocket-Key2")
|
||||
try:
|
||||
part_1 = self._calculate_part(key_1)
|
||||
part_2 = self._calculate_part(key_2)
|
||||
except ValueError:
|
||||
raise ValueError("Invalid Keys/Challenge")
|
||||
return self._generate_challenge_response(part_1, part_2, challenge)
|
||||
|
||||
def _handle_challenge(self, challenge):
|
||||
try:
|
||||
challenge_response = self.challenge_response(challenge)
|
||||
except ValueError:
|
||||
gen_log.debug("Malformed key data in WebSocket request")
|
||||
self._abort()
|
||||
return
|
||||
self._write_response(challenge_response)
|
||||
|
||||
def _write_response(self, challenge):
|
||||
self.stream.write(challenge)
|
||||
self.async_callback(self.handler.open)(*self.handler.open_args, **self.handler.open_kwargs)
|
||||
self._receive_message()
|
||||
|
||||
def _handle_websocket_headers(self):
|
||||
"""Verifies all invariant- and required headers
|
||||
|
||||
If a header is missing or have an incorrect value ValueError will be
|
||||
raised
|
||||
"""
|
||||
fields = ("Origin", "Host", "Sec-Websocket-Key1",
|
||||
"Sec-Websocket-Key2")
|
||||
if not all(map(lambda f: self.request.headers.get(f), fields)):
|
||||
raise ValueError("Missing/Invalid WebSocket headers")
|
||||
|
||||
def _calculate_part(self, key):
|
||||
"""Processes the key headers and calculates their key value.
|
||||
|
||||
Raises ValueError when feed invalid key."""
|
||||
# pyflakes complains about variable reuse if both of these lines use 'c'
|
||||
number = int(''.join(c for c in key if c.isdigit()))
|
||||
spaces = len([c2 for c2 in key if c2.isspace()])
|
||||
try:
|
||||
key_number = number // spaces
|
||||
except (ValueError, ZeroDivisionError):
|
||||
raise ValueError
|
||||
return struct.pack(">I", key_number)
|
||||
|
||||
def _generate_challenge_response(self, part_1, part_2, part_3):
|
||||
m = hashlib.md5()
|
||||
m.update(part_1)
|
||||
m.update(part_2)
|
||||
m.update(part_3)
|
||||
return m.digest()
|
||||
|
||||
def _receive_message(self):
|
||||
self.stream.read_bytes(1, self._on_frame_type)
|
||||
|
||||
def _on_frame_type(self, byte):
|
||||
frame_type = ord(byte)
|
||||
if frame_type == 0x00:
|
||||
self.stream.read_until(b"\xff", self._on_end_delimiter)
|
||||
elif frame_type == 0xff:
|
||||
self.stream.read_bytes(1, self._on_length_indicator)
|
||||
else:
|
||||
self._abort()
|
||||
|
||||
def _on_end_delimiter(self, frame):
|
||||
if not self.client_terminated:
|
||||
self.async_callback(self.handler.on_message)(
|
||||
frame[:-1].decode("utf-8", "replace"))
|
||||
if not self.client_terminated:
|
||||
self._receive_message()
|
||||
|
||||
def _on_length_indicator(self, byte):
|
||||
if ord(byte) != 0x00:
|
||||
self._abort()
|
||||
return
|
||||
self.client_terminated = True
|
||||
self.close()
|
||||
|
||||
def write_message(self, message, binary=False):
|
||||
"""Sends the given message to the client of this Web Socket."""
|
||||
if binary:
|
||||
raise ValueError(
|
||||
"Binary messages not supported by this version of websockets")
|
||||
if isinstance(message, unicode_type):
|
||||
message = message.encode("utf-8")
|
||||
assert isinstance(message, bytes_type)
|
||||
self.stream.write(b"\x00" + message + b"\xff")
|
||||
|
||||
def write_ping(self, data):
|
||||
"""Send ping frame."""
|
||||
raise ValueError("Ping messages not supported by this version of websockets")
|
||||
|
||||
def close(self):
|
||||
"""Closes the WebSocket connection."""
|
||||
if not self.server_terminated:
|
||||
if not self.stream.closed():
|
||||
self.stream.write("\xff\x00")
|
||||
self.server_terminated = True
|
||||
if self.client_terminated:
|
||||
if self._waiting is not None:
|
||||
self.stream.io_loop.remove_timeout(self._waiting)
|
||||
self._waiting = None
|
||||
self.stream.close()
|
||||
elif self._waiting is None:
|
||||
self._waiting = self.stream.io_loop.add_timeout(
|
||||
time.time() + 5, self._abort)
|
||||
|
||||
@ -0,0 +1,122 @@
|
||||
# Implementation Notes for Internationalization of Jupyter Notebook
|
||||
|
||||
This is a prototype implementation of i18n features for Jupyter notebook, and should not
|
||||
yet be considered ready for production use. I have tried to focus on the public user
|
||||
interfaces in the notebook for the first cut, while leaving much of the console messages
|
||||
behind, as their usefulness in a translated environment is questionable at best.
|
||||
|
||||
### Using a prototype translated version
|
||||
|
||||
In order to use this preliminary version, you need to do things after installing the
|
||||
notebook as normal:
|
||||
|
||||
1. Set the LANG environment variable in your shell to "xx_XX" or just "xx".
|
||||
where "xx" is the language code you're wanting to run in. If you're
|
||||
running on Windows, I've found the easiest way to do this is to use Windows PowerShell,
|
||||
and run the command:
|
||||
|
||||
`${Env:LANG} = "xx_XX"`
|
||||
|
||||
2. Set the preferred language for web pages in your browser to YourLanguage (xx). At the moment,
|
||||
it has to be first in the list.
|
||||
|
||||
3. Run the `jupyter notebook` command to start the notebook.
|
||||
|
||||
### Message extraction:
|
||||
|
||||
I have split out the translatable material for the notebook into 3 POT, as follows:
|
||||
|
||||
notebook/i18n/notebook.pot - Console and startup messages, basically anything that is
|
||||
produced by Python code.
|
||||
|
||||
notebook/i18n/nbui.pot - User interface strings, as extracted from the Jinja2 templates
|
||||
in notebook/templates/*.html
|
||||
|
||||
noteook/i18n/nbjs.pot - JavaScript strings and dialogs, which contain much of the visible
|
||||
user interface for Jupyter notebook.
|
||||
|
||||
To extract the messages from the source code whenever new material is added, use the
|
||||
`pybabel` command to extract messages from the source code as follows:
|
||||
( assuming you are in the base directory for Jupyter notebook )
|
||||
|
||||
`pybabel extract -F notebook/i18n/babel_notebook.cfg -o notebook/i18n/notebook.pot --no-wrap --project Jupyter .`
|
||||
`pybabel extract -F notebook/i18n/babel_nbui.cfg -o notebook/i18n/nbui.pot --no-wrap --project Jupyter .`
|
||||
`pybabel extract -F notebook/i18n/babel_nbjs.cfg -o notebook/i18n/nbjs.pot --no-wrap --project Jupyter .`
|
||||
|
||||
(Note: there is a '.' at the end of these commands, and it has to be there...)
|
||||
|
||||
After this is complete you have 3 POT files that you can give to a translator for your favorite language.
|
||||
Babel's documentation has instructions on how to integrate this into your setup.py so that eventually
|
||||
we can just do:
|
||||
|
||||
`setup.py extract_messages`
|
||||
|
||||
I hope to get this working at some point in the near future.
|
||||
|
||||
### Post translation procedures
|
||||
|
||||
After the source material has been translated, you should have 3 PO files with the same base names
|
||||
as the POT files above. Put them in `notebook/i18n/${LANG}/LC_MESSAGES`, where ${LANG} is the language
|
||||
code for your desired language ( i.e. German = "de", Japanese = "ja", etc. ). The first 2 files then
|
||||
need to be converted from PO to MO format for use at runtime. There are many different ways to do
|
||||
this, but pybabel has an option to do this as follows:
|
||||
|
||||
`pybabel compile -D notebook -f -l ${LANG} -i notebook/i18n/${LANG}/LC_MESSAGES/notebook.po -o notebook/i18n/${LANG}/notebook.mo`
|
||||
|
||||
`pybabel compile -D nbui -f -l ${LANG} -i notebook/i18n/${LANG}/LC_MESSAGES/nbui.po -o notebook/i18n/${LANG}/nbui.mo`
|
||||
|
||||
The nbjs.po needs to be converted to JSON for use within the JavaScript code. I'm using po2json for this, as follows:
|
||||
|
||||
`po2json -p -F -f jed1.x -d nbjs notebook/i18n/${LANG}/LC_MESSAGES/nbjs.po notebook/i18n/${LANG}/LC_MESSAGES/nbjs.json`
|
||||
|
||||
The conversions from PO to MO probably can and should be done during setup.py.
|
||||
|
||||
When new languages get added, their language codes should be added to notebook/i18n/nbjs.json
|
||||
under the "supported_languages" element.
|
||||
|
||||
### Tips for Jupyter developers
|
||||
|
||||
The biggest "mistake" I found while doing i18n enablement was the habit of constructing UI messages
|
||||
from English "piece parts". For example, code like:
|
||||
|
||||
|
||||
`var msg = "Enter a new " + type + "name:"`
|
||||
|
||||
where "type" is either "file", "directory", or "notebook"....
|
||||
|
||||
is problematic when doing translations, because the surrounding text may need to vary
|
||||
depending on the inserted word. In this case, you need to switch it and use complete phrases,
|
||||
as follows:
|
||||
|
||||
```javascript
|
||||
var rename_msg = function (type) {
|
||||
switch(type) {
|
||||
case 'file': return _("Enter a new file name:");
|
||||
case 'directory': return _("Enter a new directory name:");
|
||||
case 'notebook': return _("Enter a new notebook name:");
|
||||
default: return _("Enter a new name:");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Also you need to remember that adding an "s" or "es" to an English word to
|
||||
create the plural form doesn't translate well. Some languages have as many as 5 or 6 different
|
||||
plural forms for differing numbers, so using an API such as ngettext() is necessary in order
|
||||
to handle these cases properly.
|
||||
|
||||
### Known issues
|
||||
|
||||
1. Right now there are two different places where the desired language is set. At startup time, the Jupyter console's messages pay attention to the setting of the ${LANG} environment variable
|
||||
as set in the shell at startup time. Unfortunately, this is also the time where the Jinja2
|
||||
environment is set up, which means that the template stuff will always come from this setting.
|
||||
We really want to be paying attention to the browser's settings for the stuff that happens in the
|
||||
browser, so we need to be able to retrieve this information after the browser is started and somehow
|
||||
communicate this back to Jinja2. So far, I haven't yet figured out how to do this, which means that if the ${LANG} at startup doesn't match the browser's settings, you could potentially get a mix
|
||||
of languages in the UI ( never a good thing ).
|
||||
|
||||
2. We will need to decide if console messages should be translatable, and enable them if desired.
|
||||
3. The keyboard shorcut editor was implemented after the i18n work was completed, so that portion
|
||||
does not have translation support at this time.
|
||||
|
||||
Any questions or comments please let me know @JCEmmons on github (emmo@us.ibm.com)
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
[javascript: notebook/static/base/js/*.js]
|
||||
extract_messages = $._, i18n.msg._
|
||||
|
||||
[javascript: notebook/static/notebook/js/*.js]
|
||||
extract_messages = $._, i18n.msg._
|
||||
|
||||
[javascript: notebook/static/notebook/js/celltoolbarpresets/*.js]
|
||||
extract_messages = $._, i18n.msg._
|
||||
|
||||
[javascript: notebook/static/tree/js/*.js]
|
||||
extract_messages = $._, i18n.msg._
|
||||
@ -0,0 +1,4 @@
|
||||
[jinja2: notebook/templates/**.html]
|
||||
encoding = utf-8
|
||||
[extractors]
|
||||
jinja2 = jinja2.ext:babel_extract
|
||||
@ -0,0 +1,2 @@
|
||||
[python: notebook/*.py]
|
||||
[python: notebook/services/contents/*.py]
|
||||
@ -0,0 +1,12 @@
|
||||
{
|
||||
"domain": "nbjs",
|
||||
"supported_languages": [
|
||||
],
|
||||
"locale_data": {
|
||||
"nbjs": {
|
||||
"": {
|
||||
"domain": "nbjs"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,731 @@
|
||||
# Translations template for Jupyter.
|
||||
# Copyright (C) 2017 ORGANIZATION
|
||||
# This file is distributed under the same license as the Jupyter project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Jupyter VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2017-07-07 12:48-0500\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.3.4\n"
|
||||
|
||||
#: notebook/templates/404.html:3
|
||||
msgid "You are requesting a page that does not exist!"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/edit.html:37
|
||||
msgid "current mode"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/edit.html:48 notebook/templates/notebook.html:78
|
||||
msgid "File"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/edit.html:50 notebook/templates/tree.html:57
|
||||
msgid "New"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/edit.html:51
|
||||
msgid "Save"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/edit.html:52 notebook/templates/tree.html:36
|
||||
msgid "Rename"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/edit.html:53 notebook/templates/tree.html:38
|
||||
msgid "Download"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/edit.html:56 notebook/templates/notebook.html:131
|
||||
#: notebook/templates/tree.html:41
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/edit.html:58
|
||||
msgid "Find"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/edit.html:59
|
||||
msgid "Find & Replace"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/edit.html:61
|
||||
msgid "Key Map"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/edit.html:62
|
||||
msgid "Default"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/edit.html:63
|
||||
msgid "Sublime Text"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/edit.html:68 notebook/templates/notebook.html:159
|
||||
#: notebook/templates/tree.html:40
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/edit.html:70 notebook/templates/notebook.html:162
|
||||
msgid "Show/Hide the logo and notebook title (above menu bar)"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/edit.html:71 notebook/templates/notebook.html:163
|
||||
msgid "Toggle Header"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/edit.html:72 notebook/templates/notebook.html:171
|
||||
msgid "Toggle Line Numbers"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/edit.html:75
|
||||
msgid "Language"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/error.html:23
|
||||
msgid "The error was:"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/login.html:24
|
||||
msgid "Password or token:"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/login.html:26
|
||||
msgid "Password:"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/login.html:31
|
||||
msgid "Log in"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/login.html:39
|
||||
msgid "No login available, you shouldn't be seeing this page."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/logout.html:24
|
||||
#, python-format
|
||||
msgid "Proceed to the <a href=\"%(base_url)s\">dashboard"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/logout.html:26
|
||||
#, python-format
|
||||
msgid "Proceed to the <a href=\"%(base_url)slogin\">login page"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:62
|
||||
msgid "Menu"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:65 notebook/templates/notebook.html:254
|
||||
msgid "Kernel"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:68
|
||||
msgid "This notebook is read-only"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:81
|
||||
msgid "New Notebook"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:85
|
||||
msgid "Opens a new window with the Dashboard view"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:86
|
||||
msgid "Open..."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:90
|
||||
msgid "Open a copy of this notebook's contents and start a new kernel"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:91
|
||||
msgid "Make a Copy..."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:92
|
||||
msgid "Rename..."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:93
|
||||
msgid "Save and Checkpoint"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:96
|
||||
msgid "Revert to Checkpoint"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:106
|
||||
msgid "Print Preview"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:107
|
||||
msgid "Download as"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:109
|
||||
msgid "Notebook (.ipynb)"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:110
|
||||
msgid "Script"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:111
|
||||
msgid "HTML (.html)"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:112
|
||||
msgid "Markdown (.md)"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:113
|
||||
msgid "reST (.rst)"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:114
|
||||
msgid "LaTeX (.tex)"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:115
|
||||
msgid "PDF via LaTeX (.pdf)"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:118
|
||||
msgid "Deploy as"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:123
|
||||
msgid "Trust the output of this notebook"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:124
|
||||
msgid "Trust Notebook"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:127
|
||||
msgid "Shutdown this notebook's kernel, and close this window"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:128
|
||||
msgid "Close and Halt"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:133
|
||||
msgid "Cut Cells"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:134
|
||||
msgid "Copy Cells"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:135
|
||||
msgid "Paste Cells Above"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:136
|
||||
msgid "Paste Cells Below"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:137
|
||||
msgid "Paste Cells & Replace"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:138
|
||||
msgid "Delete Cells"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:139
|
||||
msgid "Undo Delete Cells"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:141
|
||||
msgid "Split Cell"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:142
|
||||
msgid "Merge Cell Above"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:143
|
||||
msgid "Merge Cell Below"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:145
|
||||
msgid "Move Cell Up"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:146
|
||||
msgid "Move Cell Down"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:148
|
||||
msgid "Edit Notebook Metadata"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:150
|
||||
msgid "Find and Replace"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:152
|
||||
msgid "Cut Cell Attachments"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:153
|
||||
msgid "Copy Cell Attachments"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:154
|
||||
msgid "Paste Cell Attachments"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:156
|
||||
msgid "Insert Image"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:166
|
||||
msgid "Show/Hide the action icons (below menu bar)"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:167
|
||||
msgid "Toggle Toolbar"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:170
|
||||
msgid "Show/Hide line numbers in cells"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:174
|
||||
msgid "Cell Toolbar"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:179
|
||||
msgid "Insert"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:182
|
||||
msgid "Insert an empty Code cell above the currently active cell"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:183
|
||||
msgid "Insert Cell Above"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:185
|
||||
msgid "Insert an empty Code cell below the currently active cell"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:186
|
||||
msgid "Insert Cell Below"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:189
|
||||
msgid "Cell"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:191
|
||||
msgid "Run this cell, and move cursor to the next one"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:192
|
||||
msgid "Run Cells"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:193
|
||||
msgid "Run this cell, select below"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:194
|
||||
msgid "Run Cells and Select Below"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:195
|
||||
msgid "Run this cell, insert below"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:196
|
||||
msgid "Run Cells and Insert Below"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:197
|
||||
msgid "Run all cells in the notebook"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:198
|
||||
msgid "Run All"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:199
|
||||
msgid "Run all cells above (but not including) this cell"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:200
|
||||
msgid "Run All Above"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:201
|
||||
msgid "Run this cell and all cells below it"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:202
|
||||
msgid "Run All Below"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:205
|
||||
msgid "All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:206
|
||||
msgid "Cell Type"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:209
|
||||
msgid "Contents will be sent to the kernel for execution, and output will display in the footer of cell"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:212
|
||||
msgid "Contents will be rendered as HTML and serve as explanatory text"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:213 notebook/templates/notebook.html:298
|
||||
msgid "Markdown"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:215
|
||||
msgid "Contents will pass through nbconvert unmodified"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:216
|
||||
msgid "Raw NBConvert"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:220
|
||||
msgid "Current Outputs"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:223
|
||||
msgid "Hide/Show the output of the current cell"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:224 notebook/templates/notebook.html:240
|
||||
msgid "Toggle"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:227
|
||||
msgid "Scroll the output of the current cell"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:228 notebook/templates/notebook.html:244
|
||||
msgid "Toggle Scrolling"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:231
|
||||
msgid "Clear the output of the current cell"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:232 notebook/templates/notebook.html:248
|
||||
msgid "Clear"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:236
|
||||
msgid "All Output"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:239
|
||||
msgid "Hide/Show the output of all cells"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:243
|
||||
msgid "Scroll the output of all cells"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:247
|
||||
msgid "Clear the output of all cells"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:257
|
||||
msgid "Send Keyboard Interrupt (CTRL-C) to the Kernel"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:258
|
||||
msgid "Interrupt"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:261
|
||||
msgid "Restart the Kernel"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:262
|
||||
msgid "Restart"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:265
|
||||
msgid "Restart the Kernel and clear all output"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:266
|
||||
msgid "Restart & Clear Output"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:269
|
||||
msgid "Restart the Kernel and re-run the notebook"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:270
|
||||
msgid "Restart & Run All"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:273
|
||||
msgid "Reconnect to the Kernel"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:274
|
||||
msgid "Reconnect"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:282
|
||||
msgid "Change kernel"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:287
|
||||
msgid "Help"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:290
|
||||
msgid "A quick tour of the notebook user interface"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:290
|
||||
msgid "User Interface Tour"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:291
|
||||
msgid "Opens a tooltip with all keyboard shortcuts"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:291
|
||||
msgid "Keyboard Shortcuts"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:292
|
||||
msgid "Opens a dialog allowing you to edit Keyboard shortcuts"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:292
|
||||
msgid "Edit Keyboard Shortcuts"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:297
|
||||
msgid "Notebook Help"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:303
|
||||
msgid "Opens in a new window"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:319
|
||||
msgid "About Jupyter Notebook"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/notebook.html:319
|
||||
msgid "About"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/page.html:114
|
||||
msgid "Jupyter Notebook requires JavaScript."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/page.html:115
|
||||
msgid "Please enable it to proceed. "
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/page.html:121
|
||||
msgid "dashboard"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/page.html:132
|
||||
msgid "Logout"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/page.html:134
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:23
|
||||
msgid "Files"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:24
|
||||
msgid "Running"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:25
|
||||
msgid "Clusters"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:32
|
||||
msgid "Select items to perform actions on them."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:35
|
||||
msgid "Duplicate selected"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:35
|
||||
msgid "Duplicate"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:36
|
||||
msgid "Rename selected"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:37
|
||||
msgid "Move selected"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:37
|
||||
msgid "Move"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:38
|
||||
msgid "Download selected"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:39
|
||||
msgid "Shutdown selected notebook(s)"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:39
|
||||
msgid "Shutdown"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:40
|
||||
msgid "View selected"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:41
|
||||
msgid "Edit selected"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:42
|
||||
msgid "Delete selected"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:50
|
||||
msgid "Click to browse for a file to upload."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:51
|
||||
msgid "Upload"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:65
|
||||
msgid "Text File"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:68
|
||||
msgid "Folder"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:72
|
||||
msgid "Terminal"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:76
|
||||
msgid "Terminals Unavailable"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:82
|
||||
msgid "Refresh notebook list"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:90
|
||||
msgid "Select All / None"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:93
|
||||
msgid "Select..."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:98
|
||||
msgid "Select All Folders"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:98
|
||||
msgid " Folders"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:99
|
||||
msgid "Select All Notebooks"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:99
|
||||
msgid " All Notebooks"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:100
|
||||
msgid "Select Running Notebooks"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:100
|
||||
msgid " Running"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:101
|
||||
msgid "Select All Files"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:101
|
||||
msgid " Files"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:114
|
||||
msgid "Last Modified"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:120
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:130
|
||||
msgid "Currently running Jupyter processes"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:134
|
||||
msgid "Refresh running list"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:150
|
||||
msgid "There are no terminals running."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:152
|
||||
msgid "Terminals are unavailable."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:162
|
||||
msgid "Notebooks"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:169
|
||||
msgid "There are no notebooks running."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:178
|
||||
msgid "Clusters tab is now provided by IPython parallel."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/templates/tree.html:179
|
||||
msgid "See '<a href=\"https://github.com/ipython/ipyparallel\">IPython parallel</a>' for installation details."
|
||||
msgstr ""
|
||||
|
||||
@ -0,0 +1,480 @@
|
||||
# Translations template for Jupyter.
|
||||
# Copyright (C) 2017 ORGANIZATION
|
||||
# This file is distributed under the same license as the Jupyter project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Jupyter VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2017-07-08 21:52-0500\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.3.4\n"
|
||||
|
||||
#: notebook/notebookapp.py:53
|
||||
msgid "The Jupyter Notebook requires tornado >= 4.0"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:57
|
||||
msgid "The Jupyter Notebook requires tornado >= 4.0, but you have < 1.1.0"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:59
|
||||
#, python-format
|
||||
msgid "The Jupyter Notebook requires tornado >= 4.0, but you have %s"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:209
|
||||
msgid "The `ignore_minified_js` flag is deprecated and no longer works."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:210
|
||||
#, python-format
|
||||
msgid "Alternatively use `%s` when working on the notebook's Javascript and LESS"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:211
|
||||
msgid "The `ignore_minified_js` flag is deprecated and will be removed in Notebook 6.0"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:389
|
||||
msgid "List currently running notebook servers."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:393
|
||||
msgid "Produce machine-readable JSON output."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:397
|
||||
msgid "If True, each line of output will be a JSON object with the details from the server info file."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:402
|
||||
msgid "Currently running servers:"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:419
|
||||
msgid "Don't open the notebook in a browser after startup."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:423
|
||||
msgid "DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:439
|
||||
msgid "Allow the notebook to be run from root user."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:470
|
||||
msgid ""
|
||||
"The Jupyter HTML Notebook.\n"
|
||||
" \n"
|
||||
" This launches a Tornado based HTML Notebook Server that serves up an HTML5/Javascript Notebook client."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:509
|
||||
msgid "Deprecated: Use minified JS file or not, mainly use during dev to avoid JS recompilation"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:540
|
||||
msgid "Set the Access-Control-Allow-Credentials: true header"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:544
|
||||
msgid "Whether to allow the user to run the notebook as root."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:548
|
||||
msgid "The default URL to redirect to from `/`"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:552
|
||||
msgid "The IP address the notebook server will listen on."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:565
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Cannot bind to localhost, using 127.0.0.1 as default ip\n"
|
||||
"%s"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:579
|
||||
msgid "The port the notebook server will listen on."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:583
|
||||
msgid "The number of additional ports to try if the specified port is not available."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:587
|
||||
msgid "The full path to an SSL/TLS certificate file."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:591
|
||||
msgid "The full path to a private key file for usage with SSL/TLS."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:595
|
||||
msgid "The full path to a certificate authority certificate for SSL/TLS client authentication."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:599
|
||||
msgid "The file where the cookie secret is stored."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:628
|
||||
#, python-format
|
||||
msgid "Writing notebook server cookie secret to %s"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:635
|
||||
#, python-format
|
||||
msgid "Could not set permissions on %s"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:640
|
||||
msgid ""
|
||||
"Token used for authenticating first-time connections to the server.\n"
|
||||
"\n"
|
||||
" When no password is enabled,\n"
|
||||
" the default is to generate a new, random token.\n"
|
||||
"\n"
|
||||
" Setting to an empty string disables authentication altogether, which is NOT RECOMMENDED.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:650
|
||||
msgid ""
|
||||
"One-time token used for opening a browser.\n"
|
||||
" Once used, this token cannot be used again.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:726
|
||||
msgid ""
|
||||
"Specify Where to open the notebook on startup. This is the\n"
|
||||
" `new` argument passed to the standard library method `webbrowser.open`.\n"
|
||||
" The behaviour is not guaranteed, but depends on browser support. Valid\n"
|
||||
" values are:\n"
|
||||
" 2 opens a new tab,\n"
|
||||
" 1 opens a new window,\n"
|
||||
" 0 opens in an existing window.\n"
|
||||
" See the `webbrowser.open` documentation for details.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:737
|
||||
msgid "DEPRECATED, use tornado_settings"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:742
|
||||
msgid ""
|
||||
"\n"
|
||||
" webapp_settings is deprecated, use tornado_settings.\n"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:746
|
||||
msgid "Supply overrides for the tornado.web.Application that the Jupyter notebook uses."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:750
|
||||
msgid ""
|
||||
"\n"
|
||||
" Set the tornado compression options for websocket connections.\n"
|
||||
"\n"
|
||||
" This value will be returned from :meth:`WebSocketHandler.get_compression_options`.\n"
|
||||
" None (default) will disable compression.\n"
|
||||
" A dict (even an empty one) will enable compression.\n"
|
||||
"\n"
|
||||
" See the tornado docs for WebSocketHandler.get_compression_options for details.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:761
|
||||
msgid "Supply overrides for terminado. Currently only supports \"shell_command\"."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:764
|
||||
msgid "Extra keyword arguments to pass to `set_secure_cookie`. See tornado's set_secure_cookie docs for details."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:768
|
||||
msgid ""
|
||||
"Supply SSL options for the tornado HTTPServer.\n"
|
||||
" See the tornado docs for details."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:772
|
||||
msgid "Supply extra arguments that will be passed to Jinja environment."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:776
|
||||
msgid "Extra variables to supply to jinja templates when rendering."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:812
|
||||
msgid "DEPRECATED use base_url"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:816
|
||||
msgid "base_project_url is deprecated, use base_url"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:832
|
||||
msgid "Path to search for custom.js, css"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:844
|
||||
msgid ""
|
||||
"Extra paths to search for serving jinja templates.\n"
|
||||
"\n"
|
||||
" Can be used to override templates from notebook.templates."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:855
|
||||
msgid "extra paths to look for Javascript notebook extensions"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:900
|
||||
#, python-format
|
||||
msgid "Using MathJax: %s"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:903
|
||||
msgid "The MathJax.js configuration file that is to be used."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:908
|
||||
#, python-format
|
||||
msgid "Using MathJax configuration file: %s"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:914
|
||||
msgid "The notebook manager class to use."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:920
|
||||
msgid "The kernel manager class to use."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:926
|
||||
msgid "The session manager class to use."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:932
|
||||
msgid "The config manager class to use"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:953
|
||||
msgid "The login handler class to use."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:960
|
||||
msgid "The logout handler class to use."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:964
|
||||
msgid "Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headerssent by the upstream reverse proxy. Necessary if the proxy handles SSL"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:976
|
||||
msgid ""
|
||||
"\n"
|
||||
" DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:988
|
||||
msgid "Support for specifying --pylab on the command line has been removed."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:990
|
||||
msgid "Please use `%pylab{0}` or `%matplotlib{0}` in the notebook itself."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:995
|
||||
msgid "The directory to use for notebooks and kernels."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1018
|
||||
#, python-format
|
||||
msgid "No such notebook dir: '%r'"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1031
|
||||
msgid "DEPRECATED use the nbserver_extensions dict instead"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1036
|
||||
msgid "server_extensions is deprecated, use nbserver_extensions"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1040
|
||||
msgid "Dict of Python modules to load as notebook server extensions.Entry values can be used to enable and disable the loading ofthe extensions. The extensions will be loaded in alphabetical order."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1049
|
||||
msgid "Reraise exceptions encountered loading server extensions?"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1052
|
||||
msgid ""
|
||||
"(msgs/sec)\n"
|
||||
" Maximum rate at which messages can be sent on iopub before they are\n"
|
||||
" limited."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1056
|
||||
msgid ""
|
||||
"(bytes/sec)\n"
|
||||
" Maximum rate at which stream output can be sent on iopub before they are\n"
|
||||
" limited."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1060
|
||||
msgid ""
|
||||
"(sec) Time window used to \n"
|
||||
" check the message and data rate limits."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1071
|
||||
#, python-format
|
||||
msgid "No such file or directory: %s"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1141
|
||||
msgid "Notebook servers are configured to only be run with a password."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1142
|
||||
msgid "Hint: run the following command to set a password"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1143
|
||||
msgid "\t$ python -m notebook.auth password"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1181
|
||||
#, python-format
|
||||
msgid "The port %i is already in use, trying another port."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1184
|
||||
#, python-format
|
||||
msgid "Permission to listen on port %i denied"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1193
|
||||
msgid "ERROR: the notebook server could not be started because no available port could be found."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1199
|
||||
msgid "[all ip addresses on your system]"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1223
|
||||
#, python-format
|
||||
msgid "Terminals not available (error was %s)"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1259
|
||||
msgid "interrupted"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1261
|
||||
msgid "y"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1262
|
||||
msgid "n"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1263
|
||||
#, python-format
|
||||
msgid "Shutdown this notebook server (%s/[%s])? "
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1269
|
||||
msgid "Shutdown confirmed"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1273
|
||||
msgid "No answer for 5s:"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1274
|
||||
msgid "resuming operation..."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1282
|
||||
#, python-format
|
||||
msgid "received signal %s, stopping"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1338
|
||||
#, python-format
|
||||
msgid "Error loading server extension %s"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1369
|
||||
#, python-format
|
||||
msgid "Shutting down %d kernels"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1375
|
||||
#, python-format
|
||||
msgid "%d active kernel"
|
||||
msgid_plural "%d active kernels"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: notebook/notebookapp.py:1379
|
||||
#, python-format
|
||||
msgid ""
|
||||
"The Jupyter Notebook is running at:\n"
|
||||
"\r"
|
||||
"%s"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1426
|
||||
msgid "Running as root is not recommended. Use --allow-root to bypass."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1432
|
||||
msgid "Use Control-C to stop this server and shut down all kernels (twice to skip confirmation)."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1434
|
||||
msgid "Welcome to Project Jupyter! Explore the various tools available and their corresponding documentation. If you are interested in contributing to the platform, please visit the communityresources section at http://jupyter.org/community.html."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1445
|
||||
#, python-format
|
||||
msgid "No web browser found: %s."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1450
|
||||
#, python-format
|
||||
msgid "%s does not exist"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/notebookapp.py:1484
|
||||
msgid "Interrupted..."
|
||||
msgstr ""
|
||||
|
||||
#: notebook/services/contents/filemanager.py:506
|
||||
#, python-format
|
||||
msgid "Serving notebooks from local directory: %s"
|
||||
msgstr ""
|
||||
|
||||
#: notebook/services/contents/manager.py:68
|
||||
msgid "Untitled"
|
||||
msgstr ""
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
from notebook.services.contents.filemanager import FileContentsManager
|
||||
from contextlib import contextmanager
|
||||
from tornado import web
|
||||
import nbformat
|
||||
import base64
|
||||
import os, io
|
||||
|
||||
class LargeFileManager(FileContentsManager):
|
||||
"""Handle large file upload."""
|
||||
|
||||
def save(self, model, path=''):
|
||||
"""Save the file model and return the model with no content."""
|
||||
chunk = model.get('chunk', None)
|
||||
if chunk is not None:
|
||||
path = path.strip('/')
|
||||
|
||||
if 'type' not in model:
|
||||
raise web.HTTPError(400, u'No file type provided')
|
||||
if model['type'] != 'file':
|
||||
raise web.HTTPError(400, u'File type "{}" is not supported for large file transfer'.format(model['type']))
|
||||
if 'content' not in model and model['type'] != 'directory':
|
||||
raise web.HTTPError(400, u'No file content provided')
|
||||
|
||||
os_path = self._get_os_path(path)
|
||||
|
||||
try:
|
||||
if chunk == 1:
|
||||
self.log.debug("Saving %s", os_path)
|
||||
self.run_pre_save_hook(model=model, path=path)
|
||||
super(LargeFileManager, self)._save_file(os_path, model['content'], model.get('format'))
|
||||
else:
|
||||
self._save_large_file(os_path, model['content'], model.get('format'))
|
||||
except web.HTTPError:
|
||||
raise
|
||||
except Exception as e:
|
||||
self.log.error(u'Error while saving file: %s %s', path, e, exc_info=True)
|
||||
raise web.HTTPError(500, u'Unexpected error while saving file: %s %s' % (path, e))
|
||||
|
||||
model = self.get(path, content=False)
|
||||
|
||||
# Last chunk
|
||||
if chunk == -1:
|
||||
self.run_post_save_hook(model=model, os_path=os_path)
|
||||
return model
|
||||
else:
|
||||
return super(LargeFileManager, self).save(model, path)
|
||||
|
||||
def _save_large_file(self, os_path, content, format):
|
||||
"""Save content of a generic file."""
|
||||
if format not in {'text', 'base64'}:
|
||||
raise web.HTTPError(
|
||||
400,
|
||||
"Must specify format of file contents as 'text' or 'base64'",
|
||||
)
|
||||
try:
|
||||
if format == 'text':
|
||||
bcontent = content.encode('utf8')
|
||||
else:
|
||||
b64_bytes = content.encode('ascii')
|
||||
bcontent = base64.b64decode(b64_bytes)
|
||||
except Exception as e:
|
||||
raise web.HTTPError(
|
||||
400, u'Encoding error saving %s: %s' % (os_path, e)
|
||||
)
|
||||
|
||||
with self.perm_to_403(os_path):
|
||||
if os.path.islink(os_path):
|
||||
os_path = os.path.join(os.path.dirname(os_path), os.readlink(os_path))
|
||||
with io.open(os_path, 'ab') as f:
|
||||
f.write(bcontent)
|
||||
@ -0,0 +1,113 @@
|
||||
from unittest import TestCase
|
||||
from ipython_genutils.tempdir import TemporaryDirectory
|
||||
from ..largefilemanager import LargeFileManager
|
||||
import os
|
||||
from tornado import web
|
||||
|
||||
|
||||
def _make_dir(contents_manager, api_path):
|
||||
"""
|
||||
Make a directory.
|
||||
"""
|
||||
os_path = contents_manager._get_os_path(api_path)
|
||||
try:
|
||||
os.makedirs(os_path)
|
||||
except OSError:
|
||||
print("Directory already exists: %r" % os_path)
|
||||
|
||||
|
||||
class TestLargeFileManager(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._temp_dir = TemporaryDirectory()
|
||||
self.td = self._temp_dir.name
|
||||
self.contents_manager = LargeFileManager(root_dir=self.td)
|
||||
|
||||
def make_dir(self, api_path):
|
||||
"""make a subdirectory at api_path
|
||||
|
||||
override in subclasses if contents are not on the filesystem.
|
||||
"""
|
||||
_make_dir(self.contents_manager, api_path)
|
||||
|
||||
def test_save(self):
|
||||
|
||||
cm = self.contents_manager
|
||||
# Create a notebook
|
||||
model = cm.new_untitled(type='notebook')
|
||||
name = model['name']
|
||||
path = model['path']
|
||||
|
||||
# Get the model with 'content'
|
||||
full_model = cm.get(path)
|
||||
# Save the notebook
|
||||
model = cm.save(full_model, path)
|
||||
assert isinstance(model, dict)
|
||||
self.assertIn('name', model)
|
||||
self.assertIn('path', model)
|
||||
self.assertEqual(model['name'], name)
|
||||
self.assertEqual(model['path'], path)
|
||||
|
||||
try:
|
||||
model = {'name': 'test', 'path': 'test', 'chunk': 1}
|
||||
cm.save(model, model['path'])
|
||||
except web.HTTPError as e:
|
||||
self.assertEqual('HTTP 400: Bad Request (No file type provided)', str(e))
|
||||
|
||||
try:
|
||||
model = {'name': 'test', 'path': 'test', 'chunk': 1, 'type': 'notebook'}
|
||||
cm.save(model, model['path'])
|
||||
except web.HTTPError as e:
|
||||
self.assertEqual('HTTP 400: Bad Request (File type "notebook" is not supported for large file transfer)', str(e))
|
||||
|
||||
try:
|
||||
model = {'name': 'test', 'path': 'test', 'chunk': 1, 'type': 'file'}
|
||||
cm.save(model, model['path'])
|
||||
except web.HTTPError as e:
|
||||
self.assertEqual('HTTP 400: Bad Request (No file content provided)', str(e))
|
||||
|
||||
try:
|
||||
model = {'name': 'test', 'path': 'test', 'chunk': 2, 'type': 'file',
|
||||
'content': u'test', 'format': 'json'}
|
||||
cm.save(model, model['path'])
|
||||
except web.HTTPError as e:
|
||||
self.assertEqual("HTTP 400: Bad Request (Must specify format of file contents as 'text' or 'base64')",
|
||||
str(e))
|
||||
|
||||
# Save model for different chunks
|
||||
model = {'name': 'test', 'path': 'test', 'type': 'file',
|
||||
'content': u'test==', 'format': 'text'}
|
||||
name = model['name']
|
||||
path = model['path']
|
||||
cm.save(model, path)
|
||||
|
||||
for chunk in (1, 2, -1):
|
||||
for fm in ('text', 'base64'):
|
||||
full_model = cm.get(path)
|
||||
full_model['chunk'] = chunk
|
||||
full_model['format'] = fm
|
||||
model_res = cm.save(full_model, path)
|
||||
assert isinstance(model_res, dict)
|
||||
|
||||
self.assertIn('name', model_res)
|
||||
self.assertIn('path', model_res)
|
||||
self.assertNotIn('chunk', model_res)
|
||||
self.assertEqual(model_res['name'], name)
|
||||
self.assertEqual(model_res['path'], path)
|
||||
|
||||
# Test in sub-directory
|
||||
# Create a directory and notebook in that directory
|
||||
sub_dir = '/foo/'
|
||||
self.make_dir('foo')
|
||||
model = cm.new_untitled(path=sub_dir, type='notebook')
|
||||
name = model['name']
|
||||
path = model['path']
|
||||
model = cm.get(path)
|
||||
|
||||
# Change the name in the model for rename
|
||||
model = cm.save(model, path)
|
||||
assert isinstance(model, dict)
|
||||
self.assertIn('name', model)
|
||||
self.assertIn('path', model)
|
||||
self.assertEqual(model['name'], 'Untitled.ipynb')
|
||||
self.assertEqual(model['path'], 'foo/Untitled.ipynb')
|
||||
@ -0,0 +1,15 @@
|
||||
"""HTTP handler to shut down the notebook server.
|
||||
"""
|
||||
from tornado import web, ioloop
|
||||
from notebook.base.handlers import IPythonHandler
|
||||
|
||||
class ShutdownHandler(IPythonHandler):
|
||||
@web.authenticated
|
||||
def post(self):
|
||||
self.log.info("Shutting down on /api/shutdown request.")
|
||||
ioloop.IOLoop.current().stop()
|
||||
|
||||
|
||||
default_handlers = [
|
||||
(r"/api/shutdown", ShutdownHandler),
|
||||
]
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,54 @@
|
||||
// Copyright (c) Jupyter Development Team.
|
||||
// Distributed under the terms of the Modified BSD License.
|
||||
|
||||
// Module to handle i18n ( Internationalization ) and translated UI
|
||||
|
||||
define([
|
||||
'jed',
|
||||
'moment',
|
||||
'json!../../../i18n/nbjs.json',
|
||||
'base/js/i18nload',
|
||||
], function(Jed, moment, nbjs, i18nload) {
|
||||
"use strict";
|
||||
|
||||
// Setup language related stuff
|
||||
var ui_lang = navigator.languages && navigator.languages[0] || // Chrome / Firefox
|
||||
navigator.language || // All browsers
|
||||
navigator.userLanguage; // IE <= 10
|
||||
|
||||
var init = function() {
|
||||
var msg_promise;
|
||||
if (nbjs.supported_languages.indexOf(ui_lang) >= 0) {
|
||||
moment.locale(ui_lang);
|
||||
msg_promise = new Promise( function (resolve, reject) {
|
||||
require([i18nload.id+"!"+ui_lang], function (data) {
|
||||
var newi18n = new Jed(data);
|
||||
newi18n._ = newi18n.gettext;
|
||||
resolve(newi18n);
|
||||
}, function (error) {
|
||||
console.log("Error loading translations for language: "+ui_lang);
|
||||
var newi18n = new Jed(nbjs);
|
||||
newi18n._ = newi18n.gettext;
|
||||
resolve(newi18n);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
msg_promise = new Promise( function (resolve, reject) {
|
||||
var newi18n = new Jed(nbjs);
|
||||
newi18n._ = newi18n.gettext;
|
||||
resolve(newi18n);
|
||||
});
|
||||
}
|
||||
return msg_promise;
|
||||
}
|
||||
var i18n = new Jed(nbjs);
|
||||
i18n._ = i18n.gettext;
|
||||
i18n.msg = i18n; // Just a place holder until the init promise resolves.
|
||||
|
||||
init().then(function (msg) {
|
||||
i18n.msg = msg;
|
||||
i18n.msg._ = i18n.msg.gettext;
|
||||
});
|
||||
|
||||
return i18n;
|
||||
});
|
||||
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Plugin to load a single locale.
|
||||
*/
|
||||
define([
|
||||
"require",
|
||||
"module",
|
||||
// These are only here so that the optimizer knows which ones we MIGHT load.
|
||||
// We will actually only load the ones we need. There should be one entry
|
||||
// here for each language you want to support.
|
||||
// For example, for German....
|
||||
// "json!base/../../i18n/de/LC_MESSAGES/nbjs.json"
|
||||
], function (require, module) {
|
||||
return {
|
||||
id: module.id,
|
||||
|
||||
load: function (locale, callerRequire, onload, loaderConfig) {
|
||||
|
||||
var dependencies = "json!base/../../i18n/"+locale+"/LC_MESSAGES/nbjs.json";
|
||||
|
||||
// Load the JSON file requested
|
||||
require([dependencies], function (data) {
|
||||
onload(data);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
@ -0,0 +1 @@
|
||||
base/images/favicon.ico
|
||||