Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preliminary i18n implementation as outlined in Jep16 #2140

Merged
merged 27 commits into from
Jul 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
39bef81
WIP: Preliminary work for message extraction
JCEmmons Dec 15, 2016
521138e
WIP: Implementation of i18n for Jupyter notebook
JCEmmons Jan 13, 2017
4273f55
WIP: More work on German translations and nbui templates
JCEmmons Jan 18, 2017
bae4890
WIP: Enablement of translation of actions
JCEmmons Jan 19, 2017
067c3f4
WIP: Finish most of the visible UI, except some of the help and command
JCEmmons Jan 30, 2017
ec11231
WIP: Almost there, finished most of the UI elements
JCEmmons Feb 3, 2017
8a6e82a
Preliminary German i18n for notebook
JCEmmons Feb 6, 2017
53e88de
Fix toolbar presets,enablement problems,greatly improve translations
JCEmmons Feb 26, 2017
c71e1cb
Rebase and update to current 5.0
JCEmmons Apr 14, 2017
e588d54
Add German nbjs.json
JCEmmons Apr 14, 2017
09cec11
Fix conflict between _ and gettext
JCEmmons Apr 19, 2017
c089d75
Remove stray references to utils.i18n
JCEmmons Apr 24, 2017
f66a2ac
Fix bad declaration in mathjaxutils.js
JCEmmons Apr 25, 2017
88db876
Revert some unnecessary changes...
JCEmmons Apr 25, 2017
a6b9611
Reworking msg implementation for dynamic JSON loading
JCEmmons Jun 26, 2017
510c5f4
i18n msg fix toolbar and searchandreplace
JCEmmons Jun 26, 2017
c9abcec
i18n dynamic message loading fixes
JCEmmons Jun 26, 2017
17a5a7b
Finished fixing for dynamic load of i18n messages.
JCEmmons Jun 27, 2017
6fbb3c9
Finish cleanup of i18n implementation, remove MT German, updated README.
JCEmmons Jun 27, 2017
759e5e3
Clean up a few items from code review...
JCEmmons Jul 7, 2017
b9f0e11
regen notebook.pot because of resolution of merge conflicts
JCEmmons Jul 7, 2017
75860a3
Fix enablement for "%d active kernel(s)" message introduced.
JCEmmons Jul 9, 2017
99f106c
errant tabs
minrk Jul 11, 2017
25cddba
restore unix line endings
minrk Jul 11, 2017
8ddbe51
add missing files from setupbase
minrk Jul 11, 2017
9a8a4f4
Merge branch 'master' into jep16
minrk Jul 11, 2017
6e2c159
Fix a couple of message errors
minrk Jul 11, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ src
Read the Docs
config.rst

/.project
/.pydevproject

3 changes: 3 additions & 0 deletions bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"es6-promise": "~1.0",
"font-awesome": "components/font-awesome#~4.7.0",
"google-caja": "5669",
"jed": "~1.1.1",
"jquery": "components/jquery#~2.0",
"jquery-typeahead": "~2.0.0",
"jquery-ui": "components/jqueryui#~1.10",
Expand All @@ -19,6 +20,8 @@
"preact-compat": "https://unpkg.com/preact-compat@^3.14.3/dist/preact-compat.min.js",
"proptypes": "https://unpkg.com/proptypes@^0.14.4/index.js",
"requirejs": "~2.1",
"requirejs-text": "~2.0.15",
"requirejs-plugins": "~1.0.3",
"text-encoding": "~0.1",
"underscore": "components/underscore#~1.8.3",
"xterm.js": "sourcelair/xterm.js#~2.8.1"
Expand Down
122 changes: 122 additions & 0 deletions notebook/i18n/README.md
Original file line number Diff line number Diff line change
@@ -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)

11 changes: 11 additions & 0 deletions notebook/i18n/babel_nbjs.cfg
Original file line number Diff line number Diff line change
@@ -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._
4 changes: 4 additions & 0 deletions notebook/i18n/babel_nbui.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[jinja2: notebook/templates/**.html]
encoding = utf-8
[extractors]
jinja2 = jinja2.ext:babel_extract
2 changes: 2 additions & 0 deletions notebook/i18n/babel_notebook.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[python: notebook/*.py]
[python: notebook/services/contents/*.py]
12 changes: 12 additions & 0 deletions notebook/i18n/nbjs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"domain": "nbjs",
"supported_languages": [
],
"locale_data": {
"nbjs": {
"": {
"domain": "nbjs"
}
}
}
}
Loading