i18n: replaced fragile extraction of JS translations from an _TM variable.

We replaced it with similar to babel functions _gettext, and _ngettext, that
will prevent UI from breaking in case of untranslated strings. This
also will allow to use babel built in extractors to fetch the translations
stored inside html and JS.
This commit is contained in:
Marcin Kuzminski 2016-07-03 23:52:22 +02:00
parent b39ba081a2
commit 605b26b460
18 changed files with 65 additions and 80 deletions

View file

@ -1,33 +1,42 @@
// translate select2 components
select2Locales = {
formatLoadMore: function(pageNumber) {
return _TM["Loading more results..."];
return _gettext("Loading more results...");
},
formatSearching: function() {
return _TM["Searching..."];
return _gettext("Searching...");
},
formatNoMatches: function() {
return _TM["No matches found"];
return _gettext("No matches found");
},
formatAjaxError: function(jqXHR, textStatus, errorThrown) {
return _TM["Loading failed"];
return _gettext("Loading failed");
},
formatMatches: function(matches) {
if (matches === 1) {
return _TM["One result is available, press enter to select it."];
return _gettext("One result is available, press enter to select it.");
}
return _TM["{0} results are available, use up and down arrow keys to navigate."].format(matches);
return _gettext("{0} results are available, use up and down arrow keys to navigate.").format(matches);
},
formatInputTooShort: function(input, min) {
var n = min - input.length;
return "Please enter {0} or more character".format(n) + (n === 1 ? "" : "s");
if (n === 1) {
return _gettext("Please enter {0} or more character").format(n);
}
return _gettext("Please enter {0} or more characters").format(n);
},
formatInputTooLong: function(input, max) {
var n = input.length - max;
return "Please delete {0} character".format(n) + (n === 1 ? "" : "s");
if (n === 1) {
return _gettext("Please delete {0} character").format(n);
}
return _gettext("Please delete {0} characters").format(n);
},
formatSelectionTooBig: function(limit) {
return "You can only select {0} item".format(limit) + (limit === 1 ? "" : "s");
if (limit === 1) {
return _gettext("You can only select {0} item").format(limit);
}
return _gettext("You can only select {0} items").format(limit);
}
};

View file

@ -84,7 +84,7 @@
return typeof response === 'string' ? $.parseJSON(response) : response;
},
showNoSuggestionNotice: false,
noSuggestionNotice: _TM['No results'],
noSuggestionNotice: _gettext('No results'),
orientation: 'bottom',
forceFixPosition: false,
replaceOnArrowKey: true

View file

@ -10,21 +10,6 @@ var AgeModule = (function () {
var show_suffix = show_suffix || true;
var short_format = short_format || false;
// alias for backward compat
var _ = function(s) {
if (_TM.hasOwnProperty(s)) {
return _TM[s];
}
return s
};
var ungettext = function (singular, plural, n) {
if (n === 1){
return _(singular)
}
return _(plural)
};
var _get_relative_delta = function(now, prevdate) {
var duration = moment.duration(moment(now).diff(prevdate));
@ -121,12 +106,12 @@ var AgeModule = (function () {
} else {
var fmt_funcs = {
'year': function(d) {return ungettext('{0} year', '{0} years', d).format(d)},
'month': function(d) {return ungettext('{0} month', '{0} months', d).format(d)},
'day': function(d) {return ungettext('{0} day', '{0} days', d).format(d)},
'hour': function(d) {return ungettext('{0} hour', '{0} hours', d).format(d)},
'minute': function(d) {return ungettext('{0} min', '{0} min', d).format(d)},
'second': function(d) {return ungettext('{0} sec', '{0} sec', d).format(d)}
'year': function(d) {return _ngettext('{0} year', '{0} years', d).format(d)},
'month': function(d) {return _ngettext('{0} month', '{0} months', d).format(d)},
'day': function(d) {return _ngettext('{0} day', '{0} days', d).format(d)},
'hour': function(d) {return _ngettext('{0} hour', '{0} hours', d).format(d)},
'minute': function(d) {return _ngettext('{0} min', '{0} min', d).format(d)},
'second': function(d) {return _ngettext('{0} sec', '{0} sec', d).format(d)}
}
}
@ -146,7 +131,7 @@ var AgeModule = (function () {
var _val = fmt_funcs[part](value);
if (future) {
if (show_suffix) {
return _('in {0}').format(_val)
return _gettext('in {0}').format(_val)
} else {
return _val
}
@ -154,7 +139,7 @@ var AgeModule = (function () {
}
else {
if (show_suffix) {
return _('{0} ago').format(_val)
return _gettext('{0} ago').format(_val)
} else {
return _val
}
@ -166,17 +151,17 @@ var AgeModule = (function () {
if (short_format) {
var datetime_tmpl = '{0}, {1}';
if (show_suffix) {
datetime_tmpl = _('{0}, {1} ago');
datetime_tmpl = _gettext('{0}, {1} ago');
if (future) {
datetime_tmpl = _('in {0}, {1}');
datetime_tmpl = _gettext('in {0}, {1}');
}
}
} else {
var datetime_tmpl = _('{0} and {1}');
var datetime_tmpl = _gettext('{0} and {1}');
if (show_suffix) {
datetime_tmpl = _('{0} and {1} ago');
datetime_tmpl = _gettext('{0} and {1} ago');
if (future) {
datetime_tmpl = _('in {0} and {1}')
datetime_tmpl = _gettext('in {0} and {1}')
}
}
}
@ -186,7 +171,7 @@ var AgeModule = (function () {
i += 1;
}
return _('just now')
return _gettext('just now')
},
createTimeComponent: function(dateTime, text) {

View file

@ -24,15 +24,6 @@ if (typeof console == "undefined" || typeof console.log == "undefined"){
console = { log: function() {} }
}
// alias for backward compat
var _tm = function(s) {
if (_TM.hasOwnProperty(s)) {
return _TM[s];
}
return s
};
// TODO: move the following function to submodules
/**
@ -148,7 +139,7 @@ var showRepoStats = function(target, data){
var td2 = document.createElement('td');
var trending_language = document.createElement('div');
var nr_files = obj.count +" "+ (obj.count === 1 ? _tm('file'): _tm('files'));
var nr_files = obj.count +" "+ _ngettext('file', 'files', obj.count);
trending_language.title = key + " " + nr_files;
@ -168,7 +159,7 @@ var showRepoStats = function(target, data){
lnk = document.createElement('a');
lnk.href = '#';
lnk.innerHTML = _tm('Show more');
lnk.innerHTML = _ngettext('Show more');
lnk.id = 'code_stats_show_more';
td.appendChild(lnk);

View file

@ -366,7 +366,7 @@ var initCommentBoxCodeMirror = function(textAreaId, triggerActions){
var actions = [
{
text: "approve",
displayText: _TM['Set status to Approved'],
displayText: _gettext('Set status to Approved'),
hint: function(CodeMirror, data, completion) {
CodeMirror.replaceRange("", completion.from || data.from,
completion.to || data.to, "complete");
@ -384,7 +384,7 @@ var initCommentBoxCodeMirror = function(textAreaId, triggerActions){
},
{
text: "reject",
displayText: _TM['Set status to Rejected'],
displayText: _gettext('Set status to Rejected'),
hint: function(CodeMirror, data, completion) {
CodeMirror.replaceRange("", completion.from || data.from,
completion.to || data.to, "complete");

View file

@ -240,7 +240,7 @@ var deleteComment = function(comment_id) {
};
var createInlineAddButton = function(tr){
var label = _TM['Add another comment'];
var label = _gettext('Add another comment');
var html_el = document.createElement('div');
$(html_el).addClass('add-comment');
html_el.innerHTML = '<span class="btn btn-secondary">{0}</span>'.format(label);
@ -458,7 +458,7 @@ var CommentForm = (function() {
};
$(this.submitForm).find(this.statusChange).select2({
placeholder: _TM['Status Review'],
placeholder: _gettext('Status Review'),
formatResult: formatResult,
formatSelection: formatSelection,
containerCssClass: "drop-menu status_box_menu",
@ -472,7 +472,7 @@ var CommentForm = (function() {
$(self.submitButton).prop('disabled', false);
}
//todo, fix this name
var placeholderText = _TM['Comment text will be set automatically based on currently selected status ({0}) ...'].format(status);
var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
self.cm.setOption('placeholder', placeholderText);
})
};
@ -586,7 +586,7 @@ var CommentForm = (function() {
}
$(this.submitButton).prop('disabled', submitState);
if (submitEvent) {
$(this.submitButton).val(_TM['Submitting...']);
$(this.submitButton).val(_gettext('Submitting...'));
} else {
$(this.submitButton).val(this.submitButtonText);
}
@ -636,7 +636,7 @@ var CommentForm = (function() {
self.setActionButtonsDisabled(true);
$(self.previewBoxSelector).addClass('unloaded');
$(self.previewBoxSelector).html(_TM['Loading ...']);
$(self.previewBoxSelector).html(_gettext('Loading ...'));
$(self.editContainer).hide();
$(self.previewContainer).show();

View file

@ -147,9 +147,9 @@ var fileBrowserListeners = function(node_list_url, url_base){
if(results.length > limit){
var truncated_count = results.length - matches_max;
if (truncated_count === 1) {
match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _TM['truncated result']));
match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _gettext('truncated result')));
} else {
match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _TM['truncated results']));
match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _gettext('truncated results')));
}
}
}
@ -158,7 +158,7 @@ var fileBrowserListeners = function(node_list_url, url_base){
$('#tbody_filtered').show();
if (match.length === 0){
match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_gettext('No matching files')));
}
$('#tbody_filtered').html(match.join(""));
}
@ -293,7 +293,7 @@ var getSelectionLink = function(e) {
anchor = '#L'+ranges[0]+'-'+ranges[1];
var link = document.createElement('a');
link.href = location.href.substring(0,location.href.indexOf('#'))+anchor;
link.innerHTML = _TM['Selection link'];
link.innerHTML = _gettext('Selection link');
hl_div.appendChild(link);
$('#codeblock').append(hl_div);

View file

@ -23,8 +23,8 @@ var onSuccessFollow = function(target){
if(f.hasClass('follow')){
f.removeClass('follow');
f.addClass('following');
f.attr('title', _TM['Stop following this repository']);
$(f).html(_TM['Unfollow']);
f.attr('title', _gettext('Stop following this repository'));
$(f).html(_gettext('Unfollow'));
if(f_cnt.length){
var cnt = Number(f_cnt.html())+1;
f_cnt.html(cnt);
@ -33,8 +33,8 @@ var onSuccessFollow = function(target){
else{
f.removeClass('following');
f.addClass('follow');
f.attr('title', _TM['Start following this repository']);
$(f).html(_TM['Follow']);
f.attr('title', _gettext('Start following this repository'));
$(f).html(_gettext('Follow'));
if(f_cnt.length){
var cnt = Number(f_cnt.html())-1;
f_cnt.html(cnt);

View file

@ -116,7 +116,7 @@ var show_changeset_tooltip = function(){
}
if(rid && !$(target).hasClass('tooltip')){
$(target).attr('id', ttid);
$(target).attr('title', _TM['loading ...']);
$(target).attr('title', _gettext('loading ...'));
TTIP.main.set_listeners(target);
TTIP.main.show_tip(e, target);
var url = pyroutes.url('changeset_info', {"repo_name":repo_name, "revision": rid});

View file

@ -101,7 +101,7 @@
elems.splice(elems.indexOf(plugin_id), 1);
auth_plugins_input.val(elems.join(','));
$(cur_button).removeClass('btn-success');
cur_button.innerHTML = _TM['disabled'];
cur_button.innerHTML = _gettext('disabled');
}
else{
if(elems.indexOf(plugin_id) == -1){
@ -109,7 +109,7 @@
}
auth_plugins_input.val(elems.join(','));
$(cur_button).addClass('btn-success');
cur_button.innerHTML = _TM['enabled'];
cur_button.innerHTML = _gettext('enabled');
}
});
</script>

View file

@ -42,8 +42,8 @@ $(document).ready(function() {
var api = datatable.api();
var total = api.page.info().recordsDisplay;
var active = datatable.fnGetFilteredData();
$('#user_count').text(_TM["{0} active out of {1} users"].format(active, total));
var _text = _gettext("{0} active out of {1} users").format(active, total);
$('#user_count').text(_text);
};
// custom filter that filters by username OR email

View file

@ -228,9 +228,9 @@
open_new_pull_request.hide();
} else {
if (selected_changes == 1) {
open_new_pull_request.html(_TM['Open new pull request for selected commit']);
open_new_pull_request.html(_gettext('Open new pull request for selected commit'));
} else if (selected_changes == 0) {
open_new_pull_request.html(_TM['Open new pull request']);
open_new_pull_request.html(_gettext('Open new pull request'));
}
open_new_pull_request.show();
}
@ -244,8 +244,8 @@
'revision': revStart+'...'+revEnd});
var link = (revStart == revEnd)
? _TM['Show selected commit __S']
: _TM['Show selected commits __S ... __E'];
? _gettext('Show selected commit __S')
: _gettext('Show selected commits __S ... __E');
link = link.replace('__S', revStart.substr(0,6));
link = link.replace('__E', revEnd.substr(0,6));

View file

@ -96,7 +96,7 @@
});
//push the typed in changeset
data.results.push({
'text': _TM['specify commit'],
'text': _gettext('specify commit'),
'children': [{
'id': query.term,
'text': query.term,

View file

@ -836,7 +836,7 @@ $(document).ready(function() {
};
var previewbox = $('#preview-box');
previewbox.addClass('unloaded');
previewbox.html(_TM['Loading ...']);
previewbox.html(_gettext('Loading ...'));
$('#edit-container').hide();
$('#preview-container').show();

View file

@ -233,7 +233,7 @@
{'repo_name': templateContext.repo_name,
'revision': state.rev, 'f_path': path, 'limit': 6});
$('#file_history_container').show();
$('#file_history_container').html('<div class="file-history-inner">{0}</div>'.format(_TM['Loading ...']));
$('#file_history_container').html('<div class="file-history-inner">{0}</div>'.format(_gettext('Loading ...')));
$.pjax({
url: url,

View file

@ -212,7 +212,7 @@
var _text = myCodeMirror.getValue();
var _renderer = possible_renderer || DEFAULT_RENDERER;
var post_data = {'text': _text, 'renderer': _renderer, 'csrf_token': CSRF_TOKEN};
$('#editor_preview').html(_TM['Loading ...']);
$('#editor_preview').html(_gettext('Loading ...'));
var url = pyroutes.url('changeset_comment_preview', {'repo_name': '${c.repo_name}'});
ajaxPOST(url, post_data, function(o){

View file

@ -173,7 +173,7 @@ $(document).ready(function(){
var _text = myCodeMirror.getValue();
var _renderer = possible_renderer || DEFAULT_RENDERER;
var post_data = {'text': _text, 'renderer': _renderer, 'csrf_token': CSRF_TOKEN};
$('#editor_preview').html(_TM['Loading ...']);
$('#editor_preview').html(_gettext('Loading ...'));
var url = pyroutes.url('changeset_comment_preview', {'repo_name': '${c.repo_name}'});
ajaxPOST(url, post_data, function(o){

View file

@ -546,7 +546,7 @@
$('#update_commits').on('click', function(e){
var isDisabled = !$(e.currentTarget).attr('disabled');
$(e.currentTarget).text(_TM['Updating...']);
$(e.currentTarget).text(_gettext('Updating...'));
$(e.currentTarget).attr('disabled', 'disabled');
if(isDisabled){
updateCommits("${c.repo_name}", "${c.pull_request.pull_request_id}");