Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ to use the Prism Scanner without Rails support.
- Loads environment variables via `dotenv` if available. [#395](https://github.com/glebm/i18n-tasks/issues/395)
- Adds CLI command `check_prism` to try the new parser out and see the differences in key detection.
- The Prism-based scanner supports candidate_keys for Rails translations, allowing relative translations in controllers to match either the key scoped to controller and action or only to the controller.
- Translation services now catch errors and save partial results [#642](https://github.com/glebm/i18n-tasks/issues/642)

## v1.0.15

Expand Down
25 changes: 22 additions & 3 deletions lib/i18n/tasks/translators/base_translator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,22 @@ def translate_forest(forest, from)

@progress_bar = ProgressBar.create(total: pairs.flatten.size, format: "%a <%B> %e %c/%C (%p%%)")

translated = translate_pairs(pairs, to: root.key, from: from)
begin
translated = translate_pairs(pairs, to: root.key, from: from)
rescue => e
warn "Translation for locale #{root.key} failed: #{e.message}"
# If translate_pairs raised, try to salvage any partial translations
# by attempting to translate each slice individually and collecting successes.
translated = []
pairs.group_by { |k_v| @i18n_tasks.html_key? k_v[0], from }.each do |_is_html, list_slice|
translated.concat(fetch_translations(list_slice, to: root.key, from: from))
rescue => e2
warn "Partial translation failed for locale #{root.key}: #{e2.message} - leaving keys untranslated"
# leave the original list_slice untranslated
translated.concat(list_slice)
end
end

result.merge! Data::Tree::Siblings.from_flat_pairs(translated)
end
end
Expand All @@ -40,6 +55,10 @@ def translate_pairs(list, opts)
list -= reference_key_vals
result = list.group_by { |k_v| @i18n_tasks.html_key? k_v[0], opts[:from] }.map do |is_html, list_slice|
fetch_translations(list_slice, opts.merge(is_html ? options_for_html : options_for_plain))
rescue => e
warn "Translation slice failed: #{e.message} - leaving slice untranslated"
# Return the original untranslated slice so already completed translations are preserved
list_slice
end.reduce(:+) || []
result.concat(reference_key_vals)
result.sort! { |a, b| key_pos[a[0]] <=> key_pos[b[0]] }
Expand Down Expand Up @@ -128,10 +147,10 @@ def replace_interpolations(value)
# @param [String] translated
# @return [String] 'hello, <round-trippable string>' => 'hello, %{name}'
def restore_interpolations(untranslated, translated)
return translated if untranslated !~ INTERPOLATION_KEY_RE
return translated if !INTERPOLATION_KEY_RE.match?(untranslated)

values = untranslated.scan(INTERPOLATION_KEY_RE)
translated.gsub(/#{Regexp.escape(UNTRANSLATABLE_STRING)}\d+/i) do |m|
translated.gsub(/#{Regexp.escape(UNTRANSLATABLE_STRING)}\d+/io) do |m|
values[m[UNTRANSLATABLE_STRING.length..].to_i]
end
rescue => e
Expand Down
58 changes: 58 additions & 0 deletions spec/translators/partial_failures_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# frozen_string_literal: true

require "spec_helper"

RSpec.describe I18n::Tasks::Translators::BaseTranslator do

Check failure on line 5 in spec/translators/partial_failures_spec.rb

View workflow job for this annotation

GitHub Actions / lint

RSpec/SpecFilePathFormat: Spec path should end with `i18n/tasks/translators/base_translator*_spec.rb`.
let(:task) { I18n::Tasks::BaseTask.new }

# Create a fake translator that raises for html slices
let(:translator_class) do
Class.new(described_class) do
def translate_values(list, **options)
if options[:html]
raise StandardError, "html translation failure"
end

# return translated values simply by appending `-es` for testing
list.map { |v| "#{v}-es" }
end

def options_for_translate_values(from:, to:, **options)
options.merge(from: from, to: to)
end

def options_for_html
{html: true}
end

def options_for_plain
{html: false}
end

def no_results_error_message
"no results"
end
end
end

it "preserves successful translations when a subsequent slice fails" do
translator = translator_class.new(task)

list = [
["common.plain", "Hello"],
["common.html.html", "<b>Hi</b>"]
]

result = translator.send(:translate_pairs, list, from: "en", to: "es")

# Find translated plain key
plain = result.assoc("common.plain")
expect(plain).not_to be_nil
expect(plain.last).to eq("Hello-es")

# HTML slice should have been left untranslated due to simulated failure
html = result.assoc("common.html.html")
expect(html).not_to be_nil
expect(html.last).to eq("<b>Hi</b>")
end
end
Loading