Packages

Small Gleam helpers to make numbers, bytes, durations and lists human-friendly.

Current section

Files

Jump to
humanize src humanize.erl
Raw

src/humanize.erl

-module(humanize).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/humanize.gleam").
-export([number_with/3, number/1, number_with_opts/2, get_locale_data/1, make_locale/6, get_locale_data_with/2, time_ago_unix/5, time_ago_unix_with_overrides/6, time_ago_millis/5, time_ago_millis_with_overrides/6, bytes_decimal/1, bytes_binary/1, percent_with/4, percent/1, percent_ratio/4, duration/1, duration_precise/1, list_locale/3, list/1, list_with_oxford/1, list_with_ampersand/1, ordinal/1]).
-export_type([decimal_separator/0, number_opts/0, locale_data/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type decimal_separator() :: dot | comma.
-type number_opts() :: {number_opts, integer(), decimal_separator()}.
-type locale_data() :: {locale_data,
binary(),
binary(),
binary(),
binary(),
list({integer(), binary(), binary()})}.
-file("src/humanize.gleam", 19).
-spec decimal_sep_from_sep(decimal_separator()) -> binary().
decimal_sep_from_sep(Sep) ->
case Sep of
dot ->
<<"."/utf8>>;
comma ->
<<","/utf8>>
end.
-file("src/humanize.gleam", 27).
-spec pow10_acc(integer(), integer()) -> integer().
pow10_acc(N, Acc) ->
case N of
0 ->
Acc;
_ ->
pow10_acc(N - 1, Acc * 10)
end.
-file("src/humanize.gleam", 34).
-spec pow10(integer()) -> integer().
pow10(N) ->
pow10_acc(N, 1).
-file("src/humanize.gleam", 152).
-spec pow_int_acc(integer(), integer(), integer()) -> integer().
pow_int_acc(Base, Exp, Acc) ->
case Exp of
0 ->
Acc;
_ ->
pow_int_acc(Base, Exp - 1, Acc * Base)
end.
-file("src/humanize.gleam", 159).
-spec pow_int(integer(), integer()) -> integer().
pow_int(Base, Exp) ->
pow_int_acc(Base, Exp, 1).
-file("src/humanize.gleam", 167).
-spec join_strings(list(binary()), binary()) -> binary().
join_strings(Xs, Sep) ->
case Xs of
[] ->
<<""/utf8>>;
[A] ->
A;
[A@1 | Rest] ->
gleam@list:fold(
Rest,
A@1,
fun(Acc, X) ->
<<<<Acc/binary, Sep/binary>>/binary, X/binary>>
end
)
end.
-file("src/humanize.gleam", 163).
-spec repeat_string(integer(), binary()) -> binary().
repeat_string(N, S) ->
join_strings(gleam@list:repeat(S, N), <<""/utf8>>).
-file("src/humanize.gleam", 45).
?DOC(" Format an integer using a specific number of decimals and decimal separator.\n").
-spec number_with(integer(), integer(), decimal_separator()) -> binary().
number_with(N, Decimals, Sep) ->
Abs = case N < 0 of
true ->
- N;
false ->
N
end,
Sign = case N < 0 of
true ->
<<"-"/utf8>>;
false ->
<<""/utf8>>
end,
Format_scaled = fun(N@1, Denom, Suffix, Decimals@1, Sep@1, Sign@1) ->
Mul = pow10(Decimals@1),
Scaled = case Denom of
0 -> 0;
Gleam@denominator -> ((N@1 * Mul) + (Denom div 2)) div Gleam@denominator
end,
Whole = case Mul of
0 -> 0;
Gleam@denominator@1 -> Scaled div Gleam@denominator@1
end,
Frac = case Mul of
0 -> 0;
Gleam@denominator@2 -> Scaled rem Gleam@denominator@2
end,
case Decimals@1 =:= 0 of
true ->
<<<<Sign@1/binary, (erlang:integer_to_binary(Whole))/binary>>/binary,
Suffix/binary>>;
false ->
Frac_s = erlang:integer_to_binary(Frac),
Frac_len = string:length(Frac_s),
Frac_padded = case Frac_len < Decimals@1 of
true ->
Needed = Decimals@1 - Frac_len,
Zeros = repeat_string(Needed, <<"0"/utf8>>),
<<Zeros/binary, Frac_s/binary>>;
false ->
Frac_s
end,
<<<<<<<<Sign@1/binary,
(erlang:integer_to_binary(Whole))/binary>>/binary,
(decimal_sep_from_sep(Sep@1))/binary>>/binary,
Frac_padded/binary>>/binary,
Suffix/binary>>
end
end,
case Abs >= 1000000000 of
true ->
Format_scaled(Abs, 1000000000, <<"B"/utf8>>, Decimals, Sep, Sign);
false ->
case Abs >= 1000000 of
true ->
Mul@1 = pow10(Decimals),
Scaled@1 = ((Abs * Mul@1) + (1000000 div 2)) div 1000000,
case Scaled@1 >= (1000 * Mul@1) of
true ->
Format_scaled(
Abs,
1000000000,
<<"B"/utf8>>,
Decimals,
Sep,
Sign
);
false ->
Format_scaled(
Abs,
1000000,
<<"M"/utf8>>,
Decimals,
Sep,
Sign
)
end;
false ->
case Abs >= 1000 of
true ->
Mul@2 = pow10(Decimals),
Scaled@2 = ((Abs * Mul@2) + (1000 div 2)) div 1000,
case Scaled@2 >= (1000 * Mul@2) of
true ->
Format_scaled(
Abs,
1000000,
<<"M"/utf8>>,
Decimals,
Sep,
Sign
);
false ->
Format_scaled(
Abs,
1000,
<<"K"/utf8>>,
Decimals,
Sep,
Sign
)
end;
false ->
erlang:integer_to_binary(N)
end
end
end.
-file("src/humanize.gleam", 40).
?DOC(
" Format an integer into a compact human-friendly string.\n"
" Example: `1234` -> \"1.2K\".\n"
).
-spec number(integer()) -> binary().
number(N) ->
number_with(N, 1, dot).
-file("src/humanize.gleam", 123).
?DOC(" Format an integer using a `NumberOpts` record.\n").
-spec number_with_opts(integer(), number_opts()) -> binary().
number_with_opts(N, Opts) ->
case Opts of
{number_opts, Decimals, Sep} ->
number_with(N, Decimals, Sep)
end.
-file("src/humanize.gleam", 179).
-spec join_with_conj(list(binary()), binary()) -> binary().
join_with_conj(Items, Conj_full) ->
case Items of
[] ->
<<""/utf8>>;
[A] ->
A;
[A@1, B] ->
<<<<A@1/binary, Conj_full/binary>>/binary, B/binary>>;
[A@2 | Rest] ->
gleam@list:fold(
Rest,
A@2,
fun(Acc, X) ->
<<<<Acc/binary, ", "/utf8>>/binary, X/binary>>
end
)
end.
-file("src/humanize.gleam", 229).
-spec raw_to_locale_data(
{binary(),
binary(),
binary(),
binary(),
list({integer(), binary(), binary()})}
) -> locale_data().
raw_to_locale_data(Raw) ->
case Raw of
{Ago, In_, Nowv, Conj, Units} ->
{locale_data, Ago, In_, Nowv, Conj, Units}
end.
-file("src/humanize.gleam", 237).
-spec find_locale_raw(
binary(),
list({binary(),
{binary(),
binary(),
binary(),
binary(),
list({integer(), binary(), binary()})}})
) -> locale_data().
find_locale_raw(Locale, List) ->
case List of
[] ->
raw_to_locale_data(internals@locales:default_raw());
[{K, Raw} | Rest] ->
case K =:= Locale of
true ->
raw_to_locale_data(Raw);
false ->
find_locale_raw(Locale, Rest)
end
end.
-file("src/humanize.gleam", 255).
?DOC(" Retrieve the built-in `LocaleData` for a given locale code.\n").
-spec get_locale_data(binary()) -> locale_data().
get_locale_data(Locale) ->
find_locale_raw(Locale, internals@locales:locales_raw()).
-file("src/humanize.gleam", 260).
?DOC(" Create a custom `LocaleData` entry.\n").
-spec make_locale(
binary(),
binary(),
binary(),
binary(),
binary(),
list({integer(), binary(), binary()})
) -> {binary(), locale_data()}.
make_locale(Code, Ago, In_, Nowv, Conj, Units) ->
{Code, {locale_data, Ago, In_, Nowv, Conj, Units}}.
-file("src/humanize.gleam", 272).
?DOC(" Lookup `LocaleData` with custom overrides.\n").
-spec get_locale_data_with(binary(), list({binary(), locale_data()})) -> locale_data().
get_locale_data_with(Locale, Overrides) ->
case Overrides of
[] ->
find_locale_raw(Locale, internals@locales:locales_raw());
[{K, D} | Rest] ->
case K =:= Locale of
true ->
D;
false ->
get_locale_data_with(Locale, Rest)
end
end.
-file("src/humanize.gleam", 288).
-spec format_unit(integer(), binary(), binary()) -> binary().
format_unit(Count, Singular, Plural) ->
case Count =:= 1 of
true ->
<<<<(erlang:integer_to_binary(Count))/binary, " "/utf8>>/binary,
Singular/binary>>;
false ->
<<<<(erlang:integer_to_binary(Count))/binary, " "/utf8>>/binary,
Plural/binary>>
end.
-file("src/humanize.gleam", 191).
-spec build_parts(integer(), list({integer(), binary(), binary()}), integer()) -> list(binary()).
build_parts(Rem, Us, Remaining) ->
case Remaining =< 0 of
true ->
[];
false ->
case Us of
[] ->
[];
[{Sec, Sing, Plur} | Rest] ->
case case Sec of
0 -> 0;
Gleam@denominator -> Rem div Gleam@denominator
end of
0 ->
build_parts(Rem, Rest, Remaining);
N ->
Part = format_unit(N, Sing, Plur),
New_rem = Rem - (N * Sec),
lists:append(
[Part],
build_parts(New_rem, Rest, Remaining - 1)
)
end
end
end.
-file("src/humanize.gleam", 296).
-spec time_ago_from_data(
locale_data(),
integer(),
integer(),
boolean(),
integer()
) -> binary().
time_ago_from_data(Data, Past_seconds, Now_seconds, Precise, Max_units) ->
Delta = Now_seconds - Past_seconds,
Abs_delta = case Delta < 0 of
true ->
- Delta;
false ->
Delta
end,
Now_str = case Data of
{locale_data, _, _, Nowv, _, _} ->
Nowv
end,
case Abs_delta =< 5 of
true ->
Now_str;
false ->
Units = case Data of
{locale_data, _, _, _, _, U} ->
U
end,
Needed_units = case Precise of
true ->
Max_units;
false ->
1
end,
Parts = build_parts(Abs_delta, Units, Needed_units),
Core = join_with_conj(Parts, erlang:element(5, Data)),
case Delta < 0 of
true ->
<<<<(erlang:element(3, Data))/binary, " "/utf8>>/binary,
Core/binary>>;
false ->
<<<<Core/binary, " "/utf8>>/binary,
(erlang:element(2, Data))/binary>>
end
end.
-file("src/humanize.gleam", 340).
?DOC(" Format a Unix timestamp relative to `now_seconds` using built-in locales.\n").
-spec time_ago_unix(integer(), integer(), binary(), boolean(), integer()) -> binary().
time_ago_unix(Past_seconds, Now_seconds, Locale, Precise, Max_units) ->
Data = get_locale_data(Locale),
time_ago_from_data(Data, Past_seconds, Now_seconds, Precise, Max_units).
-file("src/humanize.gleam", 352).
?DOC(" Like `time_ago_unix` but accepts a list of runtime overrides.\n").
-spec time_ago_unix_with_overrides(
integer(),
integer(),
binary(),
list({binary(), locale_data()}),
boolean(),
integer()
) -> binary().
time_ago_unix_with_overrides(
Past_seconds,
Now_seconds,
Locale,
Overrides,
Precise,
Max_units
) ->
Data = get_locale_data_with(Locale, Overrides),
time_ago_from_data(Data, Past_seconds, Now_seconds, Precise, Max_units).
-file("src/humanize.gleam", 365).
?DOC(" Convenience wrapper: accept timestamps in milliseconds instead of seconds.\n").
-spec time_ago_millis(integer(), integer(), binary(), boolean(), integer()) -> binary().
time_ago_millis(Past_millis, Now_millis, Locale, Precise, Max_units) ->
Past = Past_millis div 1000,
Now = Now_millis div 1000,
time_ago_unix(Past, Now, Locale, Precise, Max_units).
-file("src/humanize.gleam", 378).
?DOC(" Millisecond-based wrapper with overrides.\n").
-spec time_ago_millis_with_overrides(
integer(),
integer(),
binary(),
list({binary(), locale_data()}),
boolean(),
integer()
) -> binary().
time_ago_millis_with_overrides(
Past_millis,
Now_millis,
Locale,
Overrides,
Precise,
Max_units
) ->
Past = Past_millis div 1000,
Now = Now_millis div 1000,
time_ago_unix_with_overrides(
Past,
Now,
Locale,
Overrides,
Precise,
Max_units
).
-file("src/humanize.gleam", 391).
-spec find_denom_for(integer(), integer(), list(binary()), integer()) -> {integer(),
binary(),
list(binary())}.
find_denom_for(N, Base, Units, Idx) ->
case Units of
[] ->
{1, <<"B"/utf8>>, []};
[U] ->
{pow_int(Base, Idx), U, []};
[U@1 | Rest] ->
Denom = pow_int(Base, Idx),
Next = Denom * Base,
case N < Next of
true ->
{Denom, U@1, Rest};
false ->
find_denom_for(N, Base, Rest, Idx + 1)
end
end.
-file("src/humanize.gleam", 414).
-spec promote_if_needed_bytes(
integer(),
integer(),
integer(),
integer(),
binary(),
list(binary())
) -> {integer(), binary(), list(binary())}.
promote_if_needed_bytes(N, Base, Mul, Denom, Unit, Rest) ->
Scaled = case Denom of
0 -> 0;
Gleam@denominator -> ((N * Mul) + (Denom div 2)) div Gleam@denominator
end,
case Scaled >= (Base * Mul) of
true ->
case Rest of
[] ->
{Denom, Unit, Rest};
[Next | Rest2] ->
{Denom * Base, Next, Rest2}
end;
false ->
{Denom, Unit, Rest}
end.
-file("src/humanize.gleam", 434).
-spec bytes_with(
integer(),
integer(),
list(binary()),
integer(),
decimal_separator()
) -> binary().
bytes_with(Bytes, Base, Units, Decimals, Sep) ->
N = case Bytes < 0 of
true ->
- Bytes;
false ->
Bytes
end,
Sign = case Bytes < 0 of
true ->
<<"-"/utf8>>;
false ->
<<""/utf8>>
end,
{Denom, Unit, Rest} = find_denom_for(N, Base, Units, 0),
Mul = pow10(Decimals),
{Denom@1, Unit@1, _} = promote_if_needed_bytes(
N,
Base,
Mul,
Denom,
Unit,
Rest
),
Scaled = case Denom@1 of
0 -> 0;
Gleam@denominator -> ((N * Mul) + (Denom@1 div 2)) div Gleam@denominator
end,
Whole = case Mul of
0 -> 0;
Gleam@denominator@1 -> Scaled div Gleam@denominator@1
end,
Frac = case Mul of
0 -> 0;
Gleam@denominator@2 -> Scaled rem Gleam@denominator@2
end,
case Decimals =:= 0 of
true ->
<<<<<<Sign/binary, (erlang:integer_to_binary(Whole))/binary>>/binary,
" "/utf8>>/binary,
Unit@1/binary>>;
false ->
case Frac =:= 0 of
true ->
<<<<<<Sign/binary,
(erlang:integer_to_binary(Whole))/binary>>/binary,
" "/utf8>>/binary,
Unit@1/binary>>;
false ->
Frac_s = erlang:integer_to_binary(Frac),
Frac_len = string:length(Frac_s),
Frac_padded = case Frac_len < Decimals of
true ->
Needed = Decimals - Frac_len,
Zeros = repeat_string(Needed, <<"0"/utf8>>),
<<Zeros/binary, Frac_s/binary>>;
false ->
Frac_s
end,
<<<<<<<<<<Sign/binary,
(erlang:integer_to_binary(Whole))/binary>>/binary,
(decimal_sep_from_sep(Sep))/binary>>/binary,
Frac_padded/binary>>/binary,
" "/utf8>>/binary,
Unit@1/binary>>
end
end.
-file("src/humanize.gleam", 130).
?DOC(" Format bytes using decimal units (base 1000), supports up to Yottabyte (YB).\n").
-spec bytes_decimal(integer()) -> binary().
bytes_decimal(Bytes) ->
bytes_with(
Bytes,
1000,
[<<"B"/utf8>>,
<<"KB"/utf8>>,
<<"MB"/utf8>>,
<<"GB"/utf8>>,
<<"TB"/utf8>>,
<<"PB"/utf8>>,
<<"EB"/utf8>>,
<<"ZB"/utf8>>,
<<"YB"/utf8>>],
1,
dot
).
-file("src/humanize.gleam", 141).
?DOC(" Format bytes using binary units (base 1024), supports up to Yobibyte (YiB).\n").
-spec bytes_binary(integer()) -> binary().
bytes_binary(Bytes) ->
bytes_with(
Bytes,
1024,
[<<"B"/utf8>>,
<<"KiB"/utf8>>,
<<"MiB"/utf8>>,
<<"GiB"/utf8>>,
<<"TiB"/utf8>>,
<<"PiB"/utf8>>,
<<"EiB"/utf8>>,
<<"ZiB"/utf8>>,
<<"YiB"/utf8>>],
1,
dot
).
-file("src/humanize.gleam", 499).
?DOC(" Format a percentage value with custom options.\n").
-spec percent_with(integer(), integer(), decimal_separator(), boolean()) -> binary().
percent_with(N, Decimals, Sep, Spaced) ->
Formatted = case Decimals =:= 0 of
true ->
erlang:integer_to_binary(N);
false ->
Zeros = repeat_string(Decimals, <<"0"/utf8>>),
<<<<(erlang:integer_to_binary(N))/binary,
(decimal_sep_from_sep(Sep))/binary>>/binary,
Zeros/binary>>
end,
case Spaced of
true ->
<<Formatted/binary, " %"/utf8>>;
false ->
<<Formatted/binary, "%"/utf8>>
end.
-file("src/humanize.gleam", 494).
?DOC(" Format an integer as a percentage string. Uses no decimals by default.\n").
-spec percent(integer()) -> binary().
percent(N) ->
percent_with(N, 0, dot, false).
-file("src/humanize.gleam", 521).
?DOC(" Compute and format a ratio as a percentage with rounding.\n").
-spec percent_ratio(integer(), integer(), integer(), decimal_separator()) -> binary().
percent_ratio(Numerator, Denominator, Decimals, Sep) ->
case Denominator =:= 0 of
true ->
<<"NaN%"/utf8>>;
false ->
Negative_num = Numerator < 0,
Negative_den = Denominator < 0,
Sign = case Negative_num /= Negative_den of
true ->
<<"-"/utf8>>;
false ->
<<""/utf8>>
end,
N = case Numerator < 0 of
true ->
- Numerator;
false ->
Numerator
end,
D = case Denominator < 0 of
true ->
- Denominator;
false ->
Denominator
end,
Mul = pow10(Decimals),
Scaled = case D of
0 -> 0;
Gleam@denominator -> (((N * 100) * Mul) + (D div 2)) div Gleam@denominator
end,
Whole = case Mul of
0 -> 0;
Gleam@denominator@1 -> Scaled div Gleam@denominator@1
end,
Frac = case Mul of
0 -> 0;
Gleam@denominator@2 -> Scaled rem Gleam@denominator@2
end,
case Decimals =:= 0 of
true ->
<<<<Sign/binary, (erlang:integer_to_binary(Whole))/binary>>/binary,
"%"/utf8>>;
false ->
Frac_s = erlang:integer_to_binary(Frac),
Frac_len = string:length(Frac_s),
Frac_padded = case Frac_len < Decimals of
true ->
Needed = Decimals - Frac_len,
Zeros = repeat_string(Needed, <<"0"/utf8>>),
<<Zeros/binary, Frac_s/binary>>;
false ->
Frac_s
end,
<<<<<<<<Sign/binary,
(erlang:integer_to_binary(Whole))/binary>>/binary,
(decimal_sep_from_sep(Sep))/binary>>/binary,
Frac_padded/binary>>/binary,
"%"/utf8>>
end
end.
-file("src/humanize.gleam", 580).
?DOC(" Format a duration in seconds into a single largest unit description.\n").
-spec duration(integer()) -> binary().
duration(Seconds) ->
case Seconds < 60 of
true ->
case Seconds =:= 1 of
true ->
<<"1 second"/utf8>>;
false ->
<<(erlang:integer_to_binary(Seconds))/binary,
" seconds"/utf8>>
end;
false ->
case Seconds < 3600 of
true ->
M = Seconds div 60,
case M =:= 1 of
true ->
<<"1 minute"/utf8>>;
false ->
<<(erlang:integer_to_binary(M))/binary,
" minutes"/utf8>>
end;
false ->
case Seconds < 86400 of
true ->
H = Seconds div 3600,
case H =:= 1 of
true ->
<<"1 hour"/utf8>>;
false ->
<<(erlang:integer_to_binary(H))/binary,
" hours"/utf8>>
end;
false ->
D = Seconds div 86400,
case D =:= 1 of
true ->
<<"1 day"/utf8>>;
false ->
<<(erlang:integer_to_binary(D))/binary,
" days"/utf8>>
end
end
end
end.
-file("src/humanize.gleam", 621).
?DOC(" Precise duration with multiple units, joined by commas.\n").
-spec duration_precise(integer()) -> binary().
duration_precise(Seconds) ->
D = Seconds div 86400,
H = (Seconds rem 86400) div 3600,
M = (Seconds rem 3600) div 60,
S = Seconds rem 60,
Parts = [],
Parts@1 = case D > 0 of
true ->
case D =:= 1 of
true ->
lists:append(Parts, [<<"1 day"/utf8>>]);
false ->
lists:append(
Parts,
[<<(erlang:integer_to_binary(D))/binary, " days"/utf8>>]
)
end;
false ->
Parts
end,
Parts@2 = case H > 0 of
true ->
case H =:= 1 of
true ->
lists:append(Parts@1, [<<"1 hour"/utf8>>]);
false ->
lists:append(
Parts@1,
[<<(erlang:integer_to_binary(H))/binary, " hours"/utf8>>]
)
end;
false ->
Parts@1
end,
Parts@3 = case M > 0 of
true ->
case M =:= 1 of
true ->
lists:append(Parts@2, [<<"1 minute"/utf8>>]);
false ->
lists:append(
Parts@2,
[<<(erlang:integer_to_binary(M))/binary,
" minutes"/utf8>>]
)
end;
false ->
Parts@2
end,
Parts@4 = case S > 0 of
true ->
case S =:= 1 of
true ->
lists:append(Parts@3, [<<"1 second"/utf8>>]);
false ->
lists:append(
Parts@3,
[<<(erlang:integer_to_binary(S))/binary,
" seconds"/utf8>>]
)
end;
false ->
Parts@3
end,
join_strings(Parts@4, <<", "/utf8>>).
-file("src/humanize.gleam", 683).
?DOC(" Join a list with a custom conjunction and Oxford comma option.\n").
-spec list_locale(list(binary()), binary(), boolean()) -> binary().
list_locale(Items, Conj, Oxford) ->
case Items of
[] ->
<<""/utf8>>;
[A] ->
A;
[A@1, B] ->
<<<<A@1/binary, Conj/binary>>/binary, B/binary>>;
_ ->
Conj_full = case Conj /= <<""/utf8>> of
true ->
Conj;
false ->
case Oxford of
true ->
<<", and "/utf8>>;
false ->
<<" and "/utf8>>
end
end,
join_with_conj(Items, Conj_full)
end.
-file("src/humanize.gleam", 667).
?DOC(" Join a list of strings into a human-readable sentence fragment.\n").
-spec list(list(binary())) -> binary().
list(Items) ->
list_locale(Items, <<""/utf8>>, false).
-file("src/humanize.gleam", 672).
?DOC(" Join a list using the Oxford comma when appropriate.\n").
-spec list_with_oxford(list(binary())) -> binary().
list_with_oxford(Items) ->
list_locale(Items, <<""/utf8>>, true).
-file("src/humanize.gleam", 678).
?DOC(" Join a list using an ampersand as the final conjunction.\n").
-spec list_with_ampersand(list(binary())) -> binary().
list_with_ampersand(Items) ->
list_locale(Items, <<" & "/utf8>>, false).
-file("src/humanize.gleam", 704).
?DOC(" Return the English ordinal suffix for an integer (e.g., 1st, 2nd).\n").
-spec ordinal(integer()) -> binary().
ordinal(N) ->
Abs = case N < 0 of
true ->
- N;
false ->
N
end,
Mod100 = Abs rem 100,
Mod10 = Abs rem 10,
Suffix = case Mod100 of
11 ->
<<"th"/utf8>>;
12 ->
<<"th"/utf8>>;
13 ->
<<"th"/utf8>>;
_ ->
case Mod10 of
1 ->
<<"st"/utf8>>;
2 ->
<<"nd"/utf8>>;
3 ->
<<"rd"/utf8>>;
_ ->
<<"th"/utf8>>
end
end,
<<(erlang:integer_to_binary(N))/binary, Suffix/binary>>.