diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -4,3 +4,4 @@ c3fe200198f5aa34cf2e4066df2881a9cefe3704 7fd5c850745e2ea821fb4406af5f4bff9b0a7526 v4.1.1 41c87da28a179953df86061d817bc35533c66dd2 v4.1.2 baaf9f5bcea3bae0ef12ae20c8b270482e62abb6 v4.2.0 +32a70c7e56844a825f61df496ee5eaf8c3c4e189 v4.2.1 diff --git a/Gruntfile.js b/Gruntfile.js --- a/Gruntfile.js +++ b/Gruntfile.js @@ -19,6 +19,7 @@ module.exports = function(grunt) { '<%= dirs.js.src %>/mousetrap.js', '<%= dirs.js.src %>/moment.js', '<%= dirs.js.src %>/appenlight-client-0.4.1.min.js', + '<%= dirs.js.src %>/i18n_utils.js', // Plugins '<%= dirs.js.src %>/plugins/jquery.pjax.js', diff --git a/docs/release-notes/release-notes-4.2.1.rst b/docs/release-notes/release-notes-4.2.1.rst new file mode 100644 --- /dev/null +++ b/docs/release-notes/release-notes-4.2.1.rst @@ -0,0 +1,14 @@ +|RCE| 4.2.1 |RNS| +----------------- + +Release Date +^^^^^^^^^^^^ + +- 2016-07-04 + +Fixes +^^^^^ + +- ui: fixed empty labels caused by missing translation of JS components +- login: fixed bad routing URL in comments when user is not logged in. +- celery: make sure to run tasks in sync mode if connection to celery is lost. diff --git a/docs/release-notes/release-notes.rst b/docs/release-notes/release-notes.rst --- a/docs/release-notes/release-notes.rst +++ b/docs/release-notes/release-notes.rst @@ -9,6 +9,7 @@ Release Notes .. toctree:: :maxdepth: 1 + release-notes-4.2.1.rst release-notes-4.2.0.rst release-notes-4.1.2.rst release-notes-4.1.1.rst diff --git a/rhodecode/public/js/rhodecode/i18n/select2/translations.js b/rhodecode/public/js/rhodecode/i18n/select2/translations.js --- a/rhodecode/public/js/rhodecode/i18n/select2/translations.js +++ b/rhodecode/public/js/rhodecode/i18n/select2/translations.js @@ -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); } }; diff --git a/rhodecode/public/js/src/i18n_utils.js b/rhodecode/public/js/src/i18n_utils.js new file mode 100644 --- /dev/null +++ b/rhodecode/public/js/src/i18n_utils.js @@ -0,0 +1,36 @@ +// # Copyright (C) 2016-2016 RhodeCode GmbH +// # +// # This program is free software: you can redistribute it and/or modify +// # it under the terms of the GNU Affero General Public License, version 3 +// # (only), as published by the Free Software Foundation. +// # +// # This program is distributed in the hope that it will be useful, +// # but WITHOUT ANY WARRANTY; without even the implied warranty of +// # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// # GNU General Public License for more details. +// # +// # You should have received a copy of the GNU Affero General Public License +// # along with this program. If not, see . +// # +// # This program is dual-licensed. If you wish to learn more about the +// # RhodeCode Enterprise Edition, including its added features, Support services, +// # and proprietary license terms, please see https://rhodecode.com/licenses/ + +i18nLog = Logger.get('i18n'); + +var _gettext = function (s) { + if (_TM.hasOwnProperty(s)) { + return _TM[s]; + } + i18nLog.error( + 'String `' + s + '` was requested but cannot be ' + + 'found in translation table'); + return s +}; + +var _ngettext = function (singular, plural, n) { + if (n === 1) { + return _gettext(singular) + } + return _gettext(plural) +}; diff --git a/rhodecode/public/js/src/plugins/jquery.autocomplete.js b/rhodecode/public/js/src/plugins/jquery.autocomplete.js --- a/rhodecode/public/js/src/plugins/jquery.autocomplete.js +++ b/rhodecode/public/js/src/plugins/jquery.autocomplete.js @@ -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 diff --git a/rhodecode/public/js/src/plugins/jquery.timeago-extension.js b/rhodecode/public/js/src/plugins/jquery.timeago-extension.js --- a/rhodecode/public/js/src/plugins/jquery.timeago-extension.js +++ b/rhodecode/public/js/src/plugins/jquery.timeago-extension.js @@ -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) { diff --git a/rhodecode/public/js/src/rhodecode.js b/rhodecode/public/js/src/rhodecode.js --- a/rhodecode/public/js/src/rhodecode.js +++ b/rhodecode/public/js/src/rhodecode.js @@ -24,15 +24,6 @@ if (typeof console == "undefined" || typ 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, dat 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, dat 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); diff --git a/rhodecode/public/js/src/rhodecode/codemirror.js b/rhodecode/public/js/src/rhodecode/codemirror.js --- a/rhodecode/public/js/src/rhodecode/codemirror.js +++ b/rhodecode/public/js/src/rhodecode/codemirror.js @@ -366,7 +366,7 @@ var initCommentBoxCodeMirror = function( 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( }, { 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"); diff --git a/rhodecode/public/js/src/rhodecode/comments.js b/rhodecode/public/js/src/rhodecode/comments.js --- a/rhodecode/public/js/src/rhodecode/comments.js +++ b/rhodecode/public/js/src/rhodecode/comments.js @@ -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 = '{0}'.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(); diff --git a/rhodecode/public/js/src/rhodecode/files.js b/rhodecode/public/js/src/rhodecode/files.js --- a/rhodecode/public/js/src/rhodecode/files.js +++ b/rhodecode/public/js/src/rhodecode/files.js @@ -147,9 +147,9 @@ var fileBrowserListeners = function(node if(results.length > limit){ var truncated_count = results.length - matches_max; if (truncated_count === 1) { - match.push('{0} {1}'.format(truncated_count, _TM['truncated result'])); + match.push('{0} {1}'.format(truncated_count, _gettext('truncated result'))); } else { - match.push('{0} {1}'.format(truncated_count, _TM['truncated results'])); + match.push('{0} {1}'.format(truncated_count, _gettext('truncated results'))); } } } @@ -158,7 +158,7 @@ var fileBrowserListeners = function(node $('#tbody_filtered').show(); if (match.length === 0){ - match.push('{0}'.format(_TM['No matching files'])); + match.push('{0}'.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); diff --git a/rhodecode/public/js/src/rhodecode/followers.js b/rhodecode/public/js/src/rhodecode/followers.js --- a/rhodecode/public/js/src/rhodecode/followers.js +++ b/rhodecode/public/js/src/rhodecode/followers.js @@ -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); diff --git a/rhodecode/public/js/src/rhodecode/tooltips.js b/rhodecode/public/js/src/rhodecode/tooltips.js --- a/rhodecode/public/js/src/rhodecode/tooltips.js +++ b/rhodecode/public/js/src/rhodecode/tooltips.js @@ -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}); diff --git a/rhodecode/templates/admin/auth/auth_settings.html b/rhodecode/templates/admin/auth/auth_settings.html --- a/rhodecode/templates/admin/auth/auth_settings.html +++ b/rhodecode/templates/admin/auth/auth_settings.html @@ -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'); } }); diff --git a/rhodecode/templates/admin/users/users.html b/rhodecode/templates/admin/users/users.html --- a/rhodecode/templates/admin/users/users.html +++ b/rhodecode/templates/admin/users/users.html @@ -42,8 +42,8 @@ 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 diff --git a/rhodecode/templates/changelog/changelog.html b/rhodecode/templates/changelog/changelog.html --- a/rhodecode/templates/changelog/changelog.html +++ b/rhodecode/templates/changelog/changelog.html @@ -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)); diff --git a/rhodecode/templates/changeset/changeset_file_comment.html b/rhodecode/templates/changeset/changeset_file_comment.html --- a/rhodecode/templates/changeset/changeset_file_comment.html +++ b/rhodecode/templates/changeset/changeset_file_comment.html @@ -151,7 +151,7 @@ ${h.form('', class_='inline-form comment-form-login', method='get')}
- ${_('You need to be logged in to comment.')} ${_('Login now')} + ${_('You need to be logged in to comment.')} ${_('Login now')}
diff --git a/rhodecode/templates/compare/compare_diff.html b/rhodecode/templates/compare/compare_diff.html --- a/rhodecode/templates/compare/compare_diff.html +++ b/rhodecode/templates/compare/compare_diff.html @@ -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, diff --git a/rhodecode/templates/debug_style/collapsable-content.html b/rhodecode/templates/debug_style/collapsable-content.html --- a/rhodecode/templates/debug_style/collapsable-content.html +++ b/rhodecode/templates/debug_style/collapsable-content.html @@ -836,7 +836,7 @@ }; var previewbox = $('#preview-box'); previewbox.addClass('unloaded'); - previewbox.html(_TM['Loading ...']); + previewbox.html(_gettext('Loading ...')); $('#edit-container').hide(); $('#preview-container').show(); diff --git a/rhodecode/templates/files/files.html b/rhodecode/templates/files/files.html --- a/rhodecode/templates/files/files.html +++ b/rhodecode/templates/files/files.html @@ -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('
{0}
'.format(_TM['Loading ...'])); + $('#file_history_container').html('
{0}
'.format(_gettext('Loading ...'))); $.pjax({ url: url, diff --git a/rhodecode/templates/files/files_add.html b/rhodecode/templates/files/files_add.html --- a/rhodecode/templates/files/files_add.html +++ b/rhodecode/templates/files/files_add.html @@ -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){ diff --git a/rhodecode/templates/files/files_edit.html b/rhodecode/templates/files/files_edit.html --- a/rhodecode/templates/files/files_edit.html +++ b/rhodecode/templates/files/files_edit.html @@ -173,7 +173,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){ diff --git a/rhodecode/templates/pullrequests/pullrequest_show.html b/rhodecode/templates/pullrequests/pullrequest_show.html --- a/rhodecode/templates/pullrequests/pullrequest_show.html +++ b/rhodecode/templates/pullrequests/pullrequest_show.html @@ -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}"); diff --git a/setup.cfg b/setup.cfg --- a/setup.cfg +++ b/setup.cfg @@ -17,7 +17,10 @@ output_file = rhodecode/i18n/rhodecode.p msgid-bugs-address = marcin@rhodecode.com copyright-holder = RhodeCode GmbH no-wrap = true -keywords = lazy_ugettext +keywords = + lazy_ugettext + _ngettext + _gettext [init_catalog] domain = rhodecode diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -207,6 +207,7 @@ setup( message_extractors={ 'rhodecode': [ ('**.py', 'python', None), + ('**.js', 'javascript', None), ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}), ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}), ('public/**', 'ignore', None),