diff --git a/I18N.md b/I18N.md index 75fe19ef0..6387d4009 100644 --- a/I18N.md +++ b/I18N.md @@ -3,39 +3,50 @@ Internationalization RunestoneComponents uses Wikimedia'a [jQuery.i18n](https://github.com/wikimedia/jquery.i18n) Javascript internationalization library. -Configuration and message loading ---------------------------------- +Message loading scripts +----------------------- -In the `js` subfolder of a component's source folder you should create javascript files for i18n message loading. File names shoud folow the structure: +In the `js` subfolder of your component's source folder you should create javascript files for i18n message loading. A mesage loading script file name shoud folow the pattern: -_resoruce-name_`.`_language-code_`.js` +_message-bunlde-name_`.`_language-code_`.js` -where _resource-name_ should start with the component's name and ends wit `-i18n`. For examle, the english message loading script for the ActiveCode componet is `activecode-i18n.en.js`. +where _message-bunlde-name_ should start with the component's name and ends with `-i18n`. For examle, the english message loading script for the ActiveCode componet is `activecode-i18n.en.js`. Read more about message loading at [https://github.com/wikimedia/jquery.i18n#message-loading](https://github.com/wikimedia/jquery.i18n#message-loading) -Since messages for different components may be loaded in the same web page context, to avoid name conflicts, message keys should start with `msg_`_component-name_`_` like in this sample mesaage key: `msg_activecode_play_audio` +Since messages for different components may be loaded in the same web page context, to avoid name conflicts, message keys should folow the pattern: -English is the default language. For each _resource-name_ english JavaScript file is mandatory and must define all message keys you need. For other languages you may define subset of message keys defined in the english version. +`msg_`_component-name_`_`_given-name_ -Then in your component's main script file (_component-name_`.js`) import `add_i18n_javascript` from `runestone.common.runestonedirective` and in the `setup()` function add the line -`add_i18n_javascript(app,`_supported-languages_`,`_resource-name_`)` +For exemple: `msg_activecode_play_audio` + +The default language is English. For each _message-bunlde-name_ there should be English message loading script with all message keys defines. For any other language you may define just a subset of those message keys. + +Configure I18N +-------------- + +In your component's main script file (_component-name_`.py`): +- import `add_i18n_javascript` from `runestone.common.runestonedirective` +- in the `setup()` function add the call +`add_i18n_javascript(app,`_supported-languages_`,`_message-bunlde-name_`)` before adding any other JavaScript that use i18n messages. For instance: `add_i18n_javascript(app, {"sr-Cyrl"}, "activecode-i18n")` Since English language support is mandatory, it is not necessary to specify it in _supported-languages_. -You may group your messages in more files, and use: -`add_i18n_javascript(app,`_supported-languages_`,`_resource-name1_`,`_resource-name2_ ...`)` +You may group your messages in different message bundles: +`add_i18n_javascript(app,`_supported-languages_`,`_message-bunlde-name1_`,`_message-bunlde-name2_ ...`)` for instance: -`add_i18n_javascript(app, {"sr-Cyrl"}, "activecode-UI-i18n","activecode-compiler-i18n")` +`add_i18n_javascript(app, {"sr-Cyrl"}, "mycomponent-UI-i18n","mycomponent-compiler-i18n")` Use message keys instead of hard coded messages ----------------------------------------------- -See [https://github.com/wikimedia/jquery.i18n#jqueryi18n-plugin](https://github.com/wikimedia/jquery.i18n#jqueryi18n-plugin) +Now you may use defined message keys in your component's front-end code (JavaScript and HTML). For more details see [https://github.com/wikimedia/jquery.i18n#jqueryi18n-plugin](https://github.com/wikimedia/jquery.i18n#jqueryi18n-plugin). + + For book authors ---------------- -To select language, you should assign `language` value in `conf.py` to contain apropriate language code. +In `conf.py` of your book project, you should just set the `language` variable to an apropriate language code. diff --git a/runestone/activecode/activecode.py b/runestone/activecode/activecode.py index 1e9d19cfc..1516ce83b 100644 --- a/runestone/activecode/activecode.py +++ b/runestone/activecode/activecode.py @@ -22,7 +22,8 @@ from .textfield import * from sqlalchemy import Table from runestone.server.componentdb import addQuestionToDB, addHTMLToDB, engine, meta -from runestone.common.runestonedirective import RunestoneIdDirective, RunestoneNode, add_i18n_javascript +from runestone.common.runestonedirective import (RunestoneIdDirective, RunestoneNode, + add_i18n_js, add_codemirror_css_and_js, add_skulpt_js) try: from html import escape # py3 @@ -36,21 +37,14 @@ def setup(app): app.add_directive('activecode', ActiveCode) app.add_directive('actex', ActiveExercise) app.add_role('textfield',textfield_role) - app.add_stylesheet('codemirror.css') app.add_stylesheet('activecode.css') app.add_javascript('jquery.highlight.js') app.add_javascript('bookfuncs.js') - app.add_javascript('codemirror.js') - app.add_javascript('xml.js') - app.add_javascript('css.js') - app.add_javascript('htmlmixed.js') - app.add_javascript('python.js') - app.add_javascript('javascript.js') - add_i18n_javascript(app, {"en","sr-Cyrl"},"activecode-i18n") + add_codemirror_css_and_js(app,'xml','css','python','htmlmixex','javascript') + add_i18n_js(app, {"en","sr-Cyrl"},"activecode-i18n") + add_skulpt_js(app) app.add_javascript('activecode.js') - app.add_javascript('skulpt.min.js') - app.add_javascript('skulpt-stdlib.js') app.add_javascript('clike.js') app.add_javascript('timed_activecode.js') diff --git a/runestone/common/runestonedirective.py b/runestone/common/runestonedirective.py index 7c41a82b6..785ddb130 100644 --- a/runestone/common/runestonedirective.py +++ b/runestone/common/runestonedirective.py @@ -141,22 +141,47 @@ def run(self): id_to_page[id_] = Struct(docname=env.docname, lineno=self.lineno) page_to_id[env.docname].add(id_) +# returns True when called first time with particular parameters' values +def first_time(app, *keys): + key = '$'.join(keys) + if not hasattr(app,'runestone_flags'): + app.runestone_flags = set() + if not key in app.runestone_flags: + app.runestone_flags.add(key) + return True + return False + # An internationalized component should call add_i18n_javascript() from its setup() function -def add_i18n_javascript(app, supported_langs, *i18n_resources): - app.add_javascript('jquery_i18n/CLDRPluralRuleParser.js') - app.add_javascript('jquery_i18n/jquery.i18n.js') - app.add_javascript('jquery_i18n/jquery.i18n.messagestore.js') - app.add_javascript('jquery_i18n/jquery.i18n.fallbacks.js') - app.add_javascript('jquery_i18n/jquery.i18n.language.js') - app.add_javascript('jquery_i18n/jquery.i18n.parser.js') - app.add_javascript('jquery_i18n/jquery.i18n.emitter.js') - app.add_javascript('jquery_i18n/jquery.i18n.emitter.bidi.js') +def add_i18n_js(app, supported_langs, *i18n_resources): + if first_time(app, 'add_i18n_js'): + app.add_javascript('jquery_i18n/CLDRPluralRuleParser.js') + app.add_javascript('jquery_i18n/jquery.i18n.js') + app.add_javascript('jquery_i18n/jquery.i18n.messagestore.js') + app.add_javascript('jquery_i18n/jquery.i18n.fallbacks.js') + app.add_javascript('jquery_i18n/jquery.i18n.language.js') + app.add_javascript('jquery_i18n/jquery.i18n.parser.js') + app.add_javascript('jquery_i18n/jquery.i18n.emitter.js') + app.add_javascript('jquery_i18n/jquery.i18n.emitter.bidi.js') for res in i18n_resources: - app.add_javascript(res + ".en.js") - if app.config.language and app.config.language != "en" and app.config.language in supported_langs: - app.add_javascript(res + "." + app.config.language + ".js") - - + if(first_time(app,'add_i18n_js','key')): + app.add_javascript(res + ".en.js") + if app.config.language and app.config.language != "en" and app.config.language in supported_langs: + app.add_javascript(res + "." + app.config.language + ".js") + +# Adds CSS and JavaScript for the CodeMirror text editor +def add_codemirror_css_and_js(app, *mods): + if first_time(app, 'add_codemirror_css_and_js'): + app.add_stylesheet('codemirror.css') + app.add_javascript('codemirror.js') + for mod in mods: + if first_time(app, 'add_codemirror_css_and_js',mod): + app.add_javascript(mod + '.js') + +# Adds JavaScript for the Sculpt in-browser implementation of Python +def add_skulpt_js(app): + if first_time(app, 'add_skulpt_js'): + app.add_javascript('skulpt.min.js') + app.add_javascript('skulpt-stdlib.js') # Some nodes have a line number of None. Look through their children to find the node's line number. def get_node_line(node): diff --git a/runestone/datafile/__init__.py b/runestone/datafile/__init__.py index fba33b9af..9a0f94147 100644 --- a/runestone/datafile/__init__.py +++ b/runestone/datafile/__init__.py @@ -21,12 +21,12 @@ from docutils.parsers.rst import directives from sqlalchemy import Table from runestone.server.componentdb import engine, meta -from runestone.common.runestonedirective import RunestoneIdDirective, RunestoneNode +from runestone.common.runestonedirective import RunestoneIdDirective, RunestoneNode, add_skulpt_js def setup(app): app.add_directive('datafile',DataFile) - app.add_javascript('skulpt.min.js') - app.add_javascript('skulpt-stdlib.js') + add_skulpt_js(app) + app.add_javascript('datafile.js') app.add_stylesheet('datafile.css') diff --git a/runestone/webgldemo/webgldemo.py b/runestone/webgldemo/webgldemo.py index 9d0238382..6e7b04cca 100644 --- a/runestone/webgldemo/webgldemo.py +++ b/runestone/webgldemo/webgldemo.py @@ -20,6 +20,7 @@ import string import re import os +from runestone.common.runestonedirective import add_codemirror_css_and_js __author__ = 'wayne brown' @@ -43,16 +44,10 @@ def setup(app): # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - app.add_directive('webglinteractive', WebglInteractive) - app.add_stylesheet('codemirror.css') app.add_stylesheet('webglinteractive.css') # CodeMirror syntax highlighting for various types of code - app.add_javascript('codemirror.js') - app.add_javascript('xml.js') - app.add_javascript('css.js') - app.add_javascript('htmlmixed.js') - app.add_javascript('python.js') - app.add_javascript('javascript.js') + add_codemirror_css_and_js(app,'xml','css','htmlmixex','javascript') app.add_javascript('webglinteractive.js')