diff --git a/.jshintignore b/.jshintignore index cd0d011354dc38e0a64c746b351e74ae98cd24c6..5b3cf6d8c9c7291f5fe09db0b175dcf717dc7c18 100644 --- a/.jshintignore +++ b/.jshintignore @@ -1,2 +1,4 @@ **/vendor node_modules +cms/static/js/i18n/**/*.js +lms/static/js/i18n/**/*.js diff --git a/cms/envs/common.py b/cms/envs/common.py index 359d3c3dc8fa3c5f5ec0ab33033fccdf9ac2d970..7760d27e4338f58fd8b567467845d5c0424bff04 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -60,6 +60,8 @@ from lms.envs.common import ( # Django REST framework configuration REST_FRAMEWORK, + + STATICI18N_OUTPUT_DIR ) from path import Path as path from warnings import simplefilter @@ -465,6 +467,8 @@ LANGUAGE_DICT = dict(LANGUAGES) USE_I18N = True USE_L10N = True +STATICI18N_ROOT = PROJECT_ROOT / "static" + # Localization strings (e.g. django.po) are under this directory LOCALE_PATHS = (REPO_ROOT + '/conf/locale',) # edx-platform/conf/locale/ @@ -845,6 +849,9 @@ INSTALLED_APPS = ( # edx-milestones service 'milestones', + + # Static i18n support + 'statici18n', ) diff --git a/cms/static/cms/js/require-config.js b/cms/static/cms/js/require-config.js index 4ceb2635bd7954d29ee2a5128d7299d8ef86ba5d..167ed7617488b0dbb6716c7b54496087f2d5be77 100644 --- a/cms/static/cms/js/require-config.js +++ b/cms/static/cms/js/require-config.js @@ -1,289 +1,303 @@ -if (window) { - // MathJax Fast Preview was introduced in 2.5. However, it - // causes undesirable flashing/font size changes when - // MathJax is used for interactive preview (equation editor). - // Setting processSectionDelay to 0 (see below) fully eliminates - // fast preview, but to reduce confusion, we are also setting - // the option as displayed in the context menu to false. - // When upgrading to 2.6, check if this variable name changed. - window.MathJax = { - menuSettings: {CHTMLpreview: false} - }; -} +;(function (require, define) { + 'use strict'; + if (window) { + // MathJax Fast Preview was introduced in 2.5. However, it + // causes undesirable flashing/font size changes when + // MathJax is used for interactive preview (equation editor). + // Setting processSectionDelay to 0 (see below) fully eliminates + // fast preview, but to reduce confusion, we are also setting + // the option as displayed in the context menu to false. + // When upgrading to 2.6, check if this variable name changed. + window.MathJax = { + menuSettings: {CHTMLpreview: false} + }; + // Since we are serving the gettext catalog as static files, + // the URL for the gettext file will vary depending on which locale + // needs to be served. To handle this, we load the correct file in the + // rendered template and then use this to ensure that RequireJS knows + // how to find it. + define("gettext", function () { return window.gettext; }); + } -require.config({ - // NOTE: baseUrl has been previously set in cms/static/templates/base.html - waitSeconds: 60, - paths: { - "domReady": "js/vendor/domReady", - "gettext": "/i18n", - "mustache": "js/vendor/mustache", - "codemirror": "js/vendor/codemirror-compressed", - "codemirror/stex": "js/vendor/CodeMirror/stex", - "jquery": "js/vendor/jquery.min", - "jquery.ui": "js/vendor/jquery-ui.min", - "jquery.form": "js/vendor/jquery.form", - "jquery.markitup": "js/vendor/markitup/jquery.markitup", - "jquery.leanModal": "js/vendor/jquery.leanModal", - "jquery.ajaxQueue": "js/vendor/jquery.ajaxQueue", - "jquery.smoothScroll": "js/vendor/jquery.smooth-scroll.min", - "jquery.timepicker": "js/vendor/timepicker/jquery.timepicker", - "jquery.cookie": "js/vendor/jquery.cookie", - "jquery.qtip": "js/vendor/jquery.qtip.min", - "jquery.scrollTo": "js/vendor/jquery.scrollTo-1.4.2-min", - "jquery.flot": "js/vendor/flot/jquery.flot.min", - "jquery.fileupload": "js/vendor/jQuery-File-Upload/js/jquery.fileupload", - "jquery.fileupload-process": "js/vendor/jQuery-File-Upload/js/jquery.fileupload-process", - "jquery.fileupload-validate": "js/vendor/jQuery-File-Upload/js/jquery.fileupload-validate", - "jquery.iframe-transport": "js/vendor/jQuery-File-Upload/js/jquery.iframe-transport", - "jquery.inputnumber": "js/vendor/html5-input-polyfills/number-polyfill", - "jquery.immediateDescendents": "coffee/src/jquery.immediateDescendents", - "datepair": "js/vendor/timepicker/datepair", - "date": "js/vendor/date", - "moment": "js/vendor/moment.min", - "moment-with-locales": "js/vendor/moment-with-locales.min", - "text": 'js/vendor/requirejs/text', - "underscore": "js/vendor/underscore-min", - "underscore.string": "js/vendor/underscore.string.min", - "backbone": "js/vendor/backbone-min", - "backbone-relational" : "js/vendor/backbone-relational.min", - "backbone.associations": "js/vendor/backbone-associations-min", - "backbone.paginator": "js/vendor/backbone.paginator.min", - "tinymce": "js/vendor/tinymce/js/tinymce/tinymce.full.min", - "jquery.tinymce": "js/vendor/tinymce/js/tinymce/jquery.tinymce.min", - "xmodule": "/xmodule/xmodule", - "xblock/core": "js/xblock/core", - "xblock": "coffee/src/xblock", - "utility": "js/src/utility", - "accessibility": "js/src/accessibility_tools", - "draggabilly": "js/vendor/draggabilly.pkgd", - "URI": "js/vendor/URI.min", - "ieshim": "js/src/ie_shim", - "tooltip_manager": "js/src/tooltip_manager", + require.config({ + // NOTE: baseUrl has been previously set in cms/static/templates/base.html + waitSeconds: 60, + paths: { + "domReady": "js/vendor/domReady", + "mustache": "js/vendor/mustache", + "codemirror": "js/vendor/codemirror-compressed", + "codemirror/stex": "js/vendor/CodeMirror/stex", + "jquery": "js/vendor/jquery.min", + "jquery.ui": "js/vendor/jquery-ui.min", + "jquery.form": "js/vendor/jquery.form", + "jquery.markitup": "js/vendor/markitup/jquery.markitup", + "jquery.leanModal": "js/vendor/jquery.leanModal", + "jquery.ajaxQueue": "js/vendor/jquery.ajaxQueue", + "jquery.smoothScroll": "js/vendor/jquery.smooth-scroll.min", + "jquery.timepicker": "js/vendor/timepicker/jquery.timepicker", + "jquery.cookie": "js/vendor/jquery.cookie", + "jquery.qtip": "js/vendor/jquery.qtip.min", + "jquery.scrollTo": "js/vendor/jquery.scrollTo-1.4.2-min", + "jquery.flot": "js/vendor/flot/jquery.flot.min", + "jquery.fileupload": "js/vendor/jQuery-File-Upload/js/jquery.fileupload", + "jquery.fileupload-process": "js/vendor/jQuery-File-Upload/js/jquery.fileupload-process", + "jquery.fileupload-validate": "js/vendor/jQuery-File-Upload/js/jquery.fileupload-validate", + "jquery.iframe-transport": "js/vendor/jQuery-File-Upload/js/jquery.iframe-transport", + "jquery.inputnumber": "js/vendor/html5-input-polyfills/number-polyfill", + "jquery.immediateDescendents": "coffee/src/jquery.immediateDescendents", + "datepair": "js/vendor/timepicker/datepair", + "date": "js/vendor/date", + "moment": "js/vendor/moment.min", + "moment-with-locales": "js/vendor/moment-with-locales.min", + "text": 'js/vendor/requirejs/text', + "underscore": "js/vendor/underscore-min", + "underscore.string": "js/vendor/underscore.string.min", + "backbone": "js/vendor/backbone-min", + "backbone-relational" : "js/vendor/backbone-relational.min", + "backbone.associations": "js/vendor/backbone-associations-min", + "backbone.paginator": "js/vendor/backbone.paginator.min", + "tinymce": "js/vendor/tinymce/js/tinymce/tinymce.full.min", + "jquery.tinymce": "js/vendor/tinymce/js/tinymce/jquery.tinymce.min", + "xmodule": "/xmodule/xmodule", + "xblock/core": "js/xblock/core", + "xblock": "coffee/src/xblock", + "utility": "js/src/utility", + "accessibility": "js/src/accessibility_tools", + "draggabilly": "js/vendor/draggabilly.pkgd", + "URI": "js/vendor/URI.min", + "ieshim": "js/src/ie_shim", + "tooltip_manager": "js/src/tooltip_manager", - // Files needed for Annotations feature - "annotator": "js/vendor/ova/annotator-full", - "annotator-harvardx": "js/vendor/ova/annotator-full-firebase-auth", - "video.dev": "js/vendor/ova/video.dev", - "vjs.youtube": 'js/vendor/ova/vjs.youtube', - "rangeslider": 'js/vendor/ova/rangeslider', - "share-annotator": 'js/vendor/ova/share-annotator', - "richText-annotator": 'js/vendor/ova/richText-annotator', - "reply-annotator": 'js/vendor/ova/reply-annotator', - "grouping-annotator": 'js/vendor/ova/grouping-annotator', - "tags-annotator": 'js/vendor/ova/tags-annotator', - "diacritic-annotator": 'js/vendor/ova/diacritic-annotator', - "flagging-annotator": 'js/vendor/ova/flagging-annotator', - "jquery-Watch": 'js/vendor/ova/jquery-Watch', - "openseadragon": 'js/vendor/ova/openseadragon', - "osda": 'js/vendor/ova/OpenSeaDragonAnnotation', - "ova": 'js/vendor/ova/ova', - "catch": 'js/vendor/ova/catch/js/catch', - "handlebars": 'js/vendor/ova/catch/js/handlebars-1.1.2', - // end of Annotation tool files + // Files needed for Annotations feature + "annotator": "js/vendor/ova/annotator-full", + "annotator-harvardx": "js/vendor/ova/annotator-full-firebase-auth", + "video.dev": "js/vendor/ova/video.dev", + "vjs.youtube": 'js/vendor/ova/vjs.youtube', + "rangeslider": 'js/vendor/ova/rangeslider', + "share-annotator": 'js/vendor/ova/share-annotator', + "richText-annotator": 'js/vendor/ova/richText-annotator', + "reply-annotator": 'js/vendor/ova/reply-annotator', + "grouping-annotator": 'js/vendor/ova/grouping-annotator', + "tags-annotator": 'js/vendor/ova/tags-annotator', + "diacritic-annotator": 'js/vendor/ova/diacritic-annotator', + "flagging-annotator": 'js/vendor/ova/flagging-annotator', + "jquery-Watch": 'js/vendor/ova/jquery-Watch', + "openseadragon": 'js/vendor/ova/openseadragon', + "osda": 'js/vendor/ova/OpenSeaDragonAnnotation', + "ova": 'js/vendor/ova/ova', + "catch": 'js/vendor/ova/catch/js/catch', + "handlebars": 'js/vendor/ova/catch/js/handlebars-1.1.2', + // end of Annotation tool files - // externally hosted files - "mathjax": "//cdn.mathjax.org/mathjax/2.5-latest/MathJax.js?config=TeX-MML-AM_HTMLorMML-full&delayStartupUntil=configured", - "youtube": [ - // youtube URL does not end in ".js". We add "?noext" to the path so - // that require.js adds the ".js" to the query component of the URL, - // and leaves the path component intact. - "//www.youtube.com/player_api?noext", - // if youtube fails to load, fallback on a local file - // so that require doesn't fall over - "js/src/youtube_fallback" - ] - }, - shim: { - "gettext": { - exports: "gettext" - }, - "date": { - exports: "Date" - }, - "jquery.ui": { - deps: ["jquery"], - exports: "jQuery.ui" - }, - "jquery.form": { - deps: ["jquery"], - exports: "jQuery.fn.ajaxForm" - }, - "jquery.markitup": { - deps: ["jquery"], - exports: "jQuery.fn.markitup" - }, - "jquery.leanmodal": { - deps: ["jquery"], - exports: "jQuery.fn.leanModal" - }, - "jquery.ajaxQueue": { - deps: ["jquery"], - exports: "jQuery.fn.ajaxQueue" - }, - "jquery.smoothScroll": { - deps: ["jquery"], - exports: "jQuery.fn.smoothScroll" - }, - "jquery.cookie": { - deps: ["jquery"], - exports: "jQuery.fn.cookie" - }, - "jquery.qtip": { - deps: ["jquery"], - exports: "jQuery.fn.qtip" - }, - "jquery.scrollTo": { - deps: ["jquery"], - exports: "jQuery.fn.scrollTo", - }, - "jquery.flot": { - deps: ["jquery"], - exports: "jQuery.fn.plot" - }, - "jquery.fileupload": { - deps: ["jquery.ui", "jquery.iframe-transport"], - exports: "jQuery.fn.fileupload" - }, - "jquery.fileupload-process": { - deps: ["jquery.fileupload"] - }, - "jquery.fileupload-validate": { - deps: ["jquery.fileupload"] - }, - "jquery.inputnumber": { - deps: ["jquery"], - exports: "jQuery.fn.inputNumber" - }, - "jquery.tinymce": { - deps: ["jquery", "tinymce"], - exports: "jQuery.fn.tinymce" - }, - "datepair": { - deps: ["jquery.ui", "jquery.timepicker"] - }, - "underscore": { - exports: "_" - }, - "backbone": { - deps: ["underscore", "jquery"], - exports: "Backbone" - }, - "backbone.associations": { - deps: ["backbone"], - exports: "Backbone.Associations" - }, - "backbone.paginator": { - deps: ["backbone"], - exports: "Backbone.Paginator" - }, - "youtube": { - exports: "YT" - }, - "codemirror": { - exports: "CodeMirror" - }, - "codemirror/stex": { - deps: ["codemirror"] - }, - "tinymce": { - exports: "tinymce" - }, - "mathjax": { - exports: "MathJax", - init: function() { - MathJax.Hub.Config({ - tex2jax: { - inlineMath: [ - ["\\(","\\)"], - ['[mathjaxinline]','[/mathjaxinline]'] - ], - displayMath: [ - ["\\[","\\]"], - ['[mathjax]','[/mathjax]'] - ] + // externally hosted files + "mathjax": "//cdn.mathjax.org/mathjax/2.5-latest/MathJax.js?config=TeX-MML-AM_HTMLorMML-full&delayStartupUntil=configured", // jshint ignore:line + "youtube": [ + // youtube URL does not end in ".js". We add "?noext" to the path so + // that require.js adds the ".js" to the query component of the URL, + // and leaves the path component intact. + "//www.youtube.com/player_api?noext", + // if youtube fails to load, fallback on a local file + // so that require doesn't fall over + "js/src/youtube_fallback" + ] + }, + shim: { + "gettext": { + exports: "gettext" + }, + "date": { + exports: "Date" + }, + "jquery.ui": { + deps: ["jquery"], + exports: "jQuery.ui" + }, + "jquery.form": { + deps: ["jquery"], + exports: "jQuery.fn.ajaxForm" + }, + "jquery.markitup": { + deps: ["jquery"], + exports: "jQuery.fn.markitup" + }, + "jquery.leanmodal": { + deps: ["jquery"], + exports: "jQuery.fn.leanModal" + }, + "jquery.ajaxQueue": { + deps: ["jquery"], + exports: "jQuery.fn.ajaxQueue" + }, + "jquery.smoothScroll": { + deps: ["jquery"], + exports: "jQuery.fn.smoothScroll" + }, + "jquery.cookie": { + deps: ["jquery"], + exports: "jQuery.fn.cookie" + }, + "jquery.qtip": { + deps: ["jquery"], + exports: "jQuery.fn.qtip" + }, + "jquery.scrollTo": { + deps: ["jquery"], + exports: "jQuery.fn.scrollTo", + }, + "jquery.flot": { + deps: ["jquery"], + exports: "jQuery.fn.plot" + }, + "jquery.fileupload": { + deps: ["jquery.ui", "jquery.iframe-transport"], + exports: "jQuery.fn.fileupload" + }, + "jquery.fileupload-process": { + deps: ["jquery.fileupload"] + }, + "jquery.fileupload-validate": { + deps: ["jquery.fileupload"] + }, + "jquery.inputnumber": { + deps: ["jquery"], + exports: "jQuery.fn.inputNumber" + }, + "jquery.tinymce": { + deps: ["jquery", "tinymce"], + exports: "jQuery.fn.tinymce" + }, + "datepair": { + deps: ["jquery.ui", "jquery.timepicker"] + }, + "underscore": { + exports: "_" + }, + "backbone": { + deps: ["underscore", "jquery"], + exports: "Backbone" + }, + "backbone.associations": { + deps: ["backbone"], + exports: "Backbone.Associations" + }, + "backbone.paginator": { + deps: ["backbone"], + exports: "Backbone.Paginator" + }, + "youtube": { + exports: "YT" + }, + "codemirror": { + exports: "CodeMirror" + }, + "codemirror/stex": { + deps: ["codemirror"] + }, + "tinymce": { + exports: "tinymce" + }, + "mathjax": { + exports: "MathJax", + init: function() { + window.MathJax.Hub.Config({ + tex2jax: { + inlineMath: [ + ["\\(","\\)"], + ['[mathjaxinline]','[/mathjaxinline]'] + ], + displayMath: [ + ["\\[","\\]"], + ['[mathjax]','[/mathjax]'] + ] + } + }); + // In order to eliminate all flashing during interactive + // preview, it is necessary to set processSectionDelay to 0 + // (remove delay between input and output phases). This + // effectively disables fast preview, regardless of + // the fast preview setting as shown in the context menu. + window.MathJax.Hub.processSectionDelay = 0; + window.MathJax.Hub.Configured(); } - }); - // In order to eliminate all flashing during interactive - // preview, it is necessary to set processSectionDelay to 0 - // (remove delay between input and output phases). This - // effectively disables fast preview, regardless of - // the fast preview setting as shown in the context menu. - MathJax.Hub.processSectionDelay = 0; - MathJax.Hub.Configured(); - } - }, - "URI": { - exports: "URI" - }, - "tooltip_manager": { - deps: ["jquery", "underscore"] - }, - "jquery.immediateDescendents": { - deps: ["jquery"] - }, - "xblock/core": { - exports: "XBlock", - deps: ["jquery", "jquery.immediateDescendents"] - }, - "xblock/runtime.v1": { - exports: "XBlock", - deps: ["xblock/core"] - }, + }, + "URI": { + exports: "URI" + }, + "tooltip_manager": { + deps: ["jquery", "underscore"] + }, + "jquery.immediateDescendents": { + deps: ["jquery"] + }, + "xblock/core": { + exports: "XBlock", + deps: ["jquery", "jquery.immediateDescendents"] + }, + "xblock/runtime.v1": { + exports: "XBlock", + deps: ["xblock/core"] + }, - "coffee/src/main": { - deps: ["coffee/src/ajax_prefix"] - }, - "js/src/logger": { - exports: "Logger", - deps: ["coffee/src/ajax_prefix"] - }, + "coffee/src/main": { + deps: ["coffee/src/ajax_prefix"] + }, + "js/src/logger": { + exports: "Logger", + deps: ["coffee/src/ajax_prefix"] + }, - // the following are all needed for annotation tools - "video.dev": { - exports:"videojs" - }, - "vjs.youtube": { - deps: ["video.dev"] - }, - "rangeslider": { - deps: ["video.dev"] - }, - "annotator": { - exports: "Annotator" - }, - "annotator-harvardx":{ - deps: ["annotator"] - }, - "share-annotator": { - deps: ["annotator"] - }, - "richText-annotator": { - deps: ["annotator", "tinymce"] - }, - "reply-annotator": { - deps: ["annotator"] - }, - "tags-annotator": { - deps: ["annotator"] - }, - "diacritic-annotator": { - deps: ["annotator"] - }, - "flagging-annotator": { - deps: ["annotator"] - }, - "grouping-annotator": { - deps: ["annotator"] - }, - "ova":{ - exports: "ova", - deps: ["annotator", "annotator-harvardx", "video.dev", "vjs.youtube", "rangeslider", "share-annotator", "richText-annotator", "reply-annotator", "tags-annotator", "flagging-annotator", "grouping-annotator", "diacritic-annotator", "jquery-Watch", "catch", "handlebars", "URI"] - }, - "osda":{ - exports: "osda", - deps: ["annotator", "annotator-harvardx", "video.dev", "vjs.youtube", "rangeslider", "share-annotator", "richText-annotator", "reply-annotator", "tags-annotator", "flagging-annotator", "grouping-annotator", "diacritic-annotator", "openseadragon", "jquery-Watch", "catch", "handlebars", "URI"] + // the following are all needed for annotation tools + "video.dev": { + exports:"videojs" + }, + "vjs.youtube": { + deps: ["video.dev"] + }, + "rangeslider": { + deps: ["video.dev"] + }, + "annotator": { + exports: "Annotator" + }, + "annotator-harvardx":{ + deps: ["annotator"] + }, + "share-annotator": { + deps: ["annotator"] + }, + "richText-annotator": { + deps: ["annotator", "tinymce"] + }, + "reply-annotator": { + deps: ["annotator"] + }, + "tags-annotator": { + deps: ["annotator"] + }, + "diacritic-annotator": { + deps: ["annotator"] + }, + "flagging-annotator": { + deps: ["annotator"] + }, + "grouping-annotator": { + deps: ["annotator"] + }, + "ova":{ + exports: "ova", + deps: ["annotator", "annotator-harvardx", "video.dev", "vjs.youtube", + "rangeslider", "share-annotator", "richText-annotator", "reply-annotator", + "tags-annotator", "flagging-annotator", "grouping-annotator", "diacritic-annotator", + "jquery-Watch", "catch", "handlebars", "URI"] + }, + "osda":{ + exports: "osda", + deps: ["annotator", "annotator-harvardx", "video.dev", "vjs.youtube", + "rangeslider", "share-annotator", "richText-annotator", "reply-annotator", + "tags-annotator", "flagging-annotator", "grouping-annotator", "diacritic-annotator", + "openseadragon", "jquery-Watch", "catch", "handlebars", "URI"] + } + // end of annotation tool files } - // end of annotation tool files - } -}); + }); +}).call(this, require, define); diff --git a/cms/templates/base.html b/cms/templates/base.html index 9bacefda13beae4a1b0b81132123ecbb901d5706..32ce03dcdd78639c8b721b488219ea7da2a58f18 100644 --- a/cms/templates/base.html +++ b/cms/templates/base.html @@ -23,6 +23,11 @@ from openedx.core.lib.js_utils import ( ${settings.STUDIO_NAME} </title> + <% + jsi18n_path = "js/i18n/{language}/djangojs.js".format(language=LANGUAGE_CODE) + %> + + <script type="text/javascript" src="${static.url(jsi18n_path)}"></script> <meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="path_prefix" content="${EDX_ROOT_URL}"> diff --git a/cms/urls.py b/cms/urls.py index 5c3e8ed1cf09e45f335fc59984818e77720439a3..2b85e924719e5cbd885da5f71cd976271c877c42 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -122,12 +122,6 @@ JS_INFO_DICT = { 'packages': ('openassessment',), } -urlpatterns += patterns( - '', - # Serve catalog of localized strings to be rendered by Javascript - url(r'^i18n.js$', 'django.views.i18n.javascript_catalog', JS_INFO_DICT), -) - if settings.FEATURES.get('ENABLE_CONTENT_LIBRARIES'): urlpatterns += ( url(r'^library/{}?$'.format(LIBRARY_KEY_PATTERN), diff --git a/conf/locale/config.yaml b/conf/locale/config.yaml index a029efc5da92b4c3f9dd51b23a8b43ddb699a737..d2410f681088091651bd1a7b8d39949b5623ee1e 100644 --- a/conf/locale/config.yaml +++ b/conf/locale/config.yaml @@ -111,6 +111,9 @@ ignore_dirs: - '*/spec' - '*/tests' - '*/features' + # Directories full of auto-generated JS + - lms/static/js/i18n + - cms/static/js/i18n # Third-party installed apps that we also extract strings from. When adding a diff --git a/lms/envs/common.py b/lms/envs/common.py index 6ab46ebe0d244c117190ca105154053087c564d4..a1aae3015579358eeee30d372eaf9e9aa55ccdd2 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -922,6 +922,9 @@ LANGUAGE_DICT = dict(LANGUAGES) USE_I18N = True USE_L10N = True +STATICI18N_ROOT = PROJECT_ROOT / "static" +STATICI18N_OUTPUT_DIR = "js/i18n" + # Localization strings (e.g. django.po) are under this directory LOCALE_PATHS = (REPO_ROOT + '/conf/locale',) # edx-platform/conf/locale/ # Messages @@ -1943,6 +1946,9 @@ INSTALLED_APPS = ( # Gating of course content 'gating.apps.GatingConfig', + + # Static i18n support + 'statici18n', ) # Migrations which are not in the standard module "migrations" diff --git a/lms/static/lms/js/require-config.js b/lms/static/lms/js/require-config.js index acc0a3b3bcce4694f5ef6206c16761d83f6ab578..2674176e7da11a0ff7beeb2131e6ed2d98297e75 100644 --- a/lms/static/lms/js/require-config.js +++ b/lms/static/lms/js/require-config.js @@ -32,7 +32,6 @@ // NOTE: baseUrl has been previously set in lms/templates/main.html waitSeconds: 60, paths: { - "gettext": "/i18n", "annotator_1.2.9": "js/vendor/edxnotes/annotator-full.min", "date": "js/vendor/date", "moment": "js/vendor/moment.min", diff --git a/lms/templates/main.html b/lms/templates/main.html index 03c3b3f46c0ec679e4c58057ade61e794f604a85..0801da7e195a2edebf645d18db89605a5a7e5536 100644 --- a/lms/templates/main.html +++ b/lms/templates/main.html @@ -51,7 +51,11 @@ from branding import api as branding_api </script> % endif - <script type="text/javascript" src="/i18n.js"></script> + <% + jsi18n_path = "js/i18n/{language}/djangojs.js".format(language=LANGUAGE_CODE) + %> + + <script type="text/javascript" src="${static.url(jsi18n_path)}"></script> <link rel="icon" type="image/x-icon" href="${static.url(static.get_value('favicon_path', settings.FAVICON_PATH))}" /> diff --git a/lms/urls.py b/lms/urls.py index 3ea312edc252f889a69057c5ff6fb044cdbcb727..f21f8821c0b2fad700ac22effb257f5d3d4cabdf 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -138,11 +138,6 @@ js_info_dict = { 'packages': ('openassessment',), } -urlpatterns += ( - # Serve catalog of localized strings to be rendered by Javascript - url(r'^i18n.js$', 'django.views.i18n.javascript_catalog', js_info_dict), -) - # sysadmin dashboard, to see what courses are loaded, to delete & load courses if settings.FEATURES["ENABLE_SYSADMIN_DASHBOARD"]: urlpatterns += ( diff --git a/pavelib/i18n.py b/pavelib/i18n.py index cd6ed7561c8cac35e3ca3ff2559707557bb5a44b..cc7a622b2d7d56f28992387e507ae97f88ac5893 100644 --- a/pavelib/i18n.py +++ b/pavelib/i18n.py @@ -5,12 +5,15 @@ import sys import subprocess from path import Path as path from paver.easy import task, cmdopts, needs, sh +from .utils.cmd import django_cmd try: from pygments.console import colorize except ImportError: colorize = lambda color, text: text +DEFAULT_SETTINGS = 'devstack' + @task @needs( @@ -194,6 +197,7 @@ def i18n_robot_pull(): """ Pull source strings, generate po and mo files, and validate """ + # sh('paver test_i18n') # Tests were removed from repo, but there should still be tests covering the translations # TODO: Validate the recently pulled translations, and give a bail option @@ -203,10 +207,16 @@ def i18n_robot_pull(): print "\n\nValidating translations with `i18n_tool validate`..." sh("{cmd}".format(cmd=cmd)) + # Generate static i18n JS files. + for system in ['lms', 'cms']: + sh(django_cmd(system, DEFAULT_SETTINGS, 'compilejsi18n')) + con = raw_input("Continue with committing these translations (y/n)? ") if con.lower() == 'y': sh('git add conf/locale') + sh('git add cms/static/js/i18n') + sh('git add lms/static/js/i18n') sh( 'git commit --message=' diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 4ba13655e670c9938df3eb1f393496b20c825ba0..6c9d452cece2c593453c4e558435c9789c6c1989 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -26,6 +26,7 @@ django-oauth-plus==2.2.8 django-sekizai==0.8.2 django-ses==0.7.0 django-simple-history==1.6.3 +django-statici18n==1.1.5 django-storages-redux==1.3 django-method-override==0.1.0 # We need a fix to DRF 3.2.x, for now use it from our own cherry-picked repo