Skip to content

Commit

Permalink
Add unambiguous narrow symbols to currency strings
Browse files Browse the repository at this point in the history
  • Loading branch information
kipcole9 committed Oct 8, 2022
1 parent 8742ec9 commit 495b15c
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 38 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## Cldr_Currencies v2.14.2

This is the changelog for Cldr_Currencies v2.14.2 released on October 8th, 2022. For older changelogs please consult the release tag on [GitHub](https://github.com/elixir-cldr/cldr_currencies/tags)

### Bug Fixes

* Fix `Cldr.Currency.currency_strings/2` to include narrow symbols. Note that where duplicate symbols exist (same symbol for more than one currency) they are are omitted from the list of strings since they are ambiguous (unless one of them is a historic currency in which case the current currency is kept and the historic removed).

## Cldr_Currencies v2.14.1

This is the changelog for Cldr_Currencies v2.14.1 released on June 8th, 2022. For older changelogs please consult the release tag on [GitHub](https://github.com/elixir-cldr/cldr_currencies/tags)
Expand Down
6 changes: 4 additions & 2 deletions lib/cldr/backend.ex
Original file line number Diff line number Diff line change
Expand Up @@ -515,9 +515,10 @@ defmodule Cldr.Currency.Backend do
|> Map.new()

currency_strings =
for {currency_code, currency} <- Cldr.Config.currencies_for!(locale_name, config) do
for {currency_code, currency} <- currencies do
strings =
([currency.name, currency.symbol, currency.code] ++ Map.values(currency.count))
[currency.name, currency.symbol, currency.code]
|> Kernel.++(Map.values(currency.count))
|> Enum.reject(&is_nil/1)
|> Enum.map(&String.downcase/1)
|> Enum.map(&String.trim_trailing(&1, "."))
Expand All @@ -530,6 +531,7 @@ defmodule Cldr.Currency.Backend do
Cldr.Currency.invert_currency_strings(currency_strings)
|> Cldr.Currency.remove_duplicate_strings(currencies)
|> Map.new()
|> Cldr.Currency.add_unique_narrow_symbols(currencies)

def currencies_for_locale(
%LanguageTag{cldr_locale_name: unquote(locale_name)},
Expand Down
74 changes: 45 additions & 29 deletions lib/cldr/currency.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1248,7 +1248,7 @@ defmodule Cldr.Currency do
* `{:ok, currency_string_map}` or
* raises an exception
* raises an exception.
## Example
Expand Down Expand Up @@ -1549,31 +1549,9 @@ defmodule Cldr.Currency do
!annotated?(currency)
end

# Sort the list by string. If the string is the same
# then sort historic currencies after the current one

@doc false
def string_comparator({k, v1}, {k, v2}, currencies) do
cond do
historic?(currencies[v1]) ->
false

historic?(currencies[v2]) ->
true

true ->
raise "String #{inspect(k)} has two current currencies of #{inspect(v1)} and " <>
"#{inspect(v2)}."
end
end

def string_comparator({k1, _v1}, {k2, _v2}, _currencies) do
k1 < k2
end

# Its possible that more than one currency will have a string
# in common with another currency. One example is `:AFA` and
# `:AFN`. As in this csae, its most common when a country
# `:AFN`. As in this case, its most common when a country
# changes to a new currency with the same name.

# The strategy is to remove the duplicate string from the
Expand All @@ -1582,18 +1560,37 @@ defmodule Cldr.Currency do
@doc false
def remove_duplicate_strings(strings, currencies) do
strings
|> Enum.sort(fn a, b -> string_comparator(a, b, currencies) end)
|> Enum.sort(fn a, b -> string_comparator(a, b) end)
|> remove_duplicates(currencies)
end

def string_comparator({k1, _v1}, {k2, _v2}) do
k1 < k2
end

# If the same code and one is historic and the other is current then
# keep the current one. If they are both current, then omit the string
# because it is ambiguous.

defp remove_duplicates([], _currencies) do
[]
end

defp remove_duplicates([{_, _}] = currency, _currencies) do
currency
end

# Same string, different code -> omit the 2nd one since
# we sort historic currencies after the current ones
defp remove_duplicates([{c1, code1} | [{c1, _code2} | rest]], currencies) do
remove_duplicates([{c1, code1} | rest], currencies)
defp remove_duplicates([{c1, code1} | [{c1, code2} | rest]], currencies) do
cond do
historic?(currencies[code1]) && current?(currencies[code2]) ->
remove_duplicates([{c1, code2} | rest], currencies)

current?(currencies[code1]) && historic?(currencies[code2]) ->
remove_duplicates([{c1, code1} | rest], currencies)

true ->
remove_duplicates(rest, currencies)
end
end

# Not a duplicate, process the rest of the list
Expand All @@ -1609,6 +1606,25 @@ defmodule Cldr.Currency do
|> List.flatten()
end

# Add the narrow currency symbols iff they don't duplicate an
# existing string

@doc false
def add_unique_narrow_symbols(currency_strings, currencies) do
Enum.reduce(currencies, currency_strings, fn {currency_code, currency}, strings ->
cond do
is_nil(currency.narrow_symbol) ->
strings

Map.has_key?(strings, currency.narrow_symbol) ->
strings

true ->
Map.put(strings, String.downcase(currency.narrow_symbol), currency_code)
end
end)
end

defp currency_already_defined_error(code) do
"Currency #{inspect(code)} is already defined."
end
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Cldr.Currencies.MixProject do
use Mix.Project

@version "2.14.1"
@version "2.14.2"

def project do
[
Expand Down
12 changes: 6 additions & 6 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
%{
"benchee": {:hex, :benchee, "1.1.0", "f3a43817209a92a1fade36ef36b86e1052627fd8934a8b937ac9ab3a76c43062", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}], "hexpm", "7da57d545003165a012b587077f6ba90b89210fd88074ce3c60ce239eb5e6d93"},
"cldr_utils": {:hex, :cldr_utils, "2.17.1", "d5532ccfbe4b39cb652ba4566571324278344a6a2fb4500dac213ce8e123732f", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "052e0c2cf5ee801018580d69939b25528730d73ea05a05cb2c0e0a8204856d76"},
"cldr_utils": {:hex, :cldr_utils, "2.19.1", "5a7bcd2f2fd432c548e494e850bba8a9e838f1b10202f682ea1d9809d74eff31", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "fbd10f79363e70f3d893ab21e195f444ca87c2c80120b5911761491da4489620"},
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
"dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"},
"dialyxir": {:hex, :dialyxir, "1.2.0", "58344b3e87c2e7095304c81a9ae65cb68b613e28340690dfe1a5597fd08dec37", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "61072136427a851674cab81762be4dbeae7679f85b1272b6d25c3a839aff8463"},
"earmark": {:hex, :earmark, "1.4.14", "d04572cef64dd92726a97d92d714e38d6e130b024ea1b3f8a56e7de66ec04e50", [:mix], [{:earmark_parser, ">= 1.4.12", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "df338b8b1852ee425180b276c56c6941cb12220e04fe8718fe4acbdd35fd699f"},
"earmark_parser": {:hex, :earmark_parser, "1.4.21", "7299db854f6d63730c15c8a781862889bb0fbf4432d7c306b3e63ce825d64baa", [:mix], [], "hexpm", "60664e1bdf7a02d8cbec2ac1d5b6fe0a68cf1d749ba955990d647346fac421e4"},
"earmark_parser": {:hex, :earmark_parser, "1.4.28", "0bf6546eb7cd6185ae086cbc5d20cd6dbb4b428aad14c02c49f7b554484b4586", [:mix], [], "hexpm", "501cef12286a3231dc80c81352a9453decf9586977f917a96e619293132743fb"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm", "b14f1dc204321429479c569cfbe8fb287541184ed040956c8862cb7a677b8406"},
"ex_cldr": {:hex, :ex_cldr, "2.28.0", "8f7a8c70a49dc31f656eb02d4c6280550ab52abd340406e8341dd4ba2390798d", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:cldr_utils, "~> 2.17", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.19", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "564608c4a344c9cca54874e95fb77cd7149f593ccf522db5cbe8943c0b183630"},
"ex_doc": {:hex, :ex_doc, "0.28.2", "e031c7d1a9fc40959da7bf89e2dc269ddc5de631f9bd0e326cbddf7d8085a9da", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "51ee866993ffbd0e41c084a7677c570d0fc50cb85c6b5e76f8d936d9587fa719"},
"jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"},
"ex_cldr": {:hex, :ex_cldr, "2.33.2", "8adc4df3985e7f5d1d55cbbf72f993569de20eff5012ff3ea9412753961d4c00", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:cldr_utils, "~> 2.18", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.19", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: true]}], "hexpm", "fd81a7147b4ed86c0c44c0251444cb8d1defccc7b33b89067ca1635f23e9fbf8"},
"ex_doc": {:hex, :ex_doc, "0.28.5", "3e52a6d2130ce74d096859e477b97080c156d0926701c13870a4e1f752363279", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "d2c4b07133113e9aa3e9ba27efb9088ba900e9e51caa383919676afdf09ab181"},
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
Expand Down
8 changes: 8 additions & 0 deletions test/cldr_currencies_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,12 @@ defmodule Cldr.Currency.Test do
test "Currency from binary locale" do
assert _currency = Cldr.Currency.currency_from_locale("fr")
end

test "Narrow symbols are included in currency strings if they are not ambiguous" do
assert Cldr.Currency.currency_strings!("en", MyApp.Cldr)
|> Enum.filter(fn {_k, v} -> v == :ZAR end) ==
[{"south african rand", :ZAR}, {"r", :ZAR}, {"zar", :ZAR}]

assert Cldr.Currency.currency_strings!("en", MyApp.Cldr) |> Map.get("$") == :USD
end
end

0 comments on commit 495b15c

Please sign in to comment.