Add a mechanism for centralising JS translations, to avoid having to render many JS files with Jinja.

This commit is contained in:
Atira Odhner 2017-03-15 17:10:22 +00:00 committed by Dave Page
parent 2f84156dc6
commit 4445f9dd63
5 changed files with 100 additions and 1 deletions

View File

@ -0,0 +1,3 @@
define(function () {
return {}
});

View File

@ -0,0 +1,43 @@
define(["sources/translate", "translations"], function (translate, translations) {
describe("translate", function () {
describe("when there is no translation", function () {
it("returns the original string", function () {
expect(translate("something to be translated")).toEqual("something to be translated");
});
describe("when there are substitutions", function () {
it("interpolates a substitution", function () {
expect(translate("translate text for %(person)s", {"person": "Sarah"})).toEqual("translate text for Sarah")
});
it("interpolates multiple substitutions", function () {
expect(translate("translate '%(text)s' for %(person)s",
{
"text": "constitution",
"person": "Sarah"
}
)).toEqual("translate 'constitution' for Sarah")
});
});
});
describe("when there is a translation", function () {
beforeEach(function () {
translations['something to be translated'] = 'etwas zum uebersetzen';
translations['another translation for %(person)s'] = 'eine weitere Uebersetzung fuer %(person)s';
});
it("returns the translation", function () {
expect(translate("something to be translated")).toEqual("etwas zum uebersetzen");
});
describe("when there is a substitution", function () {
it("interpolates the substitution", function () {
expect(translate("another translation for %(person)s", {"person": "Sarah"}))
.toEqual("eine weitere Uebersetzung fuer Sarah");
});
});
});
});
});

View File

@ -0,0 +1,32 @@
define(["translations"], function (translations) {
/***
* This method behaves as a drop-in replacement for flask translation rendering.
* It uses the same translation file under the hood and uses flask to determine the language.
*
* ex. translate("some %(adjective)s text", {adjective: "cool"})
*
* @param {String} text
* @param {Object} substitutions
*/
return function translate(text, substitutions) {
var rawTranslation = translations[text] ? translations[text] : text;
// captures things of the form %(substitutionName)s
var substitutionGroupsRegExp = /([^%]*)%\(([^\)]+)\)s(.*)/;
var matchFound;
var interpolated = rawTranslation;
do {
matchFound = false;
interpolated = interpolated.replace(substitutionGroupsRegExp, function (_, textBeginning, substitutionName, textEnd) {
matchFound = true;
return textBeginning + substitutions[substitutionName] + textEnd;
});
} while (matchFound);
return interpolated;
};
});

View File

@ -9,7 +9,11 @@
"""A blueprint module container for keeping all submodule of type tool."""
from flask import render_template, Response
from flask import url_for
from flask.ext.babel import get_translations
from flask_babel import gettext
from pgadmin.utils import PgAdminModule
from pgadmin.utils.ajax import bad_request
@ -17,8 +21,11 @@ MODULE_NAME = 'tools'
class ToolsModule(PgAdminModule):
def get_own_javascripts(self):
from flask import url_for
return [{
'name': 'translations',
'path': url_for('tools.index') + "translations",
'when': None
},{
'name': 'pgadmin-sqlfoldcode',
'path': url_for(
'static',
@ -49,3 +56,13 @@ blueprint = ToolsModule(MODULE_NAME, __name__)
def index():
"""Calling tools index URL directly is not allowed."""
return bad_request(gettext('This URL cannot be requested directly.'))
@blueprint.route("/translations.js")
def translations():
"""Return a js file that will handle translations so Flask interpolation can be isolated"""
template = render_template("js/translations.js", translations=get_translations()._catalog)
return Response(
response=template,
status=200,
mimetype="application/javascript"
)

View File

@ -0,0 +1,4 @@
define(function () {
var translations = {{ translations|tojson }};
return translations;
});