-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Co-authored-by: Armand Fardeau <[email protected]>
- Loading branch information
1 parent
2bedf74
commit 32224a4
Showing
19 changed files
with
481 additions
and
2 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
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
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 | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
# frozen_string_literal: true | ||
|
||
module Decidim | ||
# This job is part of the machine translation flow. This one is fired every | ||
# time a `Decidim::TranslatableResource` is created or updated. If any of the | ||
# attributes defines as translatable is modified, then for each of those | ||
# attributes this job will schedule a `Decidim::MachineTranslationFieldsJob`. | ||
class MachineTranslationResourceJob < ApplicationJob | ||
queue_as :translations | ||
|
||
# rubocop: disable Metrics/CyclomaticComplexity | ||
|
||
# Performs the job. | ||
# | ||
# resource - Any kind of `Decidim::TranslatableResource` model instance | ||
# previous_changes - A Hash with the set fo changes. This is intended to be | ||
# taken from `resource.previous_changes`, but we need to manually pass | ||
# them to the job because the value gets lost when serializing the | ||
# resource. | ||
# source_locale - A Symbol representing the source locale for the translation | ||
def perform(resource, previous_changes, source_locale) | ||
return unless Decidim.machine_translation_service_klass | ||
|
||
@resource = resource | ||
@locales_to_be_translated = [] | ||
translatable_fields = @resource.class.translatable_fields_list.map(&:to_s) | ||
translatable_fields.each do |field| | ||
next unless @resource[field].is_a?(Hash) && previous_changes.keys.include?(field) | ||
|
||
translated_locales = translated_locales_list(field) | ||
remove_duplicate_translations(field, translated_locales) if @resource[field]["machine_translations"].present? | ||
|
||
next unless default_locale_changed_or_translation_removed(previous_changes, field) | ||
|
||
@locales_to_be_translated += pending_locales(translated_locales) if @locales_to_be_translated.blank? | ||
|
||
@locales_to_be_translated.each do |target_locale| | ||
Decidim::MachineTranslationFieldsJob.perform_later( | ||
@resource, | ||
field, | ||
resource_field_value( | ||
previous_changes, | ||
field, | ||
source_locale | ||
), | ||
target_locale, | ||
source_locale | ||
) | ||
end | ||
end | ||
end | ||
# rubocop: enable Metrics/CyclomaticComplexity | ||
|
||
def default_locale_changed_or_translation_removed(previous_changes, field) | ||
default_locale = default_locale(@resource) | ||
values = previous_changes[field] | ||
old_value = values.first | ||
new_value = values.last | ||
return true unless old_value.is_a?(Hash) | ||
|
||
return true if old_value[default_locale] != new_value[default_locale] | ||
|
||
# In a case where the default locale is not changed | ||
# but a translation of a different locale is deleted | ||
# We trigger a job to translate only for that locale | ||
if old_value[default_locale] == new_value[default_locale] | ||
locales_present = old_value.keys | ||
locales_present.each do |locale| | ||
@locales_to_be_translated << locale if old_value[locale] != new_value[locale] && new_value[locale] == "" | ||
end | ||
end | ||
|
||
@locales_to_be_translated.present? | ||
end | ||
|
||
def resource_field_value(previous_changes, field, source_locale) | ||
values = previous_changes[field] | ||
new_value = values.last | ||
if new_value.is_a?(Hash) | ||
locale = source_locale || default_locale(@resource) | ||
return new_value[locale] | ||
end | ||
|
||
new_value | ||
end | ||
|
||
def default_locale(resource) | ||
if resource.respond_to? :organization | ||
resource.organization.default_locale.to_s | ||
else | ||
Decidim.available_locales.first.to_s | ||
end | ||
end | ||
|
||
def translated_locales_list(field) | ||
return nil unless @resource[field].is_a? Hash | ||
|
||
translated_locales = [] | ||
existing_locales = @resource[field].keys - ["machine_translations"] | ||
existing_locales.each do |locale| | ||
translated_locales << locale if @resource[field][locale].present? | ||
end | ||
|
||
translated_locales | ||
end | ||
|
||
def remove_duplicate_translations(field, translated_locales) | ||
machine_translated_locale = @resource[field]["machine_translations"].keys | ||
unless (translated_locales & machine_translated_locale).nil? | ||
(translated_locales & machine_translated_locale).each { |key| @resource[field]["machine_translations"].delete key } | ||
end | ||
end | ||
|
||
def pending_locales(translated_locales) | ||
available_locales = @resource.organization.available_locales.map(&:to_s) if @resource.respond_to? :organization | ||
available_locales ||= Decidim.available_locales.map(&:to_s) | ||
available_locales - translated_locales | ||
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 | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# frozen_string_literal: true | ||
|
||
class DeeplTranslator | ||
attr_reader :text, :source_locale, :target_locale, :resource, :field_name | ||
|
||
def initialize(resource, field_name, text, target_locale, source_locale) | ||
@resource = resource | ||
@field_name = field_name | ||
@text = text | ||
@target_locale = target_locale | ||
@source_locale = source_locale | ||
end | ||
|
||
def translate | ||
translation = DeepL.translate text, source_locale, target_locale | ||
|
||
Decidim::MachineTranslationSaveJob.perform_later( | ||
resource, | ||
field_name, | ||
target_locale, | ||
translation.text | ||
) | ||
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
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 | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
# frozen_string_literal: true | ||
|
||
require "decidim/dev/dummy_translator" | ||
|
||
Decidim.configure do |config| | ||
config.application_name = "OSP Agora" | ||
config.mailer_sender = "OSP Agora <[email protected]>" | ||
|
@@ -103,6 +105,13 @@ | |
end | ||
|
||
config.base_uploads_path = "#{ENV["HEROKU_APP_NAME"]}/" if ENV["HEROKU_APP_NAME"].present? | ||
|
||
# Machine Translation Configuration | ||
# | ||
# Enable machine translations | ||
config.enable_machine_translations = Rails.application.secrets.translator[:enabled] | ||
config.machine_translation_service = "DeeplTranslator" | ||
config.machine_translation_delay = Rails.application.secrets.translator[:delay] | ||
end | ||
|
||
Decidim.module_eval do | ||
|
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 | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# frozen_string_literal: true | ||
|
||
# DeepL Translation service configuration | ||
DeepL.configure do |config| | ||
config.auth_key = Rails.application.secrets.translator[:api_key] | ||
config.host = Rails.application.secrets.translator[:host] | ||
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
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
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 | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# frozen_string_literal: true | ||
|
||
module Decidim | ||
module TranslatorConfigurationHelper | ||
def self.able_to_seed? | ||
return true unless translator_activated? | ||
|
||
raise "You can't seed the database with machine translations enabled unless you use a compatible backend" unless compatible_backend? | ||
end | ||
|
||
def self.compatible_backend? | ||
Rails.configuration.active_job.queue_adapter != :async | ||
end | ||
|
||
def self.translator_activated? | ||
Decidim.enable_machine_translations | ||
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 | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# frozen_string_literal: true | ||
|
||
require "spec_helper" | ||
require "deepl" | ||
|
||
module Decidim | ||
describe MachineTranslationFieldsJob do | ||
let(:title) { { en: "New Title" } } | ||
let(:process) { build :participatory_process, title: title } | ||
let(:target_locale) { "ca" } | ||
let(:source_locale) { "en" } | ||
let(:url) { "https://dummy_url.org" } | ||
let(:translation) { double("translation", text: "Nou títol") } | ||
|
||
before do | ||
allow(Decidim).to receive(:machine_translation_service_klass).and_return(DeeplTranslator) | ||
allow(DeepL).to receive(:translate).with(title[source_locale.to_sym], source_locale, target_locale).and_return(translation) | ||
end | ||
|
||
describe "When fields job is executed" do | ||
before do | ||
clear_enqueued_jobs | ||
end | ||
|
||
it "calls DeeplTranslator to create machine translations" do | ||
expect(DeeplTranslator).to receive(:new).with( | ||
process, | ||
"title", | ||
process["title"][source_locale], | ||
target_locale, | ||
source_locale | ||
).and_call_original | ||
|
||
process.save | ||
|
||
MachineTranslationFieldsJob.perform_now( | ||
process, | ||
"title", | ||
process["title"][source_locale], | ||
target_locale, | ||
source_locale | ||
) | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.