Packages
localize
0.13.0
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.41.3
0.41.2
0.41.1
0.41.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.0
0.30.1
0.30.0
retired
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
0.1.0-alpha.1
Localization (parsing, formatting) of numbers, dates/time/calendar, units of measure, messages and lists. Includes localized collation.
Current section
Files
Jump to
Current section
Files
lib/localize/validity.ex
defmodule Localize.Validity do
@moduledoc false
@doc """
Normalizes a language tag field.
"""
@callback normalize(String.t() | atom()) :: String.t() | atom()
@doc """
Validates a language tag field
"""
@callback validate(String.t() | atom()) :: {:ok, term, atom()} | {:error, String.t()}
defmacro __using__(type) do
validity_data = Localize.SupplementalData.validity(type)
Localize.Validity.generate_validity_checks(validity_data)
end
@doc false
def generate_validity_checks(validity_data) do
quote bind_quoted: [validity_data: Macro.escape(validity_data)] do
for {status, codes} <- validity_data do
{code_ranges, simple_codes} = Localize.Validity.partition(codes)
simple_check =
if length(simple_codes) > 0 do
defp valid(code) when code in unquote(simple_codes) do
{:ok, code, unquote(status)}
end
end
range_check =
for range <- code_ranges, range != [] do
{base, range_start, range_end} = Localize.Validity.range_from(range)
defp valid(unquote(base) <> <<char::utf8>> = code)
when char in unquote(range_start)..unquote(range_end) do
{:ok, code, unquote(status)}
end
end
end
defp valid(code) do
{:error, code}
end
end
end
@doc false
def partition(list) do
Enum.reduce(list, {[], []}, fn elem, {ranges, simple} ->
if String.contains?(elem, "~") do
{[elem | ranges], simple}
else
{ranges, [elem | simple]}
end
end)
end
@doc false
def range_from(code_range) do
[left, range_end] = String.split(code_range, "~")
{base, range_start} = String.split_at(left, -1)
<<range_start::utf8>> = range_start
<<range_end::utf8>> = range_end
{base, range_start, range_end}
end
# Only used for testing
@doc false
def all_valid(type) do
validity_data = Localize.SupplementalData.validity(type)
for {_status, codes} <- validity_data do
{code_ranges, simple_codes} = partition(codes)
range_check =
for range <- code_ranges, range != [] do
{base, range_start, range_end} = range_from(range)
for char <- range_start..range_end do
base <> <<char::utf8>>
end
end
simple_codes ++ range_check
end
|> List.flatten()
end
# Returns a list of known data that omits
# valid (per the spec) but shouldn't be used
# in general practise
@omit_status [:deprecated, :reserved, :special, :unknown, :private_use]
@doc false
def known(type) do
validity_data = Localize.SupplementalData.validity(type)
for {status, codes} <- validity_data, status not in @omit_status do
{code_ranges, simple_codes} = partition(codes)
range_check =
for range <- code_ranges, range != [] do
{base, range_start, range_end} = range_from(range)
for char <- range_start..range_end do
base <> <<char::utf8>>
end
end
simple_codes ++ range_check
end
|> List.flatten()
end
end