Packages
gettext
0.22.2
1.0.2
1.0.1
retired
1.0.0
0.26.2
0.26.1
0.26.0
retired
0.25.0
0.24.0
0.23.1
0.23.0
0.22.3
0.22.2
0.22.1
0.22.0
0.21.0
0.20.0
0.19.1
0.19.0
0.18.2
0.18.1
0.18.0
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.1
0.16.0
0.15.0
0.14.1
0.14.0
0.13.1
0.13.0
0.12.2
0.12.1
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.1
0.6.0
0.5.0
Internationalization and localization through gettext
Current section
Files
Jump to
Current section
Files
lib/gettext/fuzzy.ex
defmodule Gettext.Fuzzy do
@moduledoc false
alias Expo.Message
@type message_key :: {binary | nil, binary | {binary, binary}}
@doc """
Returns a matcher function that takes two message keys and checks if they
match.
`String.jaro_distance/2` (which calculates the Jaro distance) is used to
measure the distance between the two messages. `threshold` is the minimum
distance that means a match. `{:match, distance}` is returned in case of a
match, `:nomatch` otherwise.
"""
@spec matcher(float) :: (message_key, message_key -> {:match, float} | :nomatch)
def matcher(threshold) do
fn old_key, new_key ->
distance = jaro_distance(old_key, new_key)
if distance >= threshold, do: {:match, distance}, else: :nomatch
end
end
@doc """
Finds the Jaro distance between the msgids of two messages.
To mimic the behaviour of the `msgmerge` tool, this function only calculates
the Jaro distance of the msgids of the two messages, even if one (or both)
of them is a plural message.
As per `msgmerge`, the msgctxt of a message is completely ignored when
calculating the distance.
"""
@spec jaro_distance(message_key, message_key) :: float
def jaro_distance({_context1, key1}, {_context2, key2}) do
jaro_distance_on_key(key1, key2)
end
# Apparently, msgmerge only looks at the msgid when performing fuzzy
# matching. This means that if we have two plural messages with similar
# msgids but very different msgid_plurals, they'll still fuzzy match.
def jaro_distance_on_key(key1, key2) when is_binary(key1) and is_binary(key2),
do: String.jaro_distance(key1, key2)
def jaro_distance_on_key({key1, _}, key2) when is_binary(key2),
do: String.jaro_distance(key1, key2)
def jaro_distance_on_key(key1, {key2, _}) when is_binary(key1),
do: String.jaro_distance(key1, key2)
def jaro_distance_on_key({key1, _}, {key2, _}), do: String.jaro_distance(key1, key2)
@doc """
Merges a message with the corresponding fuzzy match.
`new` is the newest message and `existing` is the existing message
that we use to populate the msgstr of the newest message.
Note that if `new` is a regular message, then the result will be a regular
message; if `new` is a plural message, then the result will be a
plural message.
"""
@spec merge(new :: Message.t(), existing :: Message.t()) :: Message.t()
def merge(new, existing) do
# Everything comes from "new", except for the msgstr and the comments.
new
|> Map.put(:comments, existing.comments)
|> merge_msgstr(existing)
|> Message.append_flag("fuzzy")
end
defp merge_msgstr(%Message.Singular{} = new, %Message.Singular{} = existing),
do: %{new | msgstr: existing.msgstr}
defp merge_msgstr(%Message.Singular{} = new, %Message.Plural{} = existing),
do: %{new | msgstr: existing.msgstr[0]}
defp merge_msgstr(%Message.Plural{} = new, %Message.Singular{} = existing),
do: %{new | msgstr: Map.new(new.msgstr, fn {i, _} -> {i, existing.msgstr} end)}
defp merge_msgstr(%Message.Plural{} = new, %Message.Plural{} = existing),
do: %{new | msgstr: existing.msgstr}
end