Skip to content

Commit

Permalink
Add plural and locked state for translations
Browse files Browse the repository at this point in the history
  • Loading branch information
simonprev committed Apr 25, 2018
1 parent 6fd43e9 commit fc272bc
Show file tree
Hide file tree
Showing 33 changed files with 170 additions and 56 deletions.
4 changes: 2 additions & 2 deletions lib/accent/schemas/operation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ defmodule Accent.Operation do
field(:file_index, :integer)

field(:value_type, :string)
field(:plural, :boolean, default: false)
field(:locked, :boolean, default: false)

field(:rollbacked, :boolean, default: false)
field(:stats, {:array, :map}, default: [])
Expand All @@ -50,8 +52,6 @@ defmodule Accent.Operation do
has_one(:rollback_operation, Accent.Operation, foreign_key: :rollbacked_operation_id)
has_many(:operations, Accent.Operation, foreign_key: :batch_operation_id)

field(:language_id, :string, virtual: true)

timestamps()
end

Expand Down
2 changes: 2 additions & 0 deletions lib/accent/schemas/translation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ defmodule Accent.Translation do
field(:file_index, :integer)

field(:value_type, :string, default: "string")
field(:plural, :boolean, default: false)
field(:locked, :boolean, default: false)

belongs_to(:document, Accent.Document)
belongs_to(:revision, Accent.Revision)
Expand Down
9 changes: 9 additions & 0 deletions lib/accent/scopes/translation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ defmodule Accent.Scopes.Translation do
@spec active(Ecto.Queryable.t()) :: Ecto.Queryable.t()
def active(query), do: from(t in query, where: [removed: false])

@doc """
## Examples
iex> Accent.Scopes.Translation.not_locked(Accent.Translation)
#Ecto.Query<from t in Accent.Translation, where: t.locked == false>
"""
@spec not_locked(Ecto.Queryable.t()) :: Ecto.Queryable.t()
def not_locked(query), do: from(t in query, where: [locked: false])

@doc """
## Examples
Expand Down
1 change: 1 addition & 0 deletions lib/accent/translations/translations_counter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ defmodule Accent.TranslationsCounter do
scope =
Translation
|> Scope.active()
|> Scope.not_locked()
|> Scope.no_version()

conflicted =
Expand Down
1 change: 1 addition & 0 deletions lib/graphql/resolvers/translation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ defmodule Accent.GraphQL.Resolvers.Translation do
translations =
Translation
|> TranslationScope.active()
|> TranslationScope.not_locked()
|> TranslationScope.from_revision(revision.id)
|> TranslationScope.from_search(args[:query])
|> TranslationScope.from_document(args[:document] || :all)
Expand Down
1 change: 1 addition & 0 deletions lib/graphql/types/translation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ defmodule Accent.GraphQL.Types.Translation do
field(:id, non_null(:id))
field(:key, non_null(:string), resolve: &Accent.GraphQL.Resolvers.Translation.key/3)
field(:value_type, non_null(:translation_value_type))
field(:plural, non_null(:boolean))

field(:proposed_text, :string)
field(:corrected_text, :string)
Expand Down
2 changes: 1 addition & 1 deletion lib/langue/entry.ex
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
defmodule Langue.Entry do
defstruct key: nil, value: nil, comment: nil, index: 1, value_type: "string"
defstruct key: nil, value: nil, comment: nil, index: 1, value_type: "string", locked: false, plural: false
end
8 changes: 4 additions & 4 deletions lib/langue/formatter/gettext/parser.ex
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
defmodule Langue.Formatter.Gettext.Parser do
@behaviour Langue.Formatter.Parser

@plural_value_type "plural"

alias Langue.Entry

def parse(%{render: render}) do
Expand Down Expand Up @@ -30,7 +28,8 @@ defmodule Langue.Formatter.Gettext.Parser do
comment: join_string(translation.comments),
key: join_string(translation.msgid) <> key_suffix("_"),
value: join_string(translation.msgid_plural),
value_type: @plural_value_type
plural: true,
locked: true
}

translation.msgstr
Expand All @@ -40,7 +39,8 @@ defmodule Langue.Formatter.Gettext.Parser do
index: index,
key: join_string(translation.msgid) <> key_suffix(plural_index),
value: join_string(value),
value_type: @plural_value_type
plural: true,
value_type: Langue.ValueType.parse(join_string(value))
}
])
end)
Expand Down
48 changes: 15 additions & 33 deletions lib/langue/utils/nested_parser_helper.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ defmodule Langue.Utils.NestedParserHelper do
alias Langue.Entry

@nested_separator "."
@plural_suffixes ~w(.zero .one .two .few .many .other)

def group_by_key_with_index(entries, index, nested_separator \\ @nested_separator) do
grouped_entries =
Expand Down Expand Up @@ -33,53 +34,34 @@ defmodule Langue.Utils.NestedParserHelper do
|> List.flatten()
|> Enum.with_index(1)
|> Enum.map(fn {entry, index} -> %{entry | index: index} end)
|> Enum.map(&parse_plural/1)
end

defp flattenize_array({key, value, index}), do: flattenize_tuple({"#{key}#{@nested_separator}__KEY__#{index}", value, "string"})

defp flattenize_tuple({key, value}), do: flattenize_tuple({key, value, "string"})
defp flattenize_tuple({key, value, type}) when is_tuple(value), do: flattenize_tuple({key, elem(value, 0), type})

defp flattenize_tuple({key, value, _type}) when is_boolean(value) or value == "false" or value == "true" do
%Entry{key: key, value: entry_value_to_string(value), value_type: "boolean", comment: ""}
end

defp flattenize_tuple({key, value, _type}) when value == "" do
%Entry{key: key, value: entry_value_to_string(value), value_type: "empty", comment: ""}
end

defp flattenize_tuple({key, value, _type}) when value == :null or value == "nil" do
%Entry{key: key, value: entry_value_to_string(value), value_type: "null", comment: ""}
defp parse_plural(entry) do
if Enum.any?(@plural_suffixes, &String.ends_with?(entry.key, &1)) do
%{entry | plural: true}
else
entry
end
end

defp flattenize_tuple({key, value, _type}) when is_integer(value) do
%Entry{key: key, value: entry_value_to_string(to_string(value)), value_type: "integer", comment: ""}
end

defp flattenize_tuple({key, value, _type}) when is_float(value) do
%Entry{key: key, value: entry_value_to_string(to_string(value)), value_type: "float", comment: ""}
end
defp flattenize_array({key, value, index}), do: flattenize_tuple({"#{key}#{@nested_separator}__KEY__#{index}", value})

defp flattenize_tuple({key, value, ""}), do: flattenize_tuple({key, value, nil})
defp flattenize_tuple({key, value}) when is_tuple(value), do: flattenize_tuple({key, elem(value, 0)})

defp flattenize_tuple({key, value, type}) when is_binary(value) do
%Entry{key: key, value: entry_value_to_string(value), value_type: type, comment: ""}
end

defp flattenize_tuple({key, value, type}) when is_list(value) do
defp flattenize_tuple({key, value}) when is_list(value) do
value
|> Enum.with_index()
|> Enum.map(fn {item, index} ->
if is_tuple(item) && !is_list(elem(item, 0)) do
flattenize_tuple({"#{key}#{@nested_separator}#{elem(item, 0)}", elem(item, 1), type})
flattenize_tuple({"#{key}#{@nested_separator}#{elem(item, 0)}", elem(item, 1)})
else
flattenize_array({key, item, index})
end
end)
end

defp entry_value_to_string(true), do: "true"
defp entry_value_to_string(false), do: "false"
defp entry_value_to_string(:null), do: "null"
defp entry_value_to_string(value), do: value
defp flattenize_tuple({key, value}) do
%Entry{key: key, value: to_string(value), value_type: Langue.ValueType.parse(value), comment: ""}
end
end
8 changes: 8 additions & 0 deletions lib/langue/value_type.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
defmodule Langue.ValueType do
def parse(value) when is_boolean(value) or value == "false" or value == "true", do: "boolean"
def parse(""), do: "empty"
def parse(value) when value in [:null, "nil"], do: "null"
def parse(value) when is_integer(value), do: "integer"
def parse(value) when is_float(value), do: "float"
def parse(_), do: "string"
end
1 change: 1 addition & 0 deletions lib/movement/builders/document_delete.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ defmodule Movement.Builders.DocumentDelete do
translations =
Translation
|> TranslationScope.active()
|> TranslationScope.not_locked()
|> TranslationScope.from_document(context.assigns[:document].id)
|> Repo.all()

Expand Down
4 changes: 3 additions & 1 deletion lib/movement/builders/new_slave.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ defmodule Movement.Builders.NewSlave do
file_index: translation.file_index,
document_id: translation.document_id,
version_id: translation.version_id,
value_type: translation.value_type
value_type: translation.value_type,
plural: translation.plural,
locked: translation.locked
})
end)

Expand Down
1 change: 1 addition & 0 deletions lib/movement/builders/revision_correct_all.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ defmodule Movement.Builders.RevisionCorrectAll do
translations =
Translation
|> TranslationScope.active()
|> TranslationScope.not_locked()
|> TranslationScope.conflicted()
|> TranslationScope.from_revision(assigns[:revision].id)
|> Repo.all()
Expand Down
1 change: 1 addition & 0 deletions lib/movement/builders/revision_merge.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ defmodule Movement.Builders.RevisionMerge do
translations =
Translation
|> TranslationScope.active()
|> TranslationScope.not_locked()
|> TranslationScope.from_revision(context.assigns[:revision].id)
|> TranslationScope.from_document(context.assigns[:document].id)
|> Repo.all()
Expand Down
1 change: 1 addition & 0 deletions lib/movement/builders/revision_uncorrect_all.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ defmodule Movement.Builders.RevisionUncorrectAll do
translations =
Translation
|> TranslationScope.active()
|> TranslationScope.not_locked()
|> TranslationScope.not_conflicted()
|> TranslationScope.from_revision(assigns[:revision].id)
|> Repo.all()
Expand Down
1 change: 1 addition & 0 deletions lib/movement/builders/slave_conflict_sync.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ defmodule Movement.Builders.SlaveConflictSync do
translations =
Translation
|> TranslationScope.active()
|> TranslationScope.not_locked()
|> TranslationScope.from_revisions(assigns[:revision_ids])
|> TranslationScope.from_keys(assigns[:translation_keys])
|> Repo.all()
Expand Down
2 changes: 2 additions & 0 deletions lib/movement/entries_commit_processor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ defmodule Movement.EntriesCommitProcessor do
file_comment: entry.comment,
file_index: entry.index,
value_type: entry.value_type,
plural: entry.plural,
locked: entry.locked,
revision_id: Map.get(assigns[:revision], :id)
}

Expand Down
6 changes: 5 additions & 1 deletion lib/movement/mappers/operation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ defmodule Movement.Mappers.Operation do
key: suggested_translation.key,
file_comment: suggested_translation.file_comment,
file_index: suggested_translation.file_index,
value_type: Map.get(suggested_translation, :value_type),
value_type: suggested_translation.value_type,
plural: suggested_translation.plural,
locked: suggested_translation.locked,
revision_id: Map.get(suggested_translation, :revision_id),
document_id: Map.get(suggested_translation, :document_id),
version_id: Map.get(suggested_translation, :version_id),
Expand All @@ -28,6 +30,8 @@ defmodule Movement.Mappers.Operation do
revision_id: Map.get(suggested_translation, :revision_id, current_translation.revision_id),
version_id: Map.get(suggested_translation, :version_id, current_translation.version_id),
value_type: Map.get(suggested_translation, :value_type, current_translation.value_type),
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),
previous_translation: PreviousTranslation.from_translation(current_translation)
}
Expand Down
2 changes: 2 additions & 0 deletions lib/movement/migration/translation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ defmodule Movement.Migration.Translation do
corrected_text: operation.text,
conflicted: is_nil(operation.version_id),
value_type: operation.value_type,
plural: operation.plural,
locked: operation.locked,
file_index: operation.file_index,
file_comment: operation.file_comment,
removed: operation.previous_translation && operation.previous_translation.removed,
Expand Down
2 changes: 2 additions & 0 deletions lib/movement/operation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ defmodule Movement.Operation do
file_comment: nil,
file_index: 0,
value_type: nil,
plural: false,
locked: false,
batch: false,
translation_id: nil,
rollbacked_operation_id: nil,
Expand Down
4 changes: 2 additions & 2 deletions lib/movement/suggested_translation.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule Movement.SuggestedTranslation do
@enforce_keys ~w(text key)a
defstruct ~w(text key file_comment file_index value_type revision_id)a
@enforce_keys ~w(text)a
defstruct ~w(text key file_comment file_index value_type revision_id plural locked)a
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
defmodule Accent.Repo.Migrations.AddPluralAndLockedToTranslationsAndOperations do
use Ecto.Migration

def change do
alter table(:translations) do
add :plural, :boolean, default: false, null: false
add :locked, :boolean, default: false, null: false
end

alter table(:operations) do
add :plural, :boolean, default: false, null: false
add :locked, :boolean, default: false, null: false
end
end
end
1 change: 1 addition & 0 deletions test/graphql/resolvers/translation_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ defmodule AccentTest.GraphQL.Resolvers.Translation do

test "list revision", %{revision: revision, context: context} do
translation = %Translation{revision_id: revision.id, conflicted: true, key: "ok", corrected_text: "bar", proposed_text: "bar"} |> Repo.insert!()
%Translation{revision_id: revision.id, conflicted: true, key: "hidden", corrected_text: "bar", proposed_text: "bar", locked: true} |> Repo.insert!()

{:ok, result} = Resolver.list_revision(revision, %{}, context)

Expand Down
24 changes: 24 additions & 0 deletions test/langue/es6_module/expectation_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,28 @@ defmodule AccentTest.Formatter.Es6Module.Expectation do
]
end
end

defmodule Plural do
use Langue.Expectation.Case

def render do
"""
export default {
"count_something": {
"one": "1 item",
"other": "%{count} items",
"zero": "No items"
}
};
"""
end

def entries do
[
%Entry{comment: "", index: 1, key: "count_something.one", value: "1 item", value_type: "string", plural: true},
%Entry{comment: "", index: 2, key: "count_something.other", value: "%{count} items", value_type: "string", plural: true},
%Entry{comment: "", index: 3, key: "count_something.zero", value: "No items", value_type: "string", plural: true}
]
end
end
end
15 changes: 9 additions & 6 deletions test/langue/es6_module/formatter_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@ defmodule AccentTest.Formatter.Es6Module.Formatter do

Code.require_file("expectation_test.exs", __DIR__)

alias AccentTest.Formatter.Es6Module.Expectation.{Simple}
alias AccentTest.Formatter.Es6Module.Expectation.{Simple, Plural}
alias Langue.Formatter.Es6Module.{Parser, Serializer}

@tests [
{:test_parse, Simple, Parser},
{:test_serialize, Simple, Serializer}
Simple,
Plural
]

test "es6 module" do
Enum.each(@tests, fn {fun, ex, mo} ->
{expected, result} = apply(Accent.FormatterTestHelper, fun, [ex, mo])
assert expected == result
Enum.each(@tests, fn ex ->
{expected_parse, result_parse} = Accent.FormatterTestHelper.test_parse(ex, Parser)
{expected_serialize, result_serialize} = Accent.FormatterTestHelper.test_serialize(ex, Serializer)

assert expected_parse == result_parse
assert expected_serialize == result_serialize
end)
end
end
6 changes: 3 additions & 3 deletions test/langue/gettext/expectation_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ defmodule AccentTest.Formatter.Gettext.Expectation do
def entries do
[
%Entry{index: 1, key: "has already been taken", value: "est déjà pris"},
%Entry{index: 2, key: "should be at least n character(s).__KEY___", value: "should be at least %{count} character(s)", value_type: "plural"},
%Entry{index: 2, key: "should be at least n character(s).__KEY__0", value: "should be at least 0 characters", value_type: "plural"},
%Entry{index: 2, key: "should be at least n character(s).__KEY__1", value: "should be at least %{count} character(s)", value_type: "plural"}
%Entry{index: 2, key: "should be at least n character(s).__KEY___", value: "should be at least %{count} character(s)", plural: true, locked: true, value_type: "string"},
%Entry{index: 2, key: "should be at least n character(s).__KEY__0", value: "should be at least 0 characters", plural: true, value_type: "string"},
%Entry{index: 2, key: "should be at least n character(s).__KEY__1", value: "should be at least %{count} character(s)", plural: true, value_type: "string"}
]
end
end
Expand Down
Loading

0 comments on commit fc272bc

Please sign in to comment.