diff --git a/app/assets/javascripts/app/form-validation.js b/app/assets/javascripts/app/form-validation.js index 7d82dec7729..7d534d6e6c0 100644 --- a/app/assets/javascripts/app/form-validation.js +++ b/app/assets/javascripts/app/form-validation.js @@ -10,7 +10,7 @@ document.addEventListener('DOMContentLoaded', () => { if (input) { input.addEventListener('input', () => { if (input.validity.patternMismatch) { - input.setCustomValidity(I18n.t(`idv.errors.pattern_mismatch.${f}`)); + input.setCustomValidity(I18n.t(`idv.errors.pattern_mismatch.${I18n.key(f)}`)); } else { input.setCustomValidity(''); } diff --git a/app/assets/javascripts/misc/i18n-strings.js.erb b/app/assets/javascripts/misc/i18n-strings.js.erb index 4c5fa4c198d..32deb44529d 100644 --- a/app/assets/javascripts/misc/i18n-strings.js.erb +++ b/app/assets/javascripts/misc/i18n-strings.js.erb @@ -5,7 +5,7 @@ window.LoginGov = window.LoginGov || {}; 'errors.messages.missing_field', 'forms.passwords.show', 'idv.errors.pattern_mismatch.dob', - 'idv.errors.pattern_mismatch.personal-key', + 'idv.errors.pattern_mismatch.personal_key', 'idv.errors.pattern_mismatch.ssn', 'idv.errors.pattern_mismatch.zipcode', 'idv.modal.button.warning', @@ -16,38 +16,39 @@ window.LoginGov = window.LoginGov || {}; 'instructions.password.strength.v', 'links.remove', 'valid_email.validations.email.invalid', - 'zxcvbn.feedback.Use a few words, avoid common phrases', - 'zxcvbn.feedback.No need for symbols, digits, or uppercase letters', - 'zxcvbn.feedback.Add another word or two_ Uncommon words are better_', - 'zxcvbn.feedback.Straight rows of keys are easy to guess', - 'zxcvbn.feedback.Short keyboard patterns are easy to guess', - 'zxcvbn.feedback.Use a longer keyboard pattern with more turns', - 'zxcvbn.feedback.Repeats like "aaa" are easy to guess', - 'zxcvbn.feedback.Repeats like "abcabcabc" are only slightly harder to guess than "abc"', - 'zxcvbn.feedback.Avoid repeated words and characters', - 'zxcvbn.feedback.Sequences like abc or 6543 are easy to guess', - 'zxcvbn.feedback.Avoid sequences', - 'zxcvbn.feedback.Recent years are easy to guess', - 'zxcvbn.feedback.Avoid recent years', - 'zxcvbn.feedback.Avoid years that are associated with you', - 'zxcvbn.feedback.Dates are often easy to guess', - 'zxcvbn.feedback.Avoid dates and years that are associated with you', - 'zxcvbn.feedback.This is a top-10 common password', - 'zxcvbn.feedback.This is a top-100 common password', - 'zxcvbn.feedback.This is a very common password', - 'zxcvbn.feedback.This is similar to a commonly used password', - 'zxcvbn.feedback.A word by itself is easy to guess', - 'zxcvbn.feedback.Names and surnames by themselves are easy to guess', - 'zxcvbn.feedback.Common names and surnames are easy to guess', - 'zxcvbn.feedback.Capitalization doesn\'t help very much', - 'zxcvbn.feedback.All-uppercase is almost as easy to guess as all-lowercase', - 'zxcvbn.feedback.Reversed words aren\'t much harder to guess', - 'zxcvbn.feedback.Predictable substitutions like \'@\' instead of \'a\' don\'t help very much' + 'zxcvbn.feedback.a_word_by_itself_is_easy_to_guess', + 'zxcvbn.feedback.add_another_word_or_two_uncommon_words_are_better', + 'zxcvbn.feedback.all_uppercase_is_almost_as_easy_to_guess_as_all_lowercase', + 'zxcvbn.feedback.avoid_dates_and_years_that_are_associated_with_you', + 'zxcvbn.feedback.avoid_recent_years', + 'zxcvbn.feedback.avoid_repeated_words_and_characters', + 'zxcvbn.feedback.avoid_sequences', + 'zxcvbn.feedback.avoid_years_that_are_associated_with_you', + 'zxcvbn.feedback.capitalization_doesnt_help_very_much', + 'zxcvbn.feedback.common_names_and_surnames_are_easy_to_guess', + 'zxcvbn.feedback.dates_are_often_easy_to_guess', + 'zxcvbn.feedback.names_and_surnames_by_themselves_are_easy_to_guess', + 'zxcvbn.feedback.there_is_no_need_for_symbols_digits_or_uppercase_letters', + 'zxcvbn.feedback.predictable_substitutions_like__instead_of_a_dont_help_very_much', + 'zxcvbn.feedback.recent_years_are_easy_to_guess', + 'zxcvbn.feedback.repeats_like_aaa_are_easy_to_guess', + 'zxcvbn.feedback.repeats_like_abcabcabc_are_only_slightly_harder_to_guess_than_abc', + 'zxcvbn.feedback.reversed_words_arent_much_harder_to_guess', + 'zxcvbn.feedback.sequences_like_abc_or_6543_are_easy_to_guess', + 'zxcvbn.feedback.short_keyboard_patterns_are_easy_to_guess', + 'zxcvbn.feedback.straight_rows_of_keys_are_easy_to_guess', + 'zxcvbn.feedback.this_is_a_top_10_common_password', + 'zxcvbn.feedback.this_is_a_top_100_common_password', + 'zxcvbn.feedback.this_is_a_very_common_password', + 'zxcvbn.feedback.this_is_similar_to_a_commonly_used_password', + 'zxcvbn.feedback.for_a_stronger_password_use_a_few_words_separated_by_spaces_but_avoid_common_phrases', + 'zxcvbn.feedback.use_a_longer_keyboard_pattern_with_more_turns' ] %> window.LoginGov.I18n = { strings: {}, - t: function(key) { return this.strings[key]; } + t: function(key) { return this.strings[key]; }, + key: function(key) { return key.replace(/[ -]/g, '_').replace(/\W/g, '').toLowerCase(); } }; <% keys.each do |key| %> diff --git a/app/assets/javascripts/misc/pw-strength.js b/app/assets/javascripts/misc/pw-strength.js index ae0b444be1b..82c0a79c92a 100644 --- a/app/assets/javascripts/misc/pw-strength.js +++ b/app/assets/javascripts/misc/pw-strength.js @@ -27,8 +27,7 @@ function getFeedback(z) { const { warning, suggestions } = z.feedback; function lookup(str) { - const strFormatted = str.replace(/\./g, '_'); - return I18n.t(`zxcvbn.feedback.${strFormatted}`); + return I18n.t(`zxcvbn.feedback.${I18n.key(str)}`); } if (!warning && !suggestions.length) return ''; diff --git a/app/validators/form_password_validator.rb b/app/validators/form_password_validator.rb index 23c6b9a9a4f..6997842b5a5 100644 --- a/app/validators/form_password_validator.rb +++ b/app/validators/form_password_validator.rb @@ -40,11 +40,15 @@ def zxcvbn_feedback feedback = @pass_score.feedback.values.flatten.reject(&:empty?) feedback.map do |error| - I18n.t("zxcvbn.feedback.#{error.tr('.', '_')}") + I18n.t("zxcvbn.feedback.#{i18n_key(error)}") end.join('. ').gsub(/\.\s*\./, '.') end def password_strength_enabled? @enabled ||= FeatureManagement.password_strength_enabled? end + + def i18n_key(key) + key.tr(' -', '_').gsub(/\W/, '').downcase + end end diff --git a/config/locales/idv/en.yml b/config/locales/idv/en.yml index fd3104718a8..50dac2ba93e 100644 --- a/config/locales/idv/en.yml +++ b/config/locales/idv/en.yml @@ -30,7 +30,7 @@ en: mail_limit_reached: You have have requested too much mail in the last month. pattern_mismatch: dob: Your date of birth must be entered in as mm/dd/yyyy - "personal-key": > + personal_key: > Please enter your personal key for this account. Example: ABC1-DEF2-G3HI-J456 ssn: 'Your Social Security Number must be entered in as ###-##-####' zipcode: 'Your zipcode must be entered in as #####-####' diff --git a/config/locales/idv/es.yml b/config/locales/idv/es.yml index efb28f903ff..b54e1fbce07 100644 --- a/config/locales/idv/es.yml +++ b/config/locales/idv/es.yml @@ -26,7 +26,7 @@ es: mail_limit_reached: NOT TRANSLATED YET pattern_mismatch: dob: NOT TRANSLATED YET - "personal-key": NOT TRANSLATED YET + personal_key: NOT TRANSLATED YET ssn: NOT TRANSLATED YET zipcode: NOT TRANSLATED YET form: diff --git a/config/locales/zxcvbn/en.yml b/config/locales/zxcvbn/en.yml index 05e228179f1..7c530bb8064 100644 --- a/config/locales/zxcvbn/en.yml +++ b/config/locales/zxcvbn/en.yml @@ -2,39 +2,46 @@ en: zxcvbn: feedback: - "A word by itself is easy to guess": A word by itself is easy to guess - "Add another word or two_ Uncommon words are better_": >- - Add another word or two. Uncommon words are better - "All-uppercase is almost as easy to guess as all-lowercase": >- - All-uppercase is almost as easy to guess as all-lowercase - "Avoid dates and years that are associated with you": >- - Avoid dates and years that are associated with you - "Avoid recent years": Avoid recent years - "Avoid repeated words and characters": Avoid repeated words and characters - "Avoid sequences": Avoid sequences - "Avoid years that are associated with you": Avoid years that are associated with you - "Capitalization doesn't help very much": Capitalization doesn’t help very much - "Common names and surnames are easy to guess": Common names and surnames are easy to guess - "Dates are often easy to guess": Dates are often easy to guess - "Names and surnames by themselves are easy to guess": >- - Names and surnames by themselves are easy to guess - "No need for symbols, digits, or uppercase letters": >- - There is no need for symbols, digits, or uppercase letters - "Predictable substitutions like '@' instead of 'a' don't help very much": - Predictable substitutions like '@' instead of 'a' don’t help very much - "Recent years are easy to guess": Recent years are easy to guess - "Repeats like \"aaa\" are easy to guess": Repeats like "aaa" are easy to guess - "Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\"": >- - Repeats like "abcabcabc" are only slightly harder to guess than "abc" - "Reversed words aren't much harder to guess": Reversed words aren’t much harder to guess - "Sequences like abc or 6543 are easy to guess": Sequences like abc or 6543 are easy to guess - "Short keyboard patterns are easy to guess": Short keyboard patterns are easy to guess - "Straight rows of keys are easy to guess": Straight rows of keys are easy to guess - "This is a top-10 common password": This is a top-10 common password - "This is a top-100 common password": This is a top-100 common password - "This is a very common password": This is a very common password - "This is similar to a commonly used password": This is similar to a commonly used password - "Use a few words, avoid common phrases": - For a stronger password, use a few words separated by spaces, but avoid common phrases - "Use a longer keyboard pattern with more turns": >- - Use a longer keyboard pattern with more turns + a_word_by_itself_is_easy_to_guess: A word by itself is easy to guess + add_another_word_or_two_uncommon_words_are_better: Add another word or two. + Uncommon words are better + all_uppercase_is_almost_as_easy_to_guess_as_all_lowercase: All-uppercase is + almost as easy to guess as all-lowercase + avoid_dates_and_years_that_are_associated_with_you: Avoid dates and years that + are associated with you + avoid_recent_years: Avoid recent years + avoid_repeated_words_and_characters: Avoid repeated words and characters + avoid_sequences: Avoid sequences + avoid_years_that_are_associated_with_you: Avoid years that are associated with + you + capitalization_doesnt_help_very_much: Capitalization doesn’t help very much + common_names_and_surnames_are_easy_to_guess: Common names and surnames are easy + to guess + dates_are_often_easy_to_guess: Dates are often easy to guess + names_and_surnames_by_themselves_are_easy_to_guess: Names and surnames by themselves + are easy to guess + there_is_no_need_for_symbols_digits_or_uppercase_letters: There is no need for + symbols, digits, or uppercase letters + predictable_substitutions_like__instead_of_a_dont_help_very_much: Predictable + substitutions like '@' instead of 'a' don’t help very much + recent_years_are_easy_to_guess: Recent years are easy to guess + repeats_like_aaa_are_easy_to_guess: Repeats like "aaa" are easy to guess + repeats_like_abcabcabc_are_only_slightly_harder_to_guess_than_abc: Repeats like + "abcabcabc" are only slightly harder to guess than "abc" + reversed_words_arent_much_harder_to_guess: Reversed words aren’t much harder + to guess + sequences_like_abc_or_6543_are_easy_to_guess: Sequences like abc or 6543 are + easy to guess + short_keyboard_patterns_are_easy_to_guess: Short keyboard patterns are easy + to guess + straight_rows_of_keys_are_easy_to_guess: Straight rows of keys are easy to guess + this_is_a_top_10_common_password: This is a top-10 common password + this_is_a_top_100_common_password: This is a top-100 common password + this_is_a_very_common_password: This is a very common password + this_is_similar_to_a_commonly_used_password: This is similar to a commonly used + password + for_a_stronger_password_use_a_few_words_separated_by_spaces_but_avoid_common_phrases: For + a stronger password, use a few words separated by spaces, but avoid common + phrases + use_a_longer_keyboard_pattern_with_more_turns: Use a longer keyboard pattern + with more turns diff --git a/config/locales/zxcvbn/es.yml b/config/locales/zxcvbn/es.yml index 086ffa24261..38be5d1fb42 100644 --- a/config/locales/zxcvbn/es.yml +++ b/config/locales/zxcvbn/es.yml @@ -2,30 +2,30 @@ es: zxcvbn: feedback: - "A word by itself is easy to guess": NOT TRANSLATED YET - "Add another word or two_ Uncommon words are better_": NOT TRANSLATED YET - "All-uppercase is almost as easy to guess as all-lowercase": NOT TRANSLATED YET - "Avoid dates and years that are associated with you": NOT TRANSLATED YET - "Avoid recent years": NOT TRANSLATED YET - "Avoid repeated words and characters": NOT TRANSLATED YET - "Avoid sequences": NOT TRANSLATED YET - "Avoid years that are associated with you": NOT TRANSLATED YET - "Capitalization doesn't help very much": NOT TRANSLATED YET - "Common names and surnames are easy to guess": NOT TRANSLATED YET - "Dates are often easy to guess": NOT TRANSLATED YET - "Names and surnames by themselves are easy to guess": NOT TRANSLATED YET - "No need for symbols, digits, or uppercase letters": NOT TRANSLATED YET - "Predictable substitutions like '@' instead of 'a' don't help very much": NOT TRANSLATED YET - "Recent years are easy to guess": NOT TRANSLATED YET - "Repeats like \"aaa\" are easy to guess": NOT TRANSLATED YET - "Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\"": NOT TRANSLATED YET - "Reversed words aren't much harder to guess": NOT TRANSLATED YET - "Sequences like abc or 6543 are easy to guess": NOT TRANSLATED YET - "Short keyboard patterns are easy to guess": NOT TRANSLATED YET - "Straight rows of keys are easy to guess": NOT TRANSLATED YET - "This is a top-10 common password": NOT TRANSLATED YET - "This is a top-100 common password": NOT TRANSLATED YET - "This is a very common password": NOT TRANSLATED YET - "This is similar to a commonly used password": NOT TRANSLATED YET - "Use a few words, avoid common phrases": NOT TRANSLATED YET - "Use a longer keyboard pattern with more turns": NOT TRANSLATED YET + a_word_by_itself_is_easy_to_guess: NOT TRANSLATED YET + add_another_word_or_two_uncommon_words_are_better: NOT TRANSLATED YET + all_uppercase_is_almost_as_easy_to_guess_as_all_lowercase: NOT TRANSLATED YET + avoid_dates_and_years_that_are_associated_with_you: NOT TRANSLATED YET + avoid_recent_years: NOT TRANSLATED YET + avoid_repeated_words_and_characters: NOT TRANSLATED YET + avoid_sequences: NOT TRANSLATED YET + avoid_years_that_are_associated_with_you: NOT TRANSLATED YET + capitalization_doesnt_help_very_much: NOT TRANSLATED YET + common_names_and_surnames_are_easy_to_guess: NOT TRANSLATED YET + dates_are_often_easy_to_guess: NOT TRANSLATED YET + names_and_surnames_by_themselves_are_easy_to_guess: NOT TRANSLATED YET + there_is_no_need_for_symbols_digits_or_uppercase_letters: NOT TRANSLATED YET + predictable_substitutions_like__instead_of_a_dont_help_very_much: NOT TRANSLATED YET + recent_years_are_easy_to_guess: NOT TRANSLATED YET + repeats_like_aaa_are_easy_to_guess: NOT TRANSLATED YET + repeats_like_abcabcabc_are_only_slightly_harder_to_guess_than_abc: NOT TRANSLATED YET + reversed_words_arent_much_harder_to_guess: NOT TRANSLATED YET + sequences_like_abc_or_6543_are_easy_to_guess: NOT TRANSLATED YET + short_keyboard_patterns_are_easy_to_guess: NOT TRANSLATED YET + straight_rows_of_keys_are_easy_to_guess: NOT TRANSLATED YET + this_is_a_top_10_common_password: NOT TRANSLATED YET + this_is_a_top_100_common_password: NOT TRANSLATED YET + this_is_a_very_common_password: NOT TRANSLATED YET + this_is_similar_to_a_commonly_used_password: NOT TRANSLATED YET + for_a_stronger_password_use_a_few_words_separated_by_spaces_but_avoid_common_phrases: NOT TRANSLATED YET + use_a_longer_keyboard_pattern_with_more_turns: NOT TRANSLATED YET diff --git a/spec/features/visitors/set_password_spec.rb b/spec/features/visitors/set_password_spec.rb index 8a518521deb..ad3efba140c 100644 --- a/spec/features/visitors/set_password_spec.rb +++ b/spec/features/visitors/set_password_spec.rb @@ -55,7 +55,7 @@ expect(page).to have_content '...' fill_in 'password_form_password', with: 'password' - expect(page).to have_content 'This is a top-10 common password' + expect(page).to have_content t('zxcvbn.feedback.this_is_a_top_10_common_password') end end @@ -92,7 +92,7 @@ click_button t('forms.buttons.continue') - expect(page).to have_content t('zxcvbn.feedback.This is a top-10 common password') + expect(page).to have_content t('zxcvbn.feedback.this_is_a_top_10_common_password') end end end diff --git a/spec/i18n_spec.rb b/spec/i18n_spec.rb index 8dd0319a723..5ab0b18e9d7 100644 --- a/spec/i18n_spec.rb +++ b/spec/i18n_spec.rb @@ -20,4 +20,34 @@ "#{unused_keys.leaves.count} unused i18n keys, run `i18n-tasks unused' to show them" ) end + + root_dir = File.expand_path(File.join(File.dirname(__FILE__), '../')) + + Dir[File.join(root_dir, '/config/locales/**/*.yml')].each do |full_path| + i18n_file = full_path.sub("#{root_dir}/", '') + + describe i18n_file do + it 'has only lower_snake_case keys' do + keys = hash_keys(YAML.load_file(full_path)) + + bad_keys = keys.reject { |key| key =~ /^[a-z0-9_.]+$/ } + + expect(bad_keys).to be_empty + end + end + end + + def hash_keys(hash, parent_keys: []) + keys = [] + + hash.each do |key, value| + if value.is_a?(Hash) + keys += hash_keys(value, parent_keys: parent_keys + [key]) + else + keys << [*parent_keys, key].join('.') + end + end + + keys + end end