Packages
gettext
0.13.1
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 Gettext.PO
alias Gettext.PO.Translation
alias Gettext.PO.PluralTranslation
@type translation_key :: binary | {binary, binary}
@doc """
Returns a matcher function that takes two translation keys and checks if they
match.
`String.jaro_distance/2` (which calculates the Jaro distance) is used to
measure the distance between the two translations. `threshold` is the minimum
distance that means a match. `{:match, distance}` is returned in case of a
match, `:nomatch` otherwise.
"""
@spec matcher(float) :: (translation_key, translation_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 translations.
To mimic the behaviour of the `msgmerge` tool, this function only calculates
the Jaro distance of the msgids of the two translations, even if one (or both)
of them is a plural translation.
"""
@spec jaro_distance(translation_key, translation_key) :: float
def jaro_distance(key1, key2)
# Apparently, msgmerge only looks at the msgid when performing fuzzy
# matching. This means that if we have two plural translations with similar
# msgids but very different msgid_plurals, they'll still fuzzy match.
def jaro_distance(k1, k2) when is_binary(k1) and is_binary(k2), do: String.jaro_distance(k1, k2)
def jaro_distance({k1, _}, k2) when is_binary(k2), do: String.jaro_distance(k1, k2)
def jaro_distance(k1, {k2, _}) when is_binary(k1), do: String.jaro_distance(k1, k2)
def jaro_distance({k1, _}, {k2, _}), do: String.jaro_distance(k1, k2)
@doc """
Merges a translation with the corresponding fuzzy match.
`new` is the newest translation and `existing` is the existing translation
that we use to populate the msgstr of the newest translation.
Note that if `new` is a regular translation, then the result will be a regular
translation; if `new` is a plural translation, then the result will be a
plural translation.
"""
@spec merge(PO.translation, PO.translation) :: PO.translation
def merge(new, existing) do
new |> do_merge_fuzzy(existing) |> PO.Translations.mark_as_fuzzy
end
defp do_merge_fuzzy(%Translation{} = new, %Translation{} = existing),
do: %{new | msgstr: existing.msgstr}
defp do_merge_fuzzy(%Translation{} = new, %PluralTranslation{} = existing),
do: %{new | msgstr: existing.msgstr[0]}
defp do_merge_fuzzy(%PluralTranslation{} = new, %Translation{} = existing),
do: %{new | msgstr: (for {i, _} <- new.msgstr, into: %{}, do: {i, existing.msgstr})}
defp do_merge_fuzzy(%PluralTranslation{} = new, %PluralTranslation{} = existing),
do: %{new | msgstr: existing.msgstr}
end