Add machine translations on project sync

This commit is contained in:
Simon Prévost 2023-03-14 13:16:39 -04:00
parent 00e21d7510
commit 6bc3274f42
56 changed files with 354 additions and 95 deletions

View File

@ -9,6 +9,5 @@ export default class DocumentExportFormatter {
chalk.bold.white(documentPath),
chalk.gray.dim.underline(path)
);
console.log('');
}
}

View File

@ -18,11 +18,10 @@ export default class ProjectAddTranslationsFormatter {
.map(fetchFromRevision)
.join(', ');
const title = `Adding translations paths (${languages})`;
console.log(chalk.gray.dim('⎯'.repeat(title.length - 1)));
console.log(chalk.magenta(title));
console.log('');
console.log(
chalk.white.bold('Adding translations paths →'),
chalk.white(languages),
chalk.green('✓')
);
}
}

View File

@ -5,9 +5,7 @@ export default class ProjectExportFormatter {
log() {
const title = 'Writing files locally';
console.log(chalk.gray.dim('⎯'.repeat(title.length - 1)));
console.log(chalk.magenta(title));
console.log('');
console.log(chalk.magenta(title));
}
}

View File

@ -14,17 +14,17 @@ export default class ProjectSyncFormatter extends Base {
if (flags.version) logFlags.push(chalk.gray(`${flags.version}`));
console.log(
chalk.magenta(
'Syncing sources',
`(${fetchFromRevision(project.masterRevision)})`,
logFlags.join('')
)
chalk.white.bold('Syncing sources →'),
chalk.white(
`${fetchFromRevision(project.masterRevision)}`,
logFlags.join('') || null
),
chalk.green('✓')
);
console.log('');
}
footerDryRun(time: number) {
console.log('');
console.log(
chalk.gray.dim(
'For more informations on operations: https://www.accent.reviews/guides/glossary.html#sync'
@ -38,6 +38,7 @@ export default class ProjectSyncFormatter extends Base {
}
footer(time: number) {
console.log('');
console.log(
chalk.gray.dim(
'For more informations on operations: https://www.accent.reviews/guides/glossary.html#sync'

View File

@ -10,6 +10,7 @@ defmodule Accent.MachineTranslationsConfigManager do
"config" => %{
"key" => params[:config_key]
},
"enabled_actions" => params[:enabled_actions],
"provider" => params[:provider],
"use_platform" => params[:use_platform]
}

View File

@ -18,6 +18,7 @@ defmodule Accent.Operation do
field(:rollbacked, :boolean, default: false)
field(:stats, {:array, :map}, default: [])
field(:options, {:array, :string}, default: [])
field(:machine_translated, :boolean, default: false)
embeds_one(:previous_translation, Accent.PreviousTranslation)
@ -26,7 +27,6 @@ defmodule Accent.Operation do
belongs_to(:version, Accent.Version)
belongs_to(:translation, Accent.Translation)
belongs_to(:project, Accent.Project)
belongs_to(:comment, Accent.Comment)
belongs_to(:user, Accent.User)
belongs_to(:batch_operation, Accent.Operation)
belongs_to(:rollbacked_operation, Accent.Operation)
@ -37,4 +37,22 @@ defmodule Accent.Operation do
timestamps()
end
@spec to_langue_entry(map(), boolean(), String.t()) :: Langue.Entry.t()
def to_langue_entry(operation, is_master, language_slug) do
%Langue.Entry{
id: operation.key,
key: operation.key,
value: operation.text,
master_value: operation.text,
is_master: is_master,
comment: operation.file_comment,
index: operation.file_index,
value_type: operation.value_type,
locked: operation.locked,
plural: operation.plural,
placeholders: operation.placeholders,
language_slug: language_slug
}
end
end

View File

@ -25,7 +25,7 @@ defmodule Accent.Project do
has_many(:all_collaborators, Accent.Collaborator)
belongs_to(:language, Accent.Language)
field :machine_translations_config, MachineTranslationsConfig
field(:machine_translations_config, MachineTranslationsConfig)
timestamps()
end

View File

@ -10,6 +10,7 @@ defmodule Accent.GraphQL.Mutations.MachineTranslationsConfig do
arg(:project_id, non_null(:id))
arg(:provider, non_null(:string))
arg(:use_platform, non_null(:boolean))
arg(:enabled_actions, non_null(list_of(non_null(:string))))
arg(:config_key, :string)
resolve(project_authorize(:save_project_machine_translations_config, &Resolver.save/3, :project_id))

View File

@ -10,6 +10,7 @@ defmodule Accent.GraphQL.Mutations.Revision do
arg(:project_id, non_null(:id))
arg(:language_id, non_null(:id))
arg(:default_null, :boolean, default_value: false)
arg(:machine_translations_enabled, :boolean, default_value: false)
resolve(project_authorize(:create_slave, &RevisionResolver.create/3, :project_id))
end

View File

@ -155,6 +155,7 @@ defmodule Accent.GraphQL.Resolvers.Revision do
defp parse_new_slave_options(args) do
options = []
options = if args[:default_null], do: ["default_null" | options], else: options
options = if args[:machine_translations_enabled], do: ["machine_translations_enabled" | options], else: options
options
end

View File

@ -19,6 +19,7 @@ defmodule Accent.GraphQL.Types.Project do
object :machine_translations_config do
field(:provider, non_null(:string))
field(:enabled_actions, non_null(list_of(non_null(:string))))
field(:use_platform, non_null(:boolean))
field(:use_config_key, non_null(:boolean))
end
@ -40,6 +41,7 @@ defmodule Accent.GraphQL.Types.Project do
{:ok,
%{
provider: project.machine_translations_config["provider"],
enabled_actions: project.machine_translations_config["enabled_actions"] || [],
use_platform: project.machine_translations_config["use_platform"],
use_config_key: not is_nil(project.machine_translations_config["config"]["key"])
}}

View File

@ -11,6 +11,11 @@ defmodule Accent.MachineTranslations do
Provider.id(provider)
end
def enabled_on_action?(config, action) do
config = config || %{}
to_string(action) in Map.get(config, "enabled_actions", [])
end
def enabled?(config) do
provider = provider_from_config(config)
Provider.enabled?(provider)

View File

@ -4,6 +4,6 @@ defmodule Accent.MachineTranslations.Provider.NotImplemented do
defimpl Accent.MachineTranslations.Provider do
def id(_provider), do: :not_implemented
def enabled?(_provider), do: false
def translate(_provider, entries, _source_language, _target_language), do: entries
def translate(_provider, entries, _source_language, _target_language), do: {:ok, entries}
end
end

View File

@ -19,6 +19,7 @@ defmodule Movement.Builders.NewSlave do
defp process_operations(context = %Movement.Context{assigns: assigns, operations: operations}) do
default_null = "default_null" in assigns.new_slave_options
machine_translations_enabled = "machine_translations_enabled" in assigns.new_slave_options
new_operations =
Enum.map(assigns[:translations], fn translation ->
@ -33,10 +34,18 @@ defmodule Movement.Builders.NewSlave do
plural: translation.plural,
locked: translation.locked,
placeholders: translation.placeholders,
options: assigns.new_slave_options
options: assigns.new_slave_options,
machine_translations_enabled: machine_translations_enabled
})
end)
new_operations =
if machine_translations_enabled do
Movement.MachineTranslations.translate(new_operations, assigns[:project], assigns[:master_revision], assigns[:language])
else
new_operations
end
%{context | operations: Enum.concat(operations, new_operations)}
end
@ -56,6 +65,7 @@ defmodule Movement.Builders.NewSlave do
|> RevisionScope.from_project(assigns[:project].id)
|> RevisionScope.master()
|> Repo.one!()
|> Repo.preload(:language)
assign(context, :master_revision, master_revision)
end

View File

@ -10,10 +10,13 @@ defmodule Movement.Builders.ProjectSync do
alias Accent.{Repo, Revision}
@batch_action "sync"
def build(context) do
# Dont keep track of the last used revision to prevent error on further steps
context
|> Map.put(:operations, [])
|> assign(:batch_action, @batch_action)
|> generate_operations()
|> assign(:revision, nil)
end
@ -36,20 +39,18 @@ defmodule Movement.Builders.ProjectSync do
context
|> assign(:master_revision, master_revision)
|> assign(:slave_revisions, slave_revisions)
|> assign_revisions_operations
|> assign_revisions_operations()
end
defp assign_revisions_operations(context) do
# Master revision
# Slave revisions conflicts
context =
context
|> assign(:master_revision, context.assigns[:master_revision])
|> assign(:revision, context.assigns[:master_revision])
|> RevisionSyncBuilder.build()
|> assign(:revisions, context.assigns[:slave_revisions])
|> SlaveConflictSyncBuilder.build()
# Slave revisions add/remove
Enum.reduce(context.assigns[:slave_revisions], context, fn revision, acc ->
acc
|> assign(:revision, revision)

View File

@ -3,16 +3,32 @@ defmodule Movement.Builders.RevisionMerge do
import Movement.Context, only: [assign: 3]
alias Accent.Scopes.Revision, as: RevisionScope
alias Accent.Scopes.Translation, as: TranslationScope
alias Accent.{Repo, Translation}
alias Accent.{Repo, Revision, Translation}
alias Movement.EntriesCommitProcessor
@batch_action "merge"
def build(context) do
context
|> assign_translations()
|> assign_master_revision()
|> Movement.Context.assign(:batch_action, @batch_action)
|> EntriesCommitProcessor.process()
end
defp assign_master_revision(context) do
master_revision =
Revision
|> RevisionScope.from_project(context.assigns[:project].id)
|> RevisionScope.master()
|> Repo.one!()
|> Repo.preload(:language)
assign(context, :master_revision, master_revision)
end
defp assign_translations(context) do
translations =
Translation

View File

@ -47,8 +47,8 @@ defmodule Movement.Comparers.MergeForce do
{"conflict_on_proposed", _text} ->
OperationMapper.map("merge_on_proposed_force", translation, suggested_translation)
{_action, _text} ->
%Operation{action: "noop", key: translation.key}
{_action, text} ->
%Operation{action: "noop", key: translation.key, text: text}
end
end
end

View File

@ -51,8 +51,8 @@ defmodule Movement.Comparers.MergePassive do
OperationMapper.map("merge_on_proposed", translation, suggested_translation)
{_action, _text} ->
%Operation{action: "noop", key: translation.key}
{_action, text} ->
%Operation{action: "noop", key: translation.key, text: text}
end
end
end

View File

@ -56,8 +56,8 @@ defmodule Movement.Comparers.MergeSmart do
OperationMapper.map("merge_on_corrected", translation, suggested_translation)
{_action, _text} ->
%Operation{action: "noop", key: translation.key}
{_action, text} ->
%Operation{action: "noop", key: translation.key, text: text}
end
end
end

View File

@ -34,8 +34,8 @@ defmodule Movement.Comparers.SyncPassive do
OperationMapper.map(action, translation, suggested_translation)
_ ->
%Operation{action: "noop", key: suggested_translation.key}
{_, text} ->
%Operation{action: "noop", key: suggested_translation.key, text: text}
end
end
end

View File

@ -33,8 +33,8 @@ defmodule Movement.Comparers.SyncSmart do
"""
def compare(translation, suggested_translation) do
case TranslationComparer.compare(translation, suggested_translation.text) do
{action, _text} when action in ~w(autocorrect) ->
%Operation{action: action, key: translation.key}
{action, text} when action === "autocorrect" ->
%Operation{action: action, key: translation.key, text: text}
{action, text} ->
suggested_translation = %{suggested_translation | text: text}

View File

@ -2,6 +2,8 @@ defmodule Movement.EntriesCommitProcessor do
@no_action_keys ~w(noop autocorrect)
@included_slave_actions ~w(new remove renew merge_on_corrected merge_on_proposed merge_on_proposed_force merge_on_corrected_force)
alias Movement.MachineTranslations
@doc """
For list of translations, new data (like the content of a file upload) and a given function,
returns the list of operations that will be executed. The operations will be neither persisted nor run.
@ -33,9 +35,18 @@ defmodule Movement.EntriesCommitProcessor do
}
operation = assigns[:comparer].(current_translation, suggested_translation)
operation =
if MachineTranslations.enable_machine_translation?(operation, entry, assigns[:revision], assigns[:project], assigns[:batch_action]) do
%{operation | machine_translations_enabled: true}
else
operation
end
%{operation | options: assigns[:options]}
end)
|> filter_for_revision(assigns[:revision])
|> MachineTranslations.translate(assigns[:project], assigns[:master_revision], assigns[:revision])
%{context | operations: Enum.concat(operations, new_operations)}
end
@ -73,15 +84,19 @@ defmodule Movement.EntriesCommitProcessor do
end
defp filter_for_revision(operations, %{master: true}) do
operations
|> Enum.filter(fn %{action: operation} -> operation not in @no_action_keys end)
Enum.filter(
operations,
fn operation ->
operation.action not in @no_action_keys
end
)
end
defp filter_for_revision(operations, _) do
Enum.filter(
operations,
fn %{action: operation} ->
operation in @included_slave_actions and operation not in @no_action_keys
fn operation ->
operation.action in @included_slave_actions and operation.action not in @no_action_keys
end
)
end

View File

@ -0,0 +1,46 @@
defmodule Movement.MachineTranslations do
alias Accent.{Language, MachineTranslations, Operation, Revision}
def enable_machine_translation?(_operation, _entry, %{master: true}, _, _), do: false
def enable_machine_translation?(operation, entry, _, project, action) do
MachineTranslations.enabled?(project.machine_translations_config) and
MachineTranslations.enabled_on_action?(project.machine_translations_config, action) and
operation.text === entry.value
end
def translate(operations, _project, nil, _revision), do: operations
def translate(operations, project, master_revision, revision) do
translated_texts =
operations
|> Enum.filter(& &1.machine_translations_enabled)
|> Enum.map(&Operation.to_langue_entry(&1, master_revision.id === &1.revision_id, language_slug(revision)))
|> MachineTranslations.translate(%{slug: language_slug(master_revision)}, %{slug: language_slug(revision)}, project.machine_translations_config)
|> case do
entries when is_list(entries) ->
Map.new(Enum.map(entries, &{&1.id, &1.value}))
_ ->
%{}
end
Enum.map(operations, fn operation ->
with true <- operation.machine_translations_enabled,
text = Map.get(translated_texts, operation.key),
true <- text !== operation.text do
dbg(%{operation | text: text, machine_translated: true})
else
_ -> operation
end
end)
end
defp language_slug(%Revision{} = revision) do
revision.slug || revision.language.slug
end
defp language_slug(%Language{} = language) do
language.slug
end
end

View File

@ -17,6 +17,7 @@ defmodule Movement.Mappers.Operation do
version_id: Map.get(suggested_translation, :version_id),
translation_id: Map.get(suggested_translation, :translation_id),
previous_translation: PreviousTranslation.from_translation(current_translation),
machine_translations_enabled: suggested_translation.machine_translations_enabled,
placeholders: suggested_translation.placeholders
}
end
@ -35,6 +36,7 @@ defmodule Movement.Mappers.Operation do
plural: Map.get(suggested_translation, :plural, current_translation.plural),
locked: Map.get(suggested_translation, :locked, current_translation.locked),
translation_id: Map.get(current_translation, :id),
machine_translations_enabled: Map.get(suggested_translation, :machine_translations_enabled, false),
previous_translation: PreviousTranslation.from_translation(current_translation),
placeholders: Map.get(suggested_translation, :placeholders, current_translation.placeholders)
}

View File

@ -20,7 +20,9 @@ defmodule Movement.Operation do
project_id: nil,
previous_translation: nil,
placeholders: [],
options: []
options: [],
machine_translated: false,
machine_translations_enabled: false
@type t :: %__MODULE__{}
end

View File

@ -100,6 +100,7 @@ defmodule Movement.Persisters.Base do
version_id: operation.version_id || placeholder_values[:version_id],
revision_id: operation.revision_id || placeholder_values[:revision_id]
})
|> Map.delete(:machine_translations_enabled)
end)
|> Stream.chunk_every(@operations_inserts_chunk)
|> Stream.flat_map(fn operations ->

View File

@ -26,6 +26,7 @@ defmodule Movement.Persisters.NewSlave do
|> Repo.insert()
|> case do
{:ok, revision} ->
revision = Repo.preload(revision, :language)
Movement.Context.assign(context, :revision, revision)
{:error, changeset} ->

View File

@ -6,15 +6,12 @@ defmodule Movement.Persisters.ProjectSync do
alias Accent.{Document, Project, Repo}
alias Movement.Persisters.Base, as: BasePersister
@batch_action "sync"
def persist(context = %Movement.Context{operations: []}), do: {:ok, {context, []}}
def persist(context) do
Repo.transaction(fn ->
context
|> persist_document()
|> Movement.Context.assign(:batch_action, @batch_action)
|> BasePersister.execute()
|> case do
{context, operations} ->

View File

@ -4,13 +4,7 @@ defmodule Movement.Persisters.RevisionMerge do
alias Accent.Repo
alias Movement.Persisters.Base, as: BasePersister
@batch_action "merge"
def persist(context) do
Repo.transaction(fn ->
context
|> Movement.Context.assign(:batch_action, @batch_action)
|> BasePersister.execute()
end)
Repo.transaction(fn -> BasePersister.execute(context) end)
end
end

View File

@ -1,4 +1,4 @@
defmodule Movement.SuggestedTranslation do
@enforce_keys ~w(text)a
defstruct ~w(text key file_comment file_index value_type revision_id translation_id version_id plural locked placeholders)a
defstruct ~w(text key file_comment file_index value_type revision_id translation_id version_id plural locked placeholders machine_translations_enabled)a
end

View File

@ -42,7 +42,6 @@ defmodule Accent.LintController do
lint_translations =
conn.assigns[:context].entries
|> Enum.map(&map_entry(&1, conn))
|> dbg
|> Accent.Lint.lint()
|> map_lint_translations()

View File

@ -12,7 +12,7 @@ defmodule Accent.MachineTranslationsController do
plug(:fetch_master_revision)
plug(:load_resource, model: Language, id_name: "language", as: :source_language)
plug(:load_resource, model: Language, id_name: "to_language_id", as: :target_language)
plug(:load_resource, model: Document, id_name: "document_id", as: :document, only: [:machine_translations_translate])
plug(:fetch_document when action === :translate_document)
plug(:fetch_order when action === :translate_document)
plug(:fetch_format when action === :translate_document)
plug(Accent.Plugs.MovementContextParser when action === :translate_file)
@ -130,6 +130,10 @@ defmodule Accent.MachineTranslationsController do
assign(conn, :document_format, conn.assigns[:document].format)
end
defp fetch_document(conn, _) do
assign(conn, :document, Repo.get(Document, conn.params["document_id"]))
end
defp fetch_order(conn = %{params: %{"order_by" => ""}}, _), do: assign(conn, :order, "index")
defp fetch_order(conn = %{params: %{"order_by" => order}}, _), do: assign(conn, :order, order)
defp fetch_order(conn, _), do: assign(conn, :order, "index")

View File

@ -84,7 +84,7 @@ defmodule Accent.Plugs.MovementContextParser do
end
def assign_movement_context(conn, _) do
assign(conn, :movement_context, %Context{})
assign(conn, :movement_context, %Context{assigns: %{options: [], project: conn.assigns[:project]}})
end
def assign_movement_version(conn = %{assigns: %{version: version, movement_context: context}}, _opts) do

View File

@ -0,0 +1,9 @@
defmodule Accent.Repo.Migrations.RemoveCommentIdFromOperations do
use Ecto.Migration
def change do
alter table(:operations) do
remove(:comment_id)
end
end
end

View File

@ -0,0 +1,15 @@
defmodule Accent.Repo.Migrations.AddMachineTranslatedForOperations do
use Ecto.Migration
def change do
alter table(:operations) do
add(:machine_translated, :boolean)
end
execute("UPDATE operations SET machine_translated = false")
alter table(:operations) do
modify(:machine_translated, :boolean, default: false, null: false)
end
end
end

View File

@ -54,6 +54,7 @@ defmodule AccentTest.Movement.Persisters.ProjectSync do
|> Context.assign(:document, document)
|> Context.assign(:revision, revision)
|> Context.assign(:user_id, user.id)
|> Context.assign(:batch_action, "sync")
|> ProjectSyncPersister.persist()
batch_operation =

View File

@ -380,6 +380,7 @@
"machine_translations": {
"title": "Machine translations",
"text": "Add a valid configuration to enable translating directly in the Review page. You can also translate your projects file as well as arbirtary files that you upload on Accent.",
"enabled_actions_sync": "Automatically run on project sync",
"use_platform_label": "Use the platform config if available",
"config_key_help_present": "Config key has been set but the value will never be exposed via the API.",
"config_key_help_not_present": "Once saved, the config key will never be shown.",
@ -700,6 +701,7 @@
},
"project_manage_languages_create_form": {
"default_null": "Initialize language with empty strings",
"machine_translations_enabled": "Initialize language with machine translations",
"language_search_placeholder": "Search…",
"save_button": "Add language"
},

View File

@ -381,6 +381,7 @@
"title": "Traductions machines",
"text": "Ajoutez une configuration valide pour activer la traduction directement dans la page Réviser. Vous pouvez également traduire le fichier de votre projet ainsi que les fichiers arbirtaires que vous téléchargez sur Accent.",
"use_platform_label": "Utilisez la configuration de la plate-forme si disponible",
"enabled_actions_sync": "Exécution automatique lors de la synchronisation du projet",
"config_key_help_present": "La clé de configuration a été définie, la valeur ne sera jamais exposée via lAPI.",
"config_key_help_not_present": "Une fois enregistrée, la clé de configuration ne sera plus jamais affichée.",
"config_key_placeholder": "Copiez la clé de configuration ici…"
@ -700,6 +701,7 @@
},
"project_manage_languages_create_form": {
"default_null": "Initialiser le langage avec des chaînes vides",
"machine_translations_enabled": "Initialiser le langage avec des traductions externes",
"language_search_placeholder": "Recherche…",
"save_button": "Ajouter une langue"
},

View File

@ -11,6 +11,7 @@
display: flex;
justify-content: space-between;
margin: 0 auto;
padding: 0 30px;
max-width: var(--screen-lg);
}

View File

@ -64,6 +64,11 @@ export default class ProjectCreateForm extends Component<Args> {
this.logo = selection.emoji;
}
@action
autofocus(input: HTMLInputElement) {
input.focus();
}
@action
async submit() {
this.isCreating = true;

View File

@ -17,7 +17,7 @@
</label>
<div local-class='formItem-fields'>
<input value={{this.name}} local-class='textInput' {{on 'keyup' (fn this.setName)}} />
<input {{did-insert (fn this.autofocus)}} value={{this.name}} autofocus local-class='textInput' {{on 'keyup' (fn this.setName)}} />
<input type='color' value={{this.mainColor}} local-class='colorInput' {{on 'change' (fn this.setMainColor)}} />

View File

@ -25,7 +25,7 @@
</LinkTo>
<LinkTo @route='logged-in.project.edit.machine-translations' local-class='link'>
{{inline-svg 'assets/machine-translations.svg' local-class='link-icon link-icon--machine-translations'}}
{{inline-svg 'assets/rocket.svg' local-class='link-icon link-icon--machine-translations'}}
<strong>{{t 'components.project_settings.links_list.machine_translations'}}</strong>
<p>{{t 'components.project_settings.links_list.machine_translations_text'}}</p>
</LinkTo>

View File

@ -13,10 +13,12 @@ interface Args {
onDelete: () => void;
onSave: ({
provider,
enabledActions,
usePlatform,
configKey,
}: {
provider: string;
enabledActions: string[];
usePlatform: boolean;
configKey: string | null;
}) => Promise<any>;
@ -41,6 +43,10 @@ export default class ProjectSettingsMachineTranslations extends Component<Args>
@service('intl')
intl: IntlService;
@tracked
enabledActions =
this.args.project.machineTranslationsConfig?.enabledActions || [];
@tracked
provider =
this.args.project.machineTranslationsConfig?.provider || 'google_translate';
@ -52,6 +58,10 @@ export default class ProjectSettingsMachineTranslations extends Component<Args>
@tracked
configKey: any;
get enabledActionsSync() {
return this.enabledActions.includes('sync');
}
get providerValue() {
return this.mappedProviders.find(({value}) => value === this.provider);
}
@ -94,6 +104,17 @@ export default class ProjectSettingsMachineTranslations extends Component<Args>
this.provider = value;
}
@action
onEnabledActionsChange(action: string) {
if (this.enabledActions.includes(action)) {
this.enabledActions = this.enabledActions.filter(
(enabledAction: string) => enabledAction !== action
);
} else {
this.enabledActions = this.enabledActions.concat([action]);
}
}
@action
onUsePlatformChange(event: InputEvent) {
const checked = (event.target as HTMLInputElement).checked;
@ -111,6 +132,7 @@ export default class ProjectSettingsMachineTranslations extends Component<Args>
*submit() {
yield this.args.onSave({
provider: this.provider,
enabledActions: this.enabledActions,
usePlatform: this.usePlatform,
configKey: this.configKey,
});

View File

@ -60,10 +60,17 @@
font-size: 12px;
}
.platform-checkbox {
.options {
margin: 10px 0 0;
display: flex;
flex-direction: column;
gap: 5px;
}
.option-checkbox {
display: flex;
align-items: center;
max-width: 270px;
width: 100%;
gap: 8px;
font-size: 12px;
font-weight: bold;

View File

@ -17,15 +17,21 @@
@options={{this.mappedProviders}}
@onchange={{fn this.setProvider}}
/>
<label local-class='platform-checkbox'>
</div>
<div local-class='options'>
<label local-class='option-checkbox'>
<input value={{this.usePlatform}} type='checkbox' checked={{this.usePlatform}} {{on 'change' (fn this.onUsePlatformChange)}} />
{{t 'components.project_settings.machine_translations.use_platform_label'}}
</label>
<label local-class='option-checkbox'>
<input value={{this.enabledActionsSync}} type='checkbox' checked={{this.enabledActionsSync}} {{on 'change' (fn this.onEnabledActionsChange 'sync')}} />
{{t 'components.project_settings.machine_translations.enabled_actions_sync'}}
</label>
</div>
{{#unless this.usePlatform}}
{{#if @project.machineTranslationsConfig.useConfigKey}}
<p local-class='config-key-help'>
{{t 'components.project_settings.machine_translations.config_key_help_present'}}

View File

@ -29,6 +29,9 @@ export default class CreateForm extends Component<Args> {
@tracked
defaultNull = false;
@tracked
machineTranslationsEnabled = false;
@not('language')
emptyLanguage: boolean;
@ -49,11 +52,19 @@ export default class CreateForm extends Component<Args> {
this.defaultNull = !this.defaultNull;
}
@action
onChangeMachineTranslationsEnabled() {
this.machineTranslationsEnabled = !this.machineTranslationsEnabled;
}
@action
async submit() {
this.isLoading = true;
await this.args.onCreate(this.language, {defaultNull: this.defaultNull});
await this.args.onCreate(this.language, {
machineTranslationsEnabled: this.machineTranslationsEnabled,
defaultNull: this.defaultNull,
});
this.isLoading = false;
}

View File

@ -1,24 +1,40 @@
.fields {
.form {
margin-bottom: 25px;
padding: 12px;
border-radius: 3px;
background: var(--background-light);
border: 1px solid var(--background-light-highlight);
:global(.ember-power-select-trigger) {
min-height: 31px;
max-width: 400px;
margin-bottom: 10px;
background: var(--background-light);
background: var(--body-background);
border: 1px solid var(--background-light-highlight);
}
}
.options {
margin-top: 10px;
margin-bottom: 20px;
}
.option {
margin-bottom: 10px;
margin-bottom: 7px;
}
.optionLabel {
display: flex;
font-size: 12px;
align-items: center;
font-size: 13px;
}
.optionLabelText {
margin-left: 5px;
}
.optionLabel-icon {
width: 15px;
margin-left: 3px;
transform: rotate(45deg);
}

View File

@ -1,4 +1,4 @@
<div local-class='fields'>
<div local-class='form'>
<AccSelect
@search={{fn this.searchLanguages}}
@searchEnabled={{true}}
@ -9,16 +9,31 @@
@onchange={{fn this.setLanguage}}
/>
<div local-class='option'>
<label local-class='optionLabel'>
<input type='checkbox' checked={{this.defaultNull}} {{on 'change' (fn this.onChangeDefaultNull)}} />
<span local-class='optionLabelText'>
{{t 'components.project_manage_languages_create_form.default_null'}}
</span>
</label>
</div>
</div>
<div local-class='options'>
<div local-class='option'>
<label local-class='optionLabel'>
<input type='checkbox' checked={{this.defaultNull}} {{on 'change' (fn this.onChangeDefaultNull)}} />
<span local-class='optionLabelText'>
{{t 'components.project_manage_languages_create_form.default_null'}}
</span>
</label>
</div>
<AsyncButton @onClick={{fn this.submit}} class='button button--filled' @loading={{this.isLoading}} @disabled={{this.emptyLanguage}}>
{{t 'components.project_manage_languages_create_form.save_button'}}
</AsyncButton>
{{#if (get @permissions 'machine_translations_translate')}}
<div local-class='option'>
<label local-class='optionLabel'>
<input type='checkbox' checked={{this.machineTranslationsEnabled}} {{on 'change' (fn this.onChangeMachineTranslationsEnabled)}} />
<span local-class='optionLabelText'>
{{t 'components.project_manage_languages_create_form.machine_translations_enabled'}}
</span>
{{inline-svg 'assets/rocket.svg' local-class='optionLabel-icon'}}
</label>
</div>
{{/if}}
</div>
<AsyncButton @onClick={{fn this.submit}} class='button button--filled' @loading={{this.isLoading}} @disabled={{this.emptyLanguage}}>
{{t 'components.project_manage_languages_create_form.save_button'}}
</AsyncButton>
</div>

View File

@ -19,7 +19,8 @@
padding: 10px 2px;
margin-bottom: 10px;
border-bottom-color: var(--background-light-highlight);
font-size: 16px;
font-size: 20px;
font-weight: bold;
}
&:focus,
@ -74,7 +75,7 @@
.list-link {
display: inline-flex;
align-items: center;
align-items: baseline;
text-decoration: none;
color: var(--color-primary);

View File

@ -11,13 +11,13 @@
{{#if (get @permissions 'create_slave')}}
<div local-class='createSlaveForm'>
{{#if @errors}}
<div local-class='error'>
<ul local-class='error'>
{{#each @errors as |error|}}
<li>
{{error}}
</li>
{{/each}}
</div>
</ul>
{{/if}}
<ProjectSettings::ManageLanguages::CreateForm @permissions={{@permissions}} @project={{@project}} @languages={{@languages}} @onCreate={{@onCreate}} />
</div>

View File

@ -7,7 +7,9 @@ import GlobalState from 'accent-webapp/services/global-state';
import FlashMessages from 'ember-cli-flash/services/flash-messages';
import ApolloMutate from 'accent-webapp/services/apollo-mutate';
import machineTranslationsConfigSaveQuery from 'accent-webapp/queries/save-project-machine-translations-config';
import machineTranslationsConfigSaveQuery, {
SaveProjectMachineTranslationsConfigVariables,
} from 'accent-webapp/queries/save-project-machine-translations-config';
import machineTranslationsConfigDeleteQuery from 'accent-webapp/queries/delete-project-machine-translations-config';
const FLASH_MESSAGE_PREFIX = 'pods.project.edit.flash_messages.';
@ -42,16 +44,14 @@ export default class MachineTranslationController extends Controller {
showLoading: boolean;
@action
async saveMachineTranslationsConfig(config: {
provider: string;
usePlatform: boolean;
configKey: string | null;
}) {
async saveMachineTranslationsConfig(
config: SaveProjectMachineTranslationsConfigVariables
) {
return this.mutateResource({
mutation: machineTranslationsConfigSaveQuery,
successMessage: FLASH_MESSAGE_CONFIG_SUCCESS,
errorMessage: FLASH_MESSAGE_CONFIG_ERROR,
variables: {projectId: this.project.id, ...config},
variables: {...config, projectId: this.project.id},
});
}
@ -79,7 +79,7 @@ export default class MachineTranslationController extends Controller {
const response = await this.apolloMutate.mutate({
mutation,
variables,
refetchQueries: ['ProjectMachineTranslationsConfig'],
refetchQueries: ['Project', 'ProjectMachineTranslationsConfig'],
});
if (response.errors) {

View File

@ -112,7 +112,10 @@ export default class ManageLanguagesController extends Controller {
}
@action
async create(languageId: string, options: {defaultNull: boolean}) {
async create(
languageId: string,
options: {defaultNull: boolean; machineTranslationsEnabled: boolean}
) {
const project = this.model.project;
this.errors = [];
@ -123,6 +126,7 @@ export default class ManageLanguagesController extends Controller {
projectId: project.id,
languageId,
defaultNull: options.defaultNull,
machineTranslationsEnabled: options.machineTranslationsEnabled,
},
});

View File

@ -5,11 +5,13 @@ export default gql`
$projectId: ID!
$languageId: ID!
$defaultNull: Boolean
$machineTranslationsEnabled: Boolean
) {
createRevision(
projectId: $projectId
languageId: $languageId
defaultNull: $defaultNull
machineTranslationsEnabled: $machineTranslationsEnabled
) {
revision {
id

View File

@ -8,6 +8,7 @@ export default gql`
machineTranslationsConfig {
provider
usePlatform
enabledActions
useConfigKey
}
}

View File

@ -1,10 +1,11 @@
import gql from 'graphql-tag';
export interface SaveProjectMachineTranslationsConfigVariables {
name: string;
projectId: string;
pictureUrl: string;
permissions: string[];
provider: string;
configKey: string;
enabledActions: string[];
usePlatform: boolean;
}
export interface SaveProjectMachineTranslationsConfigResponse {
@ -20,12 +21,14 @@ export default gql`
$provider: String!
$configKey: String
$usePlatform: Boolean!
$enabledActions: [String!]!
$projectId: ID!
) {
saveProjectMachineTranslationsConfig(
provider: $provider
configKey: $configKey
usePlatform: $usePlatform
enabledActions: $enabledActions
projectId: $projectId
) {
project {

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?><svg
viewBox="0 0 24 24"
stroke-width="2"
fill="none"
xmlns="http://www.w3.org/2000/svg"
stroke="currentColor"
>
<path
d="M16.061 10.404L14 17h-4l-2.061-6.596a6 6 0 01.998-5.484l2.59-3.315a.6.6 0 01.946 0l2.59 3.315a6 6 0 01.998 5.484zM10 20c0 2 2 3 2 3s2-1 2-3M8.5 12.5C5 15 7 19 7 19l3-2M15.931 12.5c3.5 2.5 1.5 6.5 1.5 6.5l-3-2"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
></path>
<path
d="M12 11a2 2 0 110-4 2 2 0 010 4z"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
></path>
</svg>

After

Width:  |  Height:  |  Size: 671 B