Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix import for unbalanced pluralizations
fixes CNVS-4533 current i18n gem doesn't like pluralization sets where you specify "other" but not "one". This pulls the i18n import into it's own class and specifically specs out the copying over of the "other" key into the "one" key in a pluralization set unless a "one" key already exists. Importing any translation set will now solve the pluralization view errors. TEST PLAN: 1) confirm that the errors specified in CNVS-4533 are still occurring for the japanese language set with the current translations. 2) run the i18n import process for the japanese translations 3) confirm that the view errors are no longer cropping up under the japanese translation set. Change-Id: Idb856bd72f4d8e526a645f70a8fcc5556c4a4f98 Reviewed-on: https://gerrit.instructure.com/18526 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Ethan Vizitei <ethan@12spokes.com> QA-Review: Clare Hetherington <clare@instructure.com>
- Loading branch information
Showing
4 changed files
with
182 additions
and
99 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,33 @@ | |||
module I18n | |||
module HashExtensions | |||
|
|||
def flatten_keys(result={}, prefix='') | |||
each_pair do |k, v| | |||
if v.is_a?(Hash) | |||
v.flatten_keys(result, "#{prefix}#{k}.") | |||
else | |||
result["#{prefix}#{k}"] = v | |||
end | |||
end | |||
result | |||
end | |||
|
|||
def expand_keys(result = {}) | |||
each_pair do |k, v| | |||
parts = k.split('.') | |||
last = parts.pop | |||
parts.inject(result){ |h, k2| h[k2] ||= {}}[last] = v | |||
end | |||
result | |||
end | |||
|
|||
def to_ordered | |||
keys.sort_by(&:to_s).inject ActiveSupport::OrderedHash.new do |h, k| | |||
v = fetch(k) | |||
h[k] = v.is_a?(Hash) ? v.to_ordered : v | |||
h | |||
end | |||
end | |||
|
|||
end | |||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,115 @@ | |||
Hash.send(:include, I18n::HashExtensions) unless Hash.new.kind_of?(I18n::HashExtensions) | |||
|
|||
class I18nImport | |||
attr_reader :source_translations, :new_translations, :language | |||
|
|||
def initialize(source_translations, new_translations) | |||
@source_translations = init_source(source_translations) | |||
@language = init_language(new_translations) | |||
@new_translations = new_translations[language].flatten_keys | |||
end | |||
|
|||
def compile_complete_translations(warning) | |||
return nil unless warning.call(missing_keys.sort, "missing translations") if missing_keys.present? | |||
return nil unless warning.call(unexpected_keys.sort, "unexpected translations") if unexpected_keys.present? | |||
|
|||
find_mismatches | |||
|
|||
if @placeholder_mismatches.size > 0 | |||
return nil unless warning.call(mismatch_diff(@placeholder_mismatches), "placeholder mismatches") | |||
end | |||
|
|||
if @markdown_mismatches.size > 0 | |||
return nil unless warning.call(mismatch_diff(@markdown_mismatches), "markdown/wrapper mismatches") | |||
end | |||
|
|||
complete_translations | |||
end | |||
|
|||
def complete_translations | |||
I18n.available_locales | |||
base = (I18n.backend.send(:translations)[language.to_sym] || {}) | |||
translations = base.flatten_keys.merge(new_translations) | |||
fix_plural_keys(translations) | |||
translations.expand_keys | |||
end | |||
|
|||
def fix_plural_keys(flat_hash) | |||
other_keys = flat_hash.keys.grep(/\.other$/) | |||
other_keys.each do |other_key| | |||
one_key = other_key.gsub(/other$/, 'one') | |||
if flat_hash[one_key].nil? | |||
flat_hash[one_key] = flat_hash[other_key] | |||
end | |||
end | |||
end | |||
|
|||
def missing_keys | |||
source_translations.keys - new_translations.keys | |||
end | |||
|
|||
def unexpected_keys | |||
new_translations.keys - source_translations.keys | |||
end | |||
|
|||
def find_mismatches | |||
@placeholder_mismatches = {} | |||
@markdown_mismatches = {} | |||
new_translations.keys.each do |key| | |||
p1 = placeholders(source_translations[key].to_s) | |||
p2 = placeholders(new_translations[key].to_s) | |||
@placeholder_mismatches[key] = [p1, p2] if p1 != p2 | |||
|
|||
m1 = markdown_and_wrappers(source_translations[key].to_s) | |||
m2 = markdown_and_wrappers(new_translations[key].to_s) | |||
@markdown_mismatches[key] = [m1, m2] if m1 != m2 | |||
end | |||
end | |||
|
|||
def markdown_and_wrappers(str) | |||
# some stuff this doesn't check (though we don't use): | |||
# blockquotes, e.g. "> some text" | |||
# reference links, e.g. "[an example][id]" | |||
# indented code | |||
( | |||
scan_and_report(str, /\\[\\`\*_\{\}\[\]\(\)#\+\-\.!]/) + | |||
scan_and_report(str, /(\*+|_+|`+)[^\s].*?[^\s]?\1/).map{|m|"#{m}-wrap"} + | |||
scan_and_report(str, /(!?\[)[^\]]+\]\(([^\)"']+).*?\)/).map{|m|"link:#{m.last}"} + | |||
scan_and_report(str, /^((\s*\*\s*){3,}|(\s*-\s*){3,}|(\s*_\s*){3,})$/).map{"hr"} + | |||
scan_and_report(str, /^[^=\-\n]+\n^(=+|-+)$/).map{|m|m.first[0]=='=' ? 'h1' : 'h2'} + | |||
scan_and_report(str, /^(\#{1,6})\s+[^#]*#*$/).map{|m|"h#{m.first.size}"} + | |||
scan_and_report(str, /^ {0,3}(\d+\.|\*|\+|\-)\s/).map{|m|m.first =~ /\d/ ? "1." : "*"} | |||
).sort | |||
end | |||
|
|||
def placeholders(str) | |||
str.scan(/%h?\{[^\}]+\}/).sort | |||
rescue ArgumentError => e | |||
puts "Unable to scan string: #{str.inspect}" | |||
raise e | |||
end | |||
|
|||
def scan_and_report(str, re) | |||
str.scan(re) | |||
rescue ArgumentError => e | |||
puts "Unable to scan string: #{str.inspect}" | |||
raise e | |||
end | |||
|
|||
private | |||
def init_source(translations) | |||
raise "Source does not have any English strings" unless translations.keys.include?('en') | |||
translations['en'].flatten_keys | |||
end | |||
|
|||
def init_language(translations) | |||
raise "Translation file contains multiple languages" if translations.size > 1 | |||
language = translations.keys.first | |||
raise "Translation file appears to have only English strings" if language == 'en' | |||
language | |||
end | |||
|
|||
def mismatch_diff(mismatches) | |||
mismatches.map{|k,(p1,p2)| "#{k}: expected #{p1.inspect}, got #{p2.inspect}"}.sort | |||
end | |||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,23 @@ | |||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb') | |||
require File.expand_path('../../lib/i18n_import', File.dirname(__FILE__)) | |||
|
|||
describe I18nImport do | |||
describe '#fix_plural_keys' do | |||
it 'copies over the other key if there is no one key' do | |||
import = I18nImport.new({'en'=>{}}, {'ja'=>{}}) | |||
hash = { 'some.key.other' => 'value' } | |||
import.fix_plural_keys(hash) | |||
hash.should == { 'some.key.other' => 'value', 'some.key.one' => 'value' } | |||
end | |||
|
|||
it 'leaves the one key alone if it already exists' do | |||
import = I18nImport.new({'en'=>{}}, {'ja'=>{}}) | |||
hash = { | |||
'some.key.other' => 'value', | |||
'some.key.one' => 'other value' | |||
} | |||
import.fix_plural_keys(hash) | |||
hash.should == { 'some.key.other' => 'value', 'some.key.one' => 'other value' } | |||
end | |||
end | |||
end |