Commit 3cea4698 authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-06-18

# Conflicts:
#	app/views/projects/clusters/_sidebar.html.haml
#	locale/gitlab.pot

[ci skip]
parents e1792086 52dea971
...@@ -38,14 +38,17 @@ export default { ...@@ -38,14 +38,17 @@ export default {
return this.modifiedFilesLength ? 'multi-file-modified' : ''; return this.modifiedFilesLength ? 'multi-file-modified' : '';
}, },
additionsTooltip() { additionsTooltip() {
return sprintf(n__('1 %{type} addition', '%d %{type} additions', this.addedFilesLength), { return sprintf(n__('1 %{type} addition', '%{count} %{type} additions', this.addedFilesLength), {
type: this.title.toLowerCase(), type: this.title.toLowerCase(),
count: this.addedFilesLength,
}); });
}, },
modifiedTooltip() { modifiedTooltip() {
return sprintf( return sprintf(
n__('1 %{type} modification', '%d %{type} modifications', this.modifiedFilesLength), n__('1 %{type} modification', '%{count} %{type} modifications', this.modifiedFilesLength), {
{ type: this.title.toLowerCase() }, type: this.title.toLowerCase(),
count: this.modifiedFilesLength,
},
); );
}, },
titleTooltip() { titleTooltip() {
......
- clusters_help_url = help_page_path('user/project/clusters/index.md')
- help_link_start = "<a href=\"%{url}\" target=\"_blank\" rel=\"noopener noreferrer\">".html_safe
- help_link_end = '</a>'.html_safe
%h4.prepend-top-0 %h4.prepend-top-0
= s_('ClusterIntegration|Kubernetes cluster integration') = s_('ClusterIntegration|Kubernetes cluster integration')
%p %p
= s_('ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way.') = s_('ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way.')
%p %p
<<<<<<< HEAD
- link = link_to(_('Kubernetes'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer') - link = link_to(_('Kubernetes'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
= s_('ClusterIntegration|Learn more about %{link_to_documentation}').html_safe % { link_to_documentation: link } = s_('ClusterIntegration|Learn more about %{link_to_documentation}').html_safe % { link_to_documentation: link }
%p %p
- docs_link = link_to s_('If you are setting up multiple clusters and are using Auto DevOps,|read this first'), help_page_path('topics/autodevops/index.md', anchor: 'using-multiple-kubernetes-clusters'), target: '_blank' - docs_link = link_to s_('If you are setting up multiple clusters and are using Auto DevOps,|read this first'), help_page_path('topics/autodevops/index.md', anchor: 'using-multiple-kubernetes-clusters'), target: '_blank'
= s_('ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{docs_link}.').html_safe % { docs_link: docs_link } = s_('ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{docs_link}.').html_safe % { docs_link: docs_link }
=======
= s_('ClusterIntegration|Learn more about %{help_link_start}Kubernetes%{help_link_end}.').html_safe % { help_link_start: help_link_start % { url: clusters_help_url }, help_link_end: help_link_end }
>>>>>>> upstream/master
...@@ -21,7 +21,8 @@ module Gitlab ...@@ -21,7 +21,8 @@ module Gitlab
'nl_NL' => 'Nederlands', 'nl_NL' => 'Nederlands',
'tr_TR' => 'Türkçe', 'tr_TR' => 'Türkçe',
'id_ID' => 'Bahasa Indonesia', 'id_ID' => 'Bahasa Indonesia',
'fil_PH' => 'Filipino' 'fil_PH' => 'Filipino',
'pl_PL' => 'Polski'
}.freeze }.freeze
def available_locales def available_locales
......
...@@ -3,16 +3,25 @@ module Gitlab ...@@ -3,16 +3,25 @@ module Gitlab
class MetadataEntry class MetadataEntry
attr_reader :entry_data attr_reader :entry_data
# Avoid testing too many plurals if `nplurals` was incorrectly set.
# Based on info on https://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html
# which mentions special cases for numbers ending in 2 digits
MAX_FORMS_TO_TEST = 101
def initialize(entry_data) def initialize(entry_data)
@entry_data = entry_data @entry_data = entry_data
end end
def expected_plurals def expected_forms
return nil unless plural_information return nil unless plural_information
plural_information['nplurals'].to_i plural_information['nplurals'].to_i
end end
def forms_to_test
@forms_to_test ||= [expected_forms, MAX_FORMS_TO_TEST].compact.min
end
private private
def plural_information def plural_information
......
module Gitlab module Gitlab
module I18n module I18n
class PoLinter class PoLinter
include Gitlab::Utils::StrongMemoize
attr_reader :po_path, :translation_entries, :metadata_entry, :locale attr_reader :po_path, :translation_entries, :metadata_entry, :locale
VARIABLE_REGEX = /%{\w*}|%[a-z]/.freeze VARIABLE_REGEX = /%{\w*}|%[a-z]/.freeze
...@@ -34,7 +36,7 @@ module Gitlab ...@@ -34,7 +36,7 @@ module Gitlab
end end
@translation_entries = entries.map do |entry_data| @translation_entries = entries.map do |entry_data|
Gitlab::I18n::TranslationEntry.new(entry_data, metadata_entry.expected_plurals) Gitlab::I18n::TranslationEntry.new(entry_data, metadata_entry.expected_forms)
end end
nil nil
...@@ -48,7 +50,7 @@ module Gitlab ...@@ -48,7 +50,7 @@ module Gitlab
translation_entries.each do |entry| translation_entries.each do |entry|
errors_for_entry = validate_entry(entry) errors_for_entry = validate_entry(entry)
errors[join_message(entry.msgid)] = errors_for_entry if errors_for_entry.any? errors[entry.msgid] = errors_for_entry if errors_for_entry.any?
end end
errors errors
...@@ -62,6 +64,7 @@ module Gitlab ...@@ -62,6 +64,7 @@ module Gitlab
validate_newlines(errors, entry) validate_newlines(errors, entry)
validate_number_of_plurals(errors, entry) validate_number_of_plurals(errors, entry)
validate_unescaped_chars(errors, entry) validate_unescaped_chars(errors, entry)
validate_translation(errors, entry)
errors errors
end end
...@@ -81,35 +84,39 @@ module Gitlab ...@@ -81,35 +84,39 @@ module Gitlab
end end
def validate_number_of_plurals(errors, entry) def validate_number_of_plurals(errors, entry)
return unless metadata_entry&.expected_plurals return unless metadata_entry&.expected_forms
return unless entry.translated? return unless entry.translated?
if entry.has_plural? && entry.all_translations.size != metadata_entry.expected_plurals if entry.has_plural? && entry.all_translations.size != metadata_entry.expected_forms
errors << "should have #{metadata_entry.expected_plurals} "\ errors << "should have #{metadata_entry.expected_forms} "\
"#{'translations'.pluralize(metadata_entry.expected_plurals)}" "#{'translations'.pluralize(metadata_entry.expected_forms)}"
end end
end end
def validate_newlines(errors, entry) def validate_newlines(errors, entry)
if entry.msgid_contains_newlines? if entry.msgid_has_multiple_lines?
errors << 'is defined over multiple lines, this breaks some tooling.' errors << 'is defined over multiple lines, this breaks some tooling.'
end end
if entry.plural_id_contains_newlines? if entry.plural_id_has_multiple_lines?
errors << 'plural is defined over multiple lines, this breaks some tooling.' errors << 'plural is defined over multiple lines, this breaks some tooling.'
end end
if entry.translations_contain_newlines? if entry.translations_have_multiple_lines?
errors << 'has translations defined over multiple lines, this breaks some tooling.' errors << 'has translations defined over multiple lines, this breaks some tooling.'
end end
end end
def validate_variables(errors, entry) def validate_variables(errors, entry)
if entry.has_singular_translation? if entry.has_singular_translation?
validate_variables_in_message(errors, entry.msgid, entry.msgid)
validate_variables_in_message(errors, entry.msgid, entry.singular_translation) validate_variables_in_message(errors, entry.msgid, entry.singular_translation)
end end
if entry.has_plural? if entry.has_plural?
validate_variables_in_message(errors, entry.plural_id, entry.plural_id)
entry.plural_translations.each do |translation| entry.plural_translations.each do |translation|
validate_variables_in_message(errors, entry.plural_id, translation) validate_variables_in_message(errors, entry.plural_id, translation)
end end
...@@ -117,26 +124,19 @@ module Gitlab ...@@ -117,26 +124,19 @@ module Gitlab
end end
def validate_variables_in_message(errors, message_id, message_translation) def validate_variables_in_message(errors, message_id, message_translation)
message_id = join_message(message_id)
required_variables = message_id.scan(VARIABLE_REGEX) required_variables = message_id.scan(VARIABLE_REGEX)
validate_unnamed_variables(errors, required_variables) validate_unnamed_variables(errors, required_variables)
validate_translation(errors, message_id, required_variables)
validate_variable_usage(errors, message_translation, required_variables) validate_variable_usage(errors, message_translation, required_variables)
end end
def validate_translation(errors, message_id, used_variables) def validate_translation(errors, entry)
variables = fill_in_variables(used_variables)
begin
Gitlab::I18n.with_locale(locale) do Gitlab::I18n.with_locale(locale) do
translated = if message_id.include?('|') if entry.has_plural?
FastGettext::Translation.s_(message_id) translate_plural(entry)
else else
FastGettext::Translation._(message_id) translate_singular(entry)
end end
translated % variables
end end
# `sprintf` could raise an `ArgumentError` when invalid passing something # `sprintf` could raise an `ArgumentError` when invalid passing something
...@@ -151,7 +151,71 @@ module Gitlab ...@@ -151,7 +151,71 @@ module Gitlab
# `FastGettext::Translation` could raise `ArgumentError` as subclassess # `FastGettext::Translation` could raise `ArgumentError` as subclassess
# `InvalidEncoding`, `IllegalSequence` & `InvalidCharacter` # `InvalidEncoding`, `IllegalSequence` & `InvalidCharacter`
rescue ArgumentError, TypeError, RuntimeError => e rescue ArgumentError, TypeError, RuntimeError => e
errors << "Failure translating to #{locale} with #{variables}: #{e.message}" errors << "Failure translating to #{locale}: #{e.message}"
end
def translate_singular(entry)
used_variables = entry.msgid.scan(VARIABLE_REGEX)
variables = fill_in_variables(used_variables)
translation = if entry.msgid.include?('|')
FastGettext::Translation.s_(entry.msgid)
else
FastGettext::Translation._(entry.msgid)
end
translation % variables if used_variables.any?
end
def translate_plural(entry)
used_variables = entry.plural_id.scan(VARIABLE_REGEX)
variables = fill_in_variables(used_variables)
numbers_covering_all_plurals.map do |number|
translation = FastGettext::Translation.n_(entry.msgid, entry.plural_id, number)
translation % variables if used_variables.any?
end
end
def numbers_covering_all_plurals
@numbers_covering_all_plurals ||= calculate_numbers_covering_all_plurals
end
def calculate_numbers_covering_all_plurals
required_numbers = []
discovered_indexes = []
counter = 0
while discovered_indexes.size < metadata_entry.forms_to_test && counter < Gitlab::I18n::MetadataEntry::MAX_FORMS_TO_TEST
index_for_count = index_for_pluralization(counter)
unless discovered_indexes.include?(index_for_count)
discovered_indexes << index_for_count
required_numbers << counter
end
counter += 1
end
required_numbers
end
def index_for_pluralization(counter)
# This calls the C function that defines the pluralization rule, it can
# return a boolean (`false` represents 0, `true` represents 1) or an integer
# that specifies the plural form to be used for the given number
pluralization_result = Gitlab::I18n.with_locale(locale) do
FastGettext.pluralisation_rule.call(counter)
end
case pluralization_result
when false
0
when true
1
else
pluralization_result
end end
end end
...@@ -172,14 +236,18 @@ module Gitlab ...@@ -172,14 +236,18 @@ module Gitlab
end end
def validate_unnamed_variables(errors, variables) def validate_unnamed_variables(errors, variables)
if variables.size > 1 && variables.any? { |variable_name| unnamed_variable?(variable_name) } unnamed_variables, named_variables = variables.partition { |name| unnamed_variable?(name) }
if unnamed_variables.any? && named_variables.any?
errors << 'is combining named variables with unnamed variables'
end
if unnamed_variables.size > 1
errors << 'is combining multiple unnamed variables' errors << 'is combining multiple unnamed variables'
end end
end end
def validate_variable_usage(errors, translation, required_variables) def validate_variable_usage(errors, translation, required_variables)
translation = join_message(translation)
# We don't need to validate when the message is empty. # We don't need to validate when the message is empty.
# In this case we fall back to the default, which has all the the # In this case we fall back to the default, which has all the the
# required variables. # required variables.
...@@ -205,10 +273,6 @@ module Gitlab ...@@ -205,10 +273,6 @@ module Gitlab
def validate_flags(errors, entry) def validate_flags(errors, entry)
errors << "is marked #{entry.flag}" if entry.flag errors << "is marked #{entry.flag}" if entry.flag
end end
def join_message(message)
Array(message).join
end
end end
end end
end end
...@@ -11,11 +11,11 @@ module Gitlab ...@@ -11,11 +11,11 @@ module Gitlab
end end
def msgid def msgid
entry_data[:msgid] @msgid ||= Array(entry_data[:msgid]).join
end end
def plural_id def plural_id
entry_data[:msgid_plural] @plural_id ||= Array(entry_data[:msgid_plural]).join
end end
def has_plural? def has_plural?
...@@ -23,12 +23,11 @@ module Gitlab ...@@ -23,12 +23,11 @@ module Gitlab
end end
def singular_translation def singular_translation
all_translations.first if has_singular_translation? all_translations.first.to_s if has_singular_translation?
end end
def all_translations def all_translations
@all_translations ||= entry_data.fetch_values(*translation_keys) @all_translations ||= translation_entries.map { |translation| Array(translation).join }
.reject(&:empty?)
end end
def translated? def translated?
...@@ -54,16 +53,16 @@ module Gitlab ...@@ -54,16 +53,16 @@ module Gitlab
nplurals > 1 || !has_plural? nplurals > 1 || !has_plural?
end end
def msgid_contains_newlines? def msgid_has_multiple_lines?
msgid.is_a?(Array) entry_data[:msgid].is_a?(Array)
end end
def plural_id_contains_newlines? def plural_id_has_multiple_lines?
plural_id.is_a?(Array) entry_data[:msgid_plural].is_a?(Array)
end end
def translations_contain_newlines? def translations_have_multiple_lines?
all_translations.any? { |translation| translation.is_a?(Array) } translation_entries.any? { |translation| translation.is_a?(Array) }
end end
def msgid_contains_unescaped_chars? def msgid_contains_unescaped_chars?
...@@ -84,6 +83,11 @@ module Gitlab ...@@ -84,6 +83,11 @@ module Gitlab
private private
def translation_entries
@translation_entries ||= entry_data.fetch_values(*translation_keys)
.reject(&:empty?)
end
def translation_keys def translation_keys
@translation_keys ||= entry_data.keys.select { |key| key.to_s =~ /\Amsgstr(\[\d+\])?\z/ } @translation_keys ||= entry_data.keys.select { |key| key.to_s =~ /\Amsgstr(\[\d+\])?\z/ }
end end
......
...@@ -50,6 +50,32 @@ namespace :gettext do ...@@ -50,6 +50,32 @@ namespace :gettext do
end end
end end
task :updated_check do
# Removing all pre-translated files speeds up `gettext:find` as the
# files don't need to be merged.
`rm locale/*/gitlab.po`
# `gettext:find` writes touches to temp files to `stderr` which would cause
# `static-analysis` to report failures. We can ignore these
silence_stream(STDERR) { Rake::Task['gettext:find'].invoke }
changed_files = `git diff --name-only`.lines.map(&:strip)
# reset the locale folder for potential next tasks
`git checkout -- locale`
if changed_files.include?('locale/gitlab.pot')
raise <<~MSG
Newly translated strings found, please add them to `gitlab.pot` by running:
bundle exec rake gettext:find; git checkout -- locale/*/gitlab.po;
Then commit and push the resulting changes to `locale/gitlab.pot`.
MSG
end
end
def report_errors_for_file(file, errors_for_file) def report_errors_for_file(file, errors_for_file)
puts "Errors in `#{file}`:" puts "Errors in `#{file}`:"
......
...@@ -27,6 +27,7 @@ unless Rails.env.production? ...@@ -27,6 +27,7 @@ unless Rails.env.production?
scss_lint scss_lint
flay flay
gettext:lint gettext:lint
gettext:updated_check
lint:static_verification lint:static_verification
].each do |task| ].each do |task|
pid = Process.fork do pid = Process.fork do
......
...@@ -8,8 +8,13 @@ msgid "" ...@@ -8,8 +8,13 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: gitlab 1.0.0\n" "Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
<<<<<<< HEAD
"POT-Creation-Date: 2018-06-13 10:31-0400\n" "POT-Creation-Date: 2018-06-13 10:31-0400\n"
"PO-Revision-Date: 2018-06-13 10:31-0400\n" "PO-Revision-Date: 2018-06-13 10:31-0400\n"
=======
"POT-Creation-Date: 2018-06-13 14:05+0200\n"
"PO-Revision-Date: 2018-06-13 14:05+0200\n"
>>>>>>> upstream/master
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n" "Language: \n"
...@@ -164,12 +169,12 @@ msgid "- show less" ...@@ -164,12 +169,12 @@ msgid "- show less"
msgstr "" msgstr ""
msgid "1 %{type} addition" msgid "1 %{type} addition"
msgid_plural "%d %{type} additions" msgid_plural "%{count} %{type} additions"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
msgid "1 %{type} modification" msgid "1 %{type} modification"
msgid_plural "%d %{type} modifications" msgid_plural "%{count} %{type} modifications"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
...@@ -2616,10 +2621,17 @@ msgstr "" ...@@ -2616,10 +2621,17 @@ msgstr ""
msgid "GeoNodes|Does not match the primary storage configuration" msgid "GeoNodes|Does not match the primary storage configuration"
msgstr "" msgstr ""
<<<<<<< HEAD
msgid "GeoNodes|Failed" msgid "GeoNodes|Failed"
msgstr "" msgstr ""
msgid "GeoNodes|Full" msgid "GeoNodes|Full"
=======
msgid "Gitaly Servers"
msgstr ""
msgid "Gitaly|Address"
>>>>>>> upstream/master
msgstr "" msgstr ""
msgid "GeoNodes|GitLab version" msgid "GeoNodes|GitLab version"
...@@ -3032,6 +3044,15 @@ msgstr "" ...@@ -3032,6 +3044,15 @@ msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>." msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr "" msgstr ""
msgid "ImageDiffViewer|2-up"
msgstr ""
msgid "ImageDiffViewer|Onion skin"
msgstr ""
msgid "ImageDiffViewer|Swipe"
msgstr ""
msgid "Import" msgid "Import"
msgstr "" msgstr ""
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::I18n::MetadataEntry do describe Gitlab::I18n::MetadataEntry do
describe '#expected_plurals' do describe '#expected_forms' do
it 'returns the number of plurals' do it 'returns the number of plurals' do
data = { data = {
msgid: "", msgid: "",
...@@ -22,7 +22,7 @@ describe Gitlab::I18n::MetadataEntry do ...@@ -22,7 +22,7 @@ describe Gitlab::I18n::MetadataEntry do
} }
entry = described_class.new(data) entry = described_class.new(data)
expect(entry.expected_plurals).to eq(2) expect(entry.expected_forms).to eq(2)
end end
it 'returns 0 for the POT-metadata' do it 'returns 0 for the POT-metadata' do
...@@ -45,7 +45,7 @@ describe Gitlab::I18n::MetadataEntry do ...@@ -45,7 +45,7 @@ describe Gitlab::I18n::MetadataEntry do
} }
entry = described_class.new(data) entry = described_class.new(data)
expect(entry.expected_plurals).to eq(0) expect(entry.expected_forms).to eq(0)
end end
end end
end end
This diff is collapsed.
...@@ -109,7 +109,7 @@ describe Gitlab::I18n::TranslationEntry do ...@@ -109,7 +109,7 @@ describe Gitlab::I18n::TranslationEntry do
data = { msgid: %w(hello world) } data = { msgid: %w(hello world) }
entry = described_class.new(data, 2) entry = described_class.new(data, 2)
expect(entry.msgid_contains_newlines?).to be_truthy expect(entry.msgid_has_multiple_lines?).to be_truthy
end end
end end
...@@ -118,7 +118,7 @@ describe Gitlab::I18n::TranslationEntry do ...@@ -118,7 +118,7 @@ describe Gitlab::I18n::TranslationEntry do
data = { msgid_plural: %w(hello world) } data = { msgid_plural: %w(hello world) }
entry = described_class.new(data, 2) entry = described_class.new(data, 2)
expect(entry.plural_id_contains_newlines?).to be_truthy expect(entry.plural_id_has_multiple_lines?).to be_truthy
end end
end end
...@@ -127,7 +127,7 @@ describe Gitlab::I18n::TranslationEntry do ...@@ -127,7 +127,7 @@ describe Gitlab::I18n::TranslationEntry do
data = { msgstr: %w(hello world) } data = { msgstr: %w(hello world) }
entry = described_class.new(data, 2) entry = described_class.new(data, 2)
expect(entry.translations_contain_newlines?).to be_truthy expect(entry.translations_have_multiple_lines?).to be_truthy
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment