From 8f9442fcdf9d98ea28e6abd582aad1dc2086e1bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=8C=B4=F0=9F=87=BF=F0=9F=87=A6=20Lukas=20Eipert=20?= =?UTF-8?q?=28OOO=20until=20Summit=29?= Date: Thu, 16 Aug 2018 19:03:57 +0000 Subject: Frontend: Proper gettext extraction with gettext-extractor --- app/assets/javascripts/importer_status.js | 2 +- .../javascripts/locale/ensure_single_line.js | 25 +++ app/assets/javascripts/locale/index.js | 13 +- .../sidebar/components/lock/edit_form_buttons.vue | 3 +- .../sidebar/components/lock/lock_issue_sidebar.vue | 10 +- config/initializers/gettext_rails_i18n_patch.rb | 15 ++ locale/gitlab.pot | 185 +++++++++++++++++- package.json | 2 + scripts/frontend/extract_gettext_all.js | 72 +++++++ spec/javascripts/locale/ensure_single_line_spec.js | 35 ++++ spec/javascripts/notes/mock_data.js | 2 +- spec/javascripts/vue_shared/translate_spec.js | 213 ++++++++++++++++++--- yarn.lock | 80 +++++++- 13 files changed, 606 insertions(+), 51 deletions(-) create mode 100644 app/assets/javascripts/locale/ensure_single_line.js create mode 100644 scripts/frontend/extract_gettext_all.js create mode 100644 spec/javascripts/locale/ensure_single_line_spec.js diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js index 0035d809062..eda8cdad908 100644 --- a/app/assets/javascripts/importer_status.js +++ b/app/assets/javascripts/importer_status.js @@ -87,7 +87,7 @@ class ImporterStatus { details = error.response.data.errors; } - flash(__(`An error occurred while importing project: ${details}`)); + flash(sprintf(__('An error occurred while importing project: %{details}'), { details })); }); } diff --git a/app/assets/javascripts/locale/ensure_single_line.js b/app/assets/javascripts/locale/ensure_single_line.js new file mode 100644 index 00000000000..47c52fe6c50 --- /dev/null +++ b/app/assets/javascripts/locale/ensure_single_line.js @@ -0,0 +1,25 @@ +/* eslint-disable import/no-commonjs */ + +const SPLIT_REGEX = /\s*[\r\n]+\s*/; + +/** + * + * strips newlines from strings and replaces them with a single space + * + * @example + * + * ensureSingleLine('foo \n bar') === 'foo bar' + * + * @param {String} str + * @returns {String} + */ +module.exports = function ensureSingleLine(str) { + // This guard makes the function significantly faster + if (str.includes('\n') || str.includes('\r')) { + return str + .split(SPLIT_REGEX) + .filter(s => s !== '') + .join(' '); + } + return str; +}; diff --git a/app/assets/javascripts/locale/index.js b/app/assets/javascripts/locale/index.js index 2cc5fb10027..1ae3362c4bc 100644 --- a/app/assets/javascripts/locale/index.js +++ b/app/assets/javascripts/locale/index.js @@ -1,4 +1,5 @@ import Jed from 'jed'; +import ensureSingleLine from './ensure_single_line'; import sprintf from './sprintf'; const languageCode = () => document.querySelector('html').getAttribute('lang') || 'en'; @@ -10,7 +11,7 @@ delete window.translations; @param text The text to be translated @returns {String} The translated text */ -const gettext = locale.gettext.bind(locale); +const gettext = text => locale.gettext.bind(locale)(ensureSingleLine(text)); /** Translate the text with a number @@ -23,7 +24,10 @@ const gettext = locale.gettext.bind(locale); @returns {String} Translated text with the number replaced (eg. '2 days') */ const ngettext = (text, pluralText, count) => { - const translated = locale.ngettext(text, pluralText, count).replace(/%d/g, count).split('|'); + const translated = locale + .ngettext(ensureSingleLine(text), ensureSingleLine(pluralText), count) + .replace(/%d/g, count) + .split('|'); return translated[translated.length - 1]; }; @@ -40,7 +44,7 @@ const ngettext = (text, pluralText, count) => { @returns {String} Translated context based text */ const pgettext = (keyOrContext, key) => { - const normalizedKey = key ? `${keyOrContext}|${key}` : keyOrContext; + const normalizedKey = ensureSingleLine(key ? `${keyOrContext}|${key}` : keyOrContext); const translated = gettext(normalizedKey).split('|'); return translated[translated.length - 1]; @@ -52,8 +56,7 @@ const pgettext = (keyOrContext, key) => { @param formatOptions for available options, please see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat @returns {Intl.DateTimeFormat} */ -const createDateTimeFormat = - formatOptions => Intl.DateTimeFormat(languageCode(), formatOptions); +const createDateTimeFormat = formatOptions => Intl.DateTimeFormat(languageCode(), formatOptions); export { languageCode }; export { gettext as __ }; diff --git a/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue b/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue index 5e7b8f9698f..63082654101 100644 --- a/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue +++ b/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue @@ -1,4 +1,5 @@