-module(g18n). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/g18n.gleam"). -export([context_variants/2, new_translations/0, add_translation/3, add_context_translation/4, get_keys_with_prefix/2, extract_placeholders/1, validate_translation_parameters/4, find_unused_translations/2, validate_translations/3, translation_coverage/2, export_validation_report/1, translations_from_json/1, translations_to_json/1, translations_from_nested_json/1, translations_to_nested_json/1, main/0, new_translator/2, with_fallback/3, locale/1, fallback_locale/1, translations/1, fallback_translations/1, translate/2, translate_with_params/3, translate_with_context/3, translate_with_context_and_params/4, translate_plural/3, translate_plural_with_params/4, namespace/2, translate_cardinal/3, translate_ordinal/3, translate_range/4, translate_ordinal_with_params/4, translate_range_with_params/5, format_number/3, format_relative_time/3, format_time/3, format_datetime/4, format_date/3, new_format_params/0, add_param/3]). -export_type([translator/0, translation_context/0, translations/0, relative_duration/0, time_relative/0, date_time_format/0, number_format/0, validation_error/0, validation_report/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. -opaque translator() :: {translator, g18n@locale:locale(), translations(), gleam@option:option(g18n@locale:locale()), gleam@option:option(translations())}. -type translation_context() :: no_context | {context, binary()}. -opaque translations() :: {translations, trie:trie(binary(), binary())}. -type relative_duration() :: {seconds, integer()} | {minutes, integer()} | {hours, integer()} | {days, integer()} | {weeks, integer()} | {months, integer()} | {years, integer()}. -type time_relative() :: past | future. -type date_time_format() :: short | medium | long | full | {custom, binary()}. -type number_format() :: {decimal, integer()} | {currency, binary(), integer()} | {percentage, integer()} | {scientific, integer()} | compact. -type validation_error() :: {missing_translation, binary(), g18n@locale:locale()} | {missing_parameter, binary(), binary(), g18n@locale:locale()} | {unused_parameter, binary(), binary(), g18n@locale:locale()} | {invalid_plural_form, binary(), list(binary()), g18n@locale:locale()} | {empty_translation, binary(), g18n@locale:locale()}. -type validation_report() :: {validation_report, list(validation_error()), list(validation_error()), integer(), integer(), float()}. -file("src/g18n.gleam", 250). ?DOC( " Get all context variants for a given base key.\n" "\n" " Returns all translations that match the base key with different contexts.\n" " Useful for discovering available contexts for a particular key.\n" "\n" " ## Examples\n" " ```gleam\n" " let translations = g18n.translations()\n" " |> g18n.add_translation(\"bank\", \"bank\")\n" " |> g18n.add_context_translation(\"bank\", \"financial\", \"financial institution\")\n" " |> g18n.add_context_translation(\"bank\", \"river\", \"riverbank\")\n" " \n" " g18n.get_context_variants(translations, \"bank\")\n" " // [#(\"bank\", \"bank\"), #(\"bank@financial\", \"financial institution\"), #(\"bank@river\", \"riverbank\")]\n" " ```\n" ). -spec context_variants(translations(), binary()) -> list({binary(), binary()}). context_variants(Translations, Base_key) -> _pipe = trie:fold( erlang:element(2, Translations), [], fun(Acc, Key_parts, Value) -> Full_key = gleam@string:join(Key_parts, <<"."/utf8>>), case gleam_stdlib:string_starts_with(Full_key, Base_key) of true -> [{Full_key, Value} | Acc]; false -> Acc end end ), lists:reverse(_pipe). -file("src/g18n.gleam", 273). ?DOC( " Create a new empty translations container.\n" "\n" " ## Examples\n" " ```gleam\n" " let translations = g18n.new()\n" " |> g18n.add_translation(\"hello\", \"Hello\")\n" " |> g18n.add_translation(\"goodbye\", \"Goodbye\")\n" " ```\n" ). -spec new_translations() -> translations(). new_translations() -> {translations, trie:new()}. -file("src/g18n.gleam", 288). ?DOC( " Add a translation key-value pair to a translations container.\n" "\n" " Supports hierarchical keys using dot notation for organization.\n" "\n" " ## Examples\n" " ```gleam\n" " let translations = g18n.new()\n" " |> g18n.add_translation(\"ui.button.save\", \"Save\")\n" " |> g18n.add_translation(\"ui.button.cancel\", \"Cancel\")\n" " |> g18n.add_translation(\"user.name\", \"Name\")\n" " ```\n" ). -spec add_translation(translations(), binary(), binary()) -> translations(). add_translation(Translations, Key, Value) -> Key_parts = gleam@string:split(Key, <<"."/utf8>>), {translations, trie:insert(erlang:element(2, Translations), Key_parts, Value)}. -file("src/g18n.gleam", 225). ?DOC( " Add a context-sensitive translation to a translations container.\n" "\n" " Helper function to add translations with context using the `key@context` format.\n" "\n" " ## Examples\n" " ```gleam\n" " let translations = g18n.translations()\n" " |> g18n.add_context_translation(\"bank\", \"financial\", \"financial institution\")\n" " |> g18n.add_context_translation(\"bank\", \"river\", \"riverbank\")\n" " |> g18n.add_context_translation(\"bank\", \"turn\", \"lean to one side\")\n" " ```\n" ). -spec add_context_translation(translations(), binary(), binary(), binary()) -> translations(). add_context_translation(Translations, Key, Context, Value) -> Context_key = <<<>/binary, Context/binary>>, add_translation(Translations, Context_key, Value). -file("src/g18n.gleam", 311). ?DOC( " Get all translation keys that start with a given prefix.\n" "\n" " Useful for finding all keys within a specific namespace.\n" "\n" " ## Examples\n" " ```gleam\n" " let translations = g18n.new()\n" " |> g18n.add_translation(\"ui.button.save\", \"Save\")\n" " |> g18n.add_translation(\"ui.button.cancel\", \"Cancel\")\n" " |> g18n.add_translation(\"user.name\", \"Name\")\n" " \n" " g18n.get_keys_with_prefix(translations, \"ui.button\")\n" " // [\"ui.button.save\", \"ui.button.cancel\"]\n" " ```\n" ). -spec get_keys_with_prefix(translations(), binary()) -> list(binary()). get_keys_with_prefix(Translations, Prefix) -> Prefix_parts = gleam@string:split(Prefix, <<"."/utf8>>), _pipe = trie:fold( erlang:element(2, Translations), [], fun(Acc, Key_parts, _) -> Full_key = gleam@string:join(Key_parts, <<"."/utf8>>), case g18n@internal@helpers:has_prefix(Key_parts, Prefix_parts) of true -> [Full_key | Acc]; false -> Acc end end ), lists:reverse(_pipe). -file("src/g18n.gleam", 335). ?DOC( " Extract all parameter placeholders from a template string.\n" "\n" " Returns a list of parameter names found within {braces}.\n" "\n" " ## Examples\n" " ```gleam\n" " g18n.extract_placeholders(\"Hello {name}, you have {count} new {type}\")\n" " // [\"name\", \"count\", \"type\"]\n" " ```\n" ). -spec extract_placeholders(binary()) -> list(binary()). extract_placeholders(Template) -> Placeholder_regex@1 = case gleam@regexp:from_string( <<"\\{([^}]+)\\}"/utf8>> ) of {ok, Placeholder_regex} -> Placeholder_regex; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"g18n"/utf8>>, function => <<"extract_placeholders"/utf8>>, line => 336, value => _assert_fail, start => 10068, 'end' => 10138, pattern_start => 10079, pattern_end => 10100}) end, _pipe = gleam@regexp:scan(Placeholder_regex@1, Template), _pipe@1 = gleam@list:map( _pipe, fun(Match) -> case erlang:element(3, Match) of [{some, Placeholder}] -> Placeholder; _ -> <<""/utf8>> end end ), gleam@list:filter( _pipe@1, fun(Placeholder@1) -> Placeholder@1 /= <<""/utf8>> end ). -file("src/g18n.gleam", 505). ?DOC( " Validate that a translation key has the correct parameters.\n" "\n" " Checks if a specific translation contains all required parameters and\n" " identifies any unused parameters. This ensures parameter consistency\n" " between different language versions of the same translation.\n" "\n" " ## Examples\n" " ```gleam\n" " let assert Ok(locale) = g18n.locale(\"es\")\n" " let translations = g18n.translations()\n" " |> g18n.add_translation(\"user.greeting\", \"Hola {nombre}!\")\n" " |> g18n.add_translation(\"user.stats\", \"Tienes {points} puntos\")\n" " \n" " // Check if Spanish translation has required English parameters\n" " let errors1 = g18n.validate_translation_parameters(\n" " translations, \"user.greeting\", [\"name\"], locale\n" " )\n" " // Returns [MissingParameter(\"user.greeting\", \"name\", locale)] - \"name\" missing, \"nombre\" unused\n" " \n" " let errors2 = g18n.validate_translation_parameters(\n" " translations, \"user.stats\", [\"points\"], locale \n" " )\n" " // Returns [] - parameters match correctly\n" " ```\n" ). -spec validate_translation_parameters( translations(), binary(), list(binary()), g18n@locale:locale() ) -> list(validation_error()). validate_translation_parameters(Translations, Key, Required_params, Locale) -> Key_parts = gleam@string:split(Key, <<"."/utf8>>), case trie:get(erlang:element(2, Translations), Key_parts) of {ok, Template} -> Found_params = extract_placeholders(Template), Missing = gleam@list:filter( Required_params, fun(Param) -> not gleam@list:contains(Found_params, Param) end ), Unused = gleam@list:filter( Found_params, fun(Param@1) -> not gleam@list:contains(Required_params, Param@1) end ), Missing_errors = gleam@list:map( Missing, fun(Param@2) -> {missing_parameter, Key, Param@2, Locale} end ), Unused_warnings = gleam@list:map( Unused, fun(Param@3) -> {unused_parameter, Key, Param@3, Locale} end ), lists:append(Missing_errors, Unused_warnings); {error, _} -> [{missing_translation, Key, Locale}] end. -file("src/g18n.gleam", 679). -spec get_all_translation_keys(translations()) -> list(binary()). get_all_translation_keys(Translations) -> trie:fold( erlang:element(2, Translations), [], fun(Acc, Key_parts, _) -> Key = gleam@string:join(Key_parts, <<"."/utf8>>), [Key | Acc] end ). -file("src/g18n.gleam", 600). ?DOC( " Find translation keys that are not being used in the application.\n" "\n" " Compares all available translation keys against a list of keys actually used\n" " in the application code. Returns keys that exist in translations but are not\n" " referenced, helping identify obsolete translations that can be removed.\n" "\n" " ## Examples\n" " ```gleam\n" " let translations = g18n.translations()\n" " |> g18n.add_translation(\"common.save\", \"Save\")\n" " |> g18n.add_translation(\"common.cancel\", \"Cancel\")\n" " |> g18n.add_translation(\"old.feature\", \"Old Feature\")\n" " |> g18n.add_translation(\"user.profile\", \"Profile\")\n" " \n" " // Keys actually used in application code\n" " let used_keys = [\"common.save\", \"common.cancel\", \"user.profile\"]\n" " \n" " let unused = g18n.find_unused_translations(translations, used_keys)\n" " // unused == [\"old.feature\"] - this key exists but is not used\n" " \n" " // If all keys are used\n" " let all_used = [\"common.save\", \"common.cancel\", \"old.feature\", \"user.profile\"]\n" " let no_unused = g18n.find_unused_translations(translations, all_used)\n" " // no_unused == [] - all translation keys are being used\n" " ```\n" ). -spec find_unused_translations(translations(), list(binary())) -> list(binary()). find_unused_translations(Translations, Used_keys) -> All_keys = get_all_translation_keys(Translations), gleam@list:filter( All_keys, fun(Key) -> not gleam@list:contains(Used_keys, Key) end ). -file("src/g18n.gleam", 686). -spec find_missing_translations( list(binary()), list(binary()), g18n@locale:locale() ) -> list(validation_error()). find_missing_translations(Primary_keys, Target_keys, Locale) -> gleam@list:filter_map( Primary_keys, fun(Key) -> case gleam@list:contains(Target_keys, Key) of true -> {error, nil}; false -> {ok, {missing_translation, Key, Locale}} end end ). -file("src/g18n.gleam", 699). -spec validate_all_parameters( translations(), translations(), g18n@locale:locale() ) -> list(validation_error()). validate_all_parameters( Primary_translations, Target_translations, Target_locale ) -> Target_keys = get_all_translation_keys(Target_translations), gleam@list:flat_map( Target_keys, fun(Key) -> Key_parts = gleam@string:split(Key, <<"."/utf8>>), case {trie:get(erlang:element(2, Primary_translations), Key_parts), trie:get(erlang:element(2, Target_translations), Key_parts)} of {{ok, Primary_template}, {ok, _}} -> Primary_params = extract_placeholders(Primary_template), validate_translation_parameters( Target_translations, Key, Primary_params, Target_locale ); {_, _} -> [] end end ). -file("src/g18n.gleam", 762). -spec validate_single_plural_form( translations(), binary(), g18n@locale:locale() ) -> list(validation_error()). validate_single_plural_form(Translations, Base_key, Locale) -> Required_forms = case g18n@locale:language(Locale) of <<"en"/utf8>> -> [<<"one"/utf8>>, <<"other"/utf8>>]; <<"pt"/utf8>> -> [<<"zero"/utf8>>, <<"one"/utf8>>, <<"other"/utf8>>]; <<"ru"/utf8>> -> [<<"one"/utf8>>, <<"few"/utf8>>, <<"many"/utf8>>]; _ -> [<<"one"/utf8>>, <<"other"/utf8>>] end, Missing_forms = gleam@list:filter( Required_forms, fun(Form) -> Key_parts = gleam@string:split( <<<>/binary, Form/binary>>, <<"."/utf8>> ), case trie:get(erlang:element(2, Translations), Key_parts) of {ok, _} -> false; {error, _} -> true end end ), case Missing_forms of [] -> []; Forms -> [{invalid_plural_form, Base_key, Forms, Locale}] end. -file("src/g18n.gleam", 726). -spec validate_plural_forms(translations(), g18n@locale:locale()) -> list(validation_error()). validate_plural_forms(Translations, Locale) -> All_keys = get_all_translation_keys(Translations), Base_keys = gleam@list:filter_map( All_keys, fun(Key) -> case (gleam_stdlib:contains_string(Key, <<".one"/utf8>>) orelse gleam_stdlib:contains_string( Key, <<".other"/utf8>> )) orelse gleam_stdlib:contains_string(Key, <<".zero"/utf8>>) of true -> Base@3 = case splitter_ffi:split( splitter:new([Key]), <<".one"/utf8>> ) of {Base, <<".one"/utf8>>, _} -> {ok, Base}; _ -> case splitter_ffi:split( splitter:new([Key]), <<".other"/utf8>> ) of {Base@1, <<".other"/utf8>>, _} -> {ok, Base@1}; _ -> case splitter_ffi:split( splitter:new([Key]), <<".zero"/utf8>> ) of {Base@2, <<".zero"/utf8>>, _} -> {ok, Base@2}; _ -> {error, nil} end end end, Base@3; false -> {error, nil} end end ), gleam@list:flat_map( Base_keys, fun(Base_key) -> validate_single_plural_form(Translations, Base_key, Locale) end ). -file("src/g18n.gleam", 789). -spec find_empty_translations(translations(), g18n@locale:locale()) -> list(validation_error()). find_empty_translations(Translations, Locale) -> trie:fold( erlang:element(2, Translations), [], fun(Acc, Key_parts, Value) -> Key = gleam@string:join(Key_parts, <<"."/utf8>>), case gleam@string:trim(Value) of <<""/utf8>> -> [{empty_translation, Key, Locale} | Acc]; _ -> Acc end end ). -file("src/g18n.gleam", 439). ?DOC( " Validate target translations against primary translations.\n" "\n" " Compares a primary set of translations (e.g., English) with target translations\n" " (e.g., Spanish) to identify missing translations, parameter mismatches, invalid\n" " plural forms, and empty translations. Returns a comprehensive validation report\n" " with error details and translation coverage statistics.\n" "\n" " ## Examples\n" " ```gleam\n" " let assert Ok(en) = g18n.locale(\"en\")\n" " let assert Ok(es) = g18n.locale(\"es\")\n" " \n" " let primary = g18n.new()\n" " |> g18n.add_translation(\"welcome\", \"Welcome {name}!\")\n" " |> g18n.add_translation(\"items.one\", \"1 item\")\n" " |> g18n.add_translation(\"items.other\", \"{count} items\")\n" " \n" " let target = g18n.new()\n" " |> g18n.add_translation(\"welcome\", \"¡Bienvenido {nombre}!\") // Parameter mismatch\n" " |> g18n.add_translation(\"items.one\", \"1 artículo\")\n" " // Missing \"items.other\" translation\n" " \n" " let report = g18n.validate_translations(primary, target, es)\n" " // report.errors will contain MissingParameter and MissingTranslation errors\n" " // report.coverage will be 0.67 (2 out of 3 keys translated)\n" " ```\n" ). -spec validate_translations( translations(), translations(), g18n@locale:locale() ) -> validation_report(). validate_translations(Primary_translations, Target_translations, Target_locale) -> Primary_keys = get_all_translation_keys(Primary_translations), Target_keys = get_all_translation_keys(Target_translations), Missing_translations = find_missing_translations( Primary_keys, Target_keys, Target_locale ), Parameter_errors = validate_all_parameters( Primary_translations, Target_translations, Target_locale ), Plural_errors = validate_plural_forms(Target_translations, Target_locale), Empty_errors = find_empty_translations(Target_translations, Target_locale), All_errors = lists:append( [Missing_translations, Parameter_errors, Plural_errors, Empty_errors] ), Total_keys = erlang:length(Primary_keys), Translated_keys = erlang:length(Target_keys), Coverage = case Total_keys of 0 -> +0.0; _ -> case erlang:float(Total_keys) of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> erlang:float(Translated_keys) / Gleam@denominator end end, {validation_report, All_errors, [], Total_keys, Translated_keys, Coverage}. -file("src/g18n.gleam", 802). -spec count_translations(translations()) -> integer(). count_translations(Translations) -> trie:fold( erlang:element(2, Translations), 0, fun(Count, _, _) -> Count + 1 end ). -file("src/g18n.gleam", 562). ?DOC( " Calculate translation coverage percentage.\n" "\n" " Computes the percentage of primary translation keys that have been\n" " translated in the target translations. Returns a float between 0.0 and 1.0\n" " where 1.0 indicates complete coverage (100%).\n" "\n" " ## Examples\n" " ```gleam\n" " let primary = g18n.translations()\n" " |> g18n.add_translation(\"hello\", \"Hello\")\n" " |> g18n.add_translation(\"goodbye\", \"Goodbye\")\n" " |> g18n.add_translation(\"welcome\", \"Welcome\")\n" " \n" " let partial_target = g18n.translations()\n" " |> g18n.add_translation(\"hello\", \"Hola\")\n" " |> g18n.add_translation(\"goodbye\", \"Adiós\")\n" " // Missing \"welcome\" translation\n" " \n" " let coverage = g18n.get_translation_coverage(primary, partial_target)\n" " // coverage == 0.67 (67% - 2 out of 3 keys translated)\n" " \n" " let complete_target = partial_target\n" " |> g18n.add_translation(\"welcome\", \"Bienvenido\")\n" " \n" " let full_coverage = g18n.get_translation_coverage(primary, complete_target)\n" " // full_coverage == 1.0 (100% coverage)\n" " ```\n" ). -spec translation_coverage(translations(), translations()) -> float(). translation_coverage(Primary_translations, Target_translations) -> Primary_count = count_translations(Primary_translations), Target_count = count_translations(Target_translations), case Primary_count of 0 -> +0.0; _ -> case erlang:float(Primary_count) of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> erlang:float(Target_count) / Gleam@denominator end end. -file("src/g18n.gleam", 806). -spec format_validation_errors(list(validation_error())) -> binary(). format_validation_errors(Errors) -> _pipe = Errors, _pipe@1 = gleam@list:map(_pipe, fun(Error) -> case Error of {missing_translation, Key, Locale} -> <<<<<<" - Missing translation for '"/utf8, Key/binary>>/binary, "' in "/utf8>>/binary, (g18n@locale:to_string(Locale))/binary>>; {missing_parameter, Key@1, Param, Locale@1} -> <<<<<<<<<<<<" - Missing parameter '{"/utf8, Param/binary>>/binary, "}' in '"/utf8>>/binary, Key@1/binary>>/binary, "' ("/utf8>>/binary, (g18n@locale:to_string(Locale@1))/binary>>/binary, ")"/utf8>>; {unused_parameter, Key@2, Param@1, Locale@2} -> <<<<<<<<<<<<" - Unused parameter '{"/utf8, Param@1/binary>>/binary, "}' in '"/utf8>>/binary, Key@2/binary>>/binary, "' ("/utf8>>/binary, (g18n@locale:to_string(Locale@2))/binary>>/binary, ")"/utf8>>; {invalid_plural_form, Key@3, Forms, Locale@3} -> <<<<<<<<<<" - Missing plural forms "/utf8, (gleam@string:join(Forms, <<", "/utf8>>))/binary>>/binary, " for '"/utf8>>/binary, Key@3/binary>>/binary, "' in "/utf8>>/binary, (g18n@locale:to_string(Locale@3))/binary>>; {empty_translation, Key@4, Locale@4} -> <<<<<<" - Empty translation for '"/utf8, Key@4/binary>>/binary, "' in "/utf8>>/binary, (g18n@locale:to_string(Locale@4))/binary>> end end), gleam@string:join(_pipe@1, <<"\n"/utf8>>). -file("src/g18n.gleam", 643). ?DOC( " Export a validation report to a formatted string.\n" "\n" " Converts a ValidationReport into a human-readable text format suitable\n" " for display in console output, log files, or CI/CD reports. Includes\n" " coverage statistics, error counts, and detailed error descriptions.\n" "\n" " ## Examples\n" " ```gleam\n" " let assert Ok(en) = g18n.locale(\"en\")\n" " let assert Ok(es) = g18n.locale(\"es\")\n" " \n" " let primary = g18n.translations()\n" " |> g18n.add_translation(\"hello\", \"Hello {name}!\")\n" " |> g18n.add_translation(\"goodbye\", \"Goodbye\")\n" " \n" " let target = g18n.translations()\n" " |> g18n.add_translation(\"hello\", \"Hola {nombre}!\") // Parameter mismatch\n" " // Missing \"goodbye\" translation\n" " \n" " let report = g18n.validate_translations(primary, target, es)\n" " let formatted = g18n.export_validation_report(report)\n" " \n" " // formatted contains:\n" " // \"Translation Validation Report\"\n" " // \"================================\"\n" " // \"Coverage: 50.0%\"\n" " // \"Total Keys: 2\"\n" " // \"Translated: 1\" \n" " // \"Errors: 2\"\n" " // \"Warnings: 0\"\n" " // \n" " // \"ERRORS:\"\n" " // \"Missing translation: 'goodbye' for locale es\"\n" " // \"Missing parameter: 'name' in 'hello' for locale es\"\n" " ```\n" ). -spec export_validation_report(validation_report()) -> binary(). export_validation_report(Report) -> Error_count = erlang:length(erlang:element(2, Report)), Warning_count = erlang:length(erlang:element(3, Report)), Header = <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"Translation Validation Report\n"/utf8, "================================\n"/utf8>>/binary, "Coverage: "/utf8>>/binary, (gleam_stdlib:float_to_string( erlang:element( 6, Report ) * 100.0 ))/binary>>/binary, "%\n"/utf8>>/binary, "Total Keys: "/utf8>>/binary, (erlang:integer_to_binary( erlang:element(4, Report) ))/binary>>/binary, "\n"/utf8>>/binary, "Translated: "/utf8>>/binary, (erlang:integer_to_binary( erlang:element(5, Report) ))/binary>>/binary, "\n"/utf8>>/binary, "Errors: "/utf8>>/binary, (erlang:integer_to_binary(Error_count))/binary>>/binary, "\n"/utf8>>/binary, "Warnings: "/utf8>>/binary, (erlang:integer_to_binary(Warning_count))/binary>>/binary, "\n\n"/utf8>>, Error_section = case Error_count of 0 -> <<""/utf8>>; _ -> <<<<"ERRORS:\n"/utf8, (format_validation_errors(erlang:element(2, Report)))/binary>>/binary, "\n"/utf8>> end, Warning_section = case Warning_count of 0 -> <<""/utf8>>; _ -> <<<<"WARNINGS:\n"/utf8, (format_validation_errors(erlang:element(3, Report)))/binary>>/binary, "\n"/utf8>> end, <<<
>/binary, Warning_section/binary>>. -file("src/g18n.gleam", 862). ?DOC( " Parse a JSON string into a Translations structure.\n" " \n" " Converts a JSON object with dotted keys into an internal trie structure\n" " for efficient translation lookups. The JSON should contain key-value pairs\n" " where keys use dot notation (e.g., \"user.name\", \"welcome.message\") and\n" " values are the translation strings.\n" " \n" " ## Examples\n" " ```gleam\n" " let json = \"{\\\"user.name\\\": \\\"Name\\\", \\\"user.email\\\": \\\"Email\\\"}\"\n" " let assert Ok(translations) = g18n.translations_from_json(json)\n" " ```\n" ). -spec translations_from_json(binary()) -> {ok, translations()} | {error, nil}. translations_from_json(Json_string) -> case gleam@json:parse( Json_string, gleam@dynamic@decode:dict( {decoder, fun gleam@dynamic@decode:decode_string/1}, {decoder, fun gleam@dynamic@decode:decode_string/1} ) ) of {ok, Dict_result} -> Trie_result = gleam@dict:fold( Dict_result, new_translations(), fun(Translations, Key, Value) -> Key_parts = gleam@string:split(Key, <<"."/utf8>>), {translations, trie:insert( erlang:element(2, Translations), Key_parts, Value )} end ), {ok, Trie_result}; {error, _} -> {error, nil} end. -file("src/g18n.gleam", 889). ?DOC( " Convert a Translations structure to a JSON string.\n" " \n" " Converts the internal trie structure back to a JSON object with dotted keys.\n" " This is useful for exporting translations or debugging the current state\n" " of loaded translations.\n" " \n" " ## Examples\n" " ```gleam\n" " let assert Ok(translations) = g18n.translations_from_json(\"{\\\"user.name\\\": \\\"Name\\\"}\")\n" " let json_output = g18n.translations_to_json(translations)\n" " // Returns: {\"user.name\": \"Name\"}\n" " ```\n" ). -spec translations_to_json(translations()) -> binary(). translations_to_json(Translations) -> Dict_translations = trie:fold( erlang:element(2, Translations), maps:new(), fun(Dict_acc, Key_parts, Value) -> Key = gleam@string:join(Key_parts, <<"."/utf8>>), gleam@dict:insert(Dict_acc, Key, Value) end ), _pipe = Dict_translations, _pipe@1 = maps:to_list(_pipe), _pipe@2 = gleam@list:map( _pipe@1, fun(Pair) -> {erlang:element(1, Pair), gleam@json:string(erlang:element(2, Pair))} end ), _pipe@3 = gleam@json:object(_pipe@2), gleam@json:to_string(_pipe@3). -file("src/g18n.gleam", 984). -spec flatten_json_object( gleam@dict:dict(binary(), gleam@dynamic:dynamic_()), binary() ) -> gleam@dict:dict(binary(), binary()). flatten_json_object(Dict_obj, Prefix) -> gleam@dict:fold( Dict_obj, maps:new(), fun(Acc, Key, Value) -> Current_key = case Prefix of <<""/utf8>> -> Key; _ -> <<<>/binary, Key/binary>> end, case gleam@dynamic@decode:run( Value, gleam@dynamic@decode:dict( {decoder, fun gleam@dynamic@decode:decode_string/1}, {decoder, fun gleam@dynamic@decode:decode_dynamic/1} ) ) of {ok, Nested_dict} -> Nested_flattened = flatten_json_object( Nested_dict, Current_key ), gleam@dict:fold( Nested_flattened, Acc, fun gleam@dict:insert/3 ); {error, _} -> case gleam@dynamic@decode:run( Value, {decoder, fun gleam@dynamic@decode:decode_string/1} ) of {ok, Str_value} -> gleam@dict:insert(Acc, Current_key, Str_value); {error, _} -> Acc end end end ). -file("src/g18n.gleam", 939). ?DOC( " Import translations from nested JSON format.\n" "\n" " Converts nested JSON objects to the internal flat trie structure.\n" " This is the industry-standard format used by most i18n libraries like\n" " react-i18next, Vue i18n, and Angular i18n.\n" "\n" " ## Parameters\n" " - `json_string`: JSON string with nested structure\n" "\n" " ## Returns\n" " `Result(Translations, String)` - Success with translations or error message\n" "\n" " ## Examples\n" " ```gleam\n" " let nested_json = \"\n" " {\n" " \\\"ui\\\": {\n" " \\\"button\\\": {\n" " \\\"save\\\": \\\"Save\\\",\n" " \\\"cancel\\\": \\\"Cancel\\\"\n" " }\n" " },\n" " \\\"user\\\": {\n" " \\\"name\\\": \\\"Name\\\",\n" " \\\"email\\\": \\\"Email\\\"\n" " }\n" " }\"\n" " \n" " let assert Ok(translations) = g18n.translations_from_nested_json(nested_json)\n" " // Converts to flat keys: \"ui.button.save\", \"ui.button.cancel\", etc.\n" " ```\n" ). -spec translations_from_nested_json(binary()) -> {ok, translations()} | {error, binary()}. translations_from_nested_json(Json_string) -> case gleam@json:parse( Json_string, gleam@dynamic@decode:dict( {decoder, fun gleam@dynamic@decode:decode_string/1}, {decoder, fun gleam@dynamic@decode:decode_dynamic/1} ) ) of {ok, Dict_result} -> Flattened_dict = flatten_json_object(Dict_result, <<""/utf8>>), Trie_result = gleam@dict:fold( Flattened_dict, new_translations(), fun(Trie, Key, Value) -> Key_parts = gleam@string:split(Key, <<"."/utf8>>), {translations, trie:insert(erlang:element(2, Trie), Key_parts, Value)} end ), {ok, Trie_result}; {error, _} -> {error, <<"Failed to parse nested JSON: "/utf8>>} end. -file("src/g18n.gleam", 1055). -spec extract_dict_from_json(gleam@json:json()) -> gleam@dict:dict(binary(), gleam@json:json()). extract_dict_from_json(Json_val) -> case Json_val of _ -> maps:new() end. -file("src/g18n.gleam", 1035). -spec insert_at_path( gleam@dict:dict(binary(), gleam@json:json()), list(binary()), gleam@json:json() ) -> gleam@dict:dict(binary(), gleam@json:json()). insert_at_path(Dict_acc, Key_parts, Value) -> case Key_parts of [] -> Dict_acc; [Single_key] -> gleam@dict:insert(Dict_acc, Single_key, Value); [First_key | Remaining_keys] -> Existing = case gleam_stdlib:map_get(Dict_acc, First_key) of {ok, Json_obj} -> extract_dict_from_json(Json_obj); {error, _} -> maps:new() end, Updated = insert_at_path(Existing, Remaining_keys, Value), gleam@dict:insert( Dict_acc, First_key, begin _pipe = maps:to_list(Updated), gleam@json:object(_pipe) end ) end. -file("src/g18n.gleam", 1024). -spec build_nested_structure(list({list(binary()), binary()})) -> gleam@json:json(). build_nested_structure(Pairs) -> _pipe = Pairs, _pipe@1 = gleam@list:fold( _pipe, maps:new(), fun(Acc, Pair) -> {Key_parts, Value} = Pair, insert_at_path(Acc, Key_parts, gleam@json:string(Value)) end ), _pipe@2 = maps:to_list(_pipe@1), gleam@json:object(_pipe@2). -file("src/g18n.gleam", 1012). -spec trie_to_nested_json(translations()) -> gleam@json:json(). trie_to_nested_json(Translations) -> All_pairs = trie:fold( erlang:element(2, Translations), [], fun(Acc, Key_parts, Value) -> [{Key_parts, Value} | Acc] end ), build_nested_structure(All_pairs). -file("src/g18n.gleam", 977). ?DOC( " Export translations to nested JSON format.\n" "\n" " Converts the internal flat trie structure to nested JSON objects.\n" " This produces the industry-standard format expected by most i18n tools.\n" "\n" " ## Parameters\n" " - `translations`: The translations to export\n" "\n" " ## Returns\n" " `String` - Nested JSON representation of the translations\n" "\n" " ## Examples\n" " ```gleam\n" " let translations = g18n.translations()\n" " |> g18n.add_translation(\"ui.button.save\", \"Save\")\n" " |> g18n.add_translation(\"ui.button.cancel\", \"Cancel\")\n" " |> g18n.add_translation(\"user.name\", \"Name\")\n" " \n" " let nested_json = g18n.translations_to_nested_json(translations)\n" " // Returns: {\"ui\":{\"button\":{\"save\":\"Save\",\"cancel\":\"Cancel\"}},\"user\":{\"name\":\"Name\"}}\n" " ```\n" ). -spec translations_to_nested_json(translations()) -> binary(). translations_to_nested_json(Translations) -> _pipe = trie_to_nested_json(Translations), gleam@json:to_string(_pipe). -file("src/g18n.gleam", 1132). -spec help_command() -> nil. help_command() -> gleam_stdlib:println(<<"g18n CLI - Internationalization for Gleam"/utf8>>), gleam_stdlib:println(<<""/utf8>>), gleam_stdlib:println(<<"Commands:"/utf8>>), gleam_stdlib:println( <<" generate Generate Gleam module from flat JSON files"/utf8>> ), gleam_stdlib:println( <<" generate_nested Generate Gleam module from nested JSON files (industry standard)"/utf8>> ), gleam_stdlib:println(<<" help Show this help message"/utf8>>), gleam_stdlib:println(<<""/utf8>>), gleam_stdlib:println(<<"Flat JSON usage:"/utf8>>), gleam_stdlib:println( <<" Place flat JSON files in src//translations/"/utf8>> ), gleam_stdlib:println( <<" Example: {\"ui.button.save\": \"Save\", \"user.name\": \"Name\"}"/utf8>> ), gleam_stdlib:println( <<" Run 'gleam run generate' to create the translations module"/utf8>> ), gleam_stdlib:println(<<""/utf8>>), gleam_stdlib:println(<<"Nested JSON usage:"/utf8>>), gleam_stdlib:println( <<" Place nested JSON files in src//translations/"/utf8>> ), gleam_stdlib:println( <<" Example: {\"ui\": {\"button\": {\"save\": \"Save\"}}, \"user\": {\"name\": \"Name\"}}"/utf8>> ), gleam_stdlib:println( <<" Run 'gleam run generate_nested' to create the translations module"/utf8>> ), gleam_stdlib:println(<<""/utf8>>), gleam_stdlib:println(<<"Supported formats:"/utf8>>), gleam_stdlib:println(<<" ✅ Flat JSON (g18n optimized)"/utf8>>), gleam_stdlib:println( <<" ✅ Nested JSON (react-i18next, Vue i18n, Angular i18n compatible)"/utf8>> ), gleam_stdlib:println(<<""/utf8>>). -file("src/g18n.gleam", 1212). -spec escape_string(binary()) -> binary(). escape_string(Str) -> _pipe = Str, _pipe@1 = gleam@string:replace(_pipe, <<"\\"/utf8>>, <<"\\\\"/utf8>>), _pipe@2 = gleam@string:replace(_pipe@1, <<"\""/utf8>>, <<"\\\""/utf8>>), _pipe@3 = gleam@string:replace(_pipe@2, <<"\n"/utf8>>, <<"\\n"/utf8>>), _pipe@4 = gleam@string:replace(_pipe@3, <<"\r"/utf8>>, <<"\\r"/utf8>>), gleam@string:replace(_pipe@4, <<"\t"/utf8>>, <<"\\t"/utf8>>). -file("src/g18n.gleam", 1221). -spec find_root(binary()) -> binary(). find_root(Path) -> Toml = filepath:join(Path, <<"gleam.toml"/utf8>>), case simplifile_erl:is_file(Toml) of {ok, false} -> find_root(filepath:join(Path, <<".."/utf8>>)); {error, _} -> find_root(filepath:join(Path, <<".."/utf8>>)); {ok, true} -> Path end. -file("src/g18n.gleam", 1166). -spec format() -> {ok, binary()} | {error, snag:snag()}. format() -> _pipe = shellout:command( <<"gleam"/utf8>>, [<<"format"/utf8>>], find_root(<<"."/utf8>>), [] ), snag:map_error( _pipe, fun(_) -> <<"Could not format generated file"/utf8>> end ). -file("src/g18n.gleam", 1190). -spec get_project_name() -> {ok, binary()} | {error, snag:snag()}. get_project_name() -> Root = find_root(<<"."/utf8>>), Toml_path = filepath:join(Root, <<"gleam.toml"/utf8>>), begin Result = begin _pipe = simplifile:read(Toml_path), snag:map_error( _pipe, fun(_) -> <<"Could not read gleam.toml"/utf8>> end ) end, case Result of {ok, X} -> begin Result@1 = begin _pipe@1 = tom:parse(X), snag:map_error( _pipe@1, fun(_) -> <<"Could not parse gleam.toml"/utf8>> end ) end, case Result@1 of {ok, X@1} -> begin Result@2 = begin _pipe@2 = tom:get_string( X@1, [<<"name"/utf8>>] ), snag:map_error( _pipe@2, fun(_) -> <<"Could not find project name in gleam.toml"/utf8>> end ) end, case Result@2 of {ok, X@2} -> {ok, X@2}; {error, E} -> {error, E} end end; {error, E@1} -> {error, E@1} end end; {error, E@2} -> {error, E@2} end end. -file("src/g18n.gleam", 1230). -spec find_locale_files(binary()) -> {ok, list({binary(), binary()})} | {error, snag:snag()}. find_locale_files(Project_name) -> Root = find_root(<<"."/utf8>>), Translations_dir = begin _pipe = filepath:join(Root, <<"src"/utf8>>), _pipe@1 = filepath:join(_pipe, Project_name), filepath:join(_pipe@1, <<"translations"/utf8>>) end, case simplifile_erl:read_directory(Translations_dir) of {ok, Files} -> Locale_files = begin _pipe@2 = Files, _pipe@3 = gleam@list:filter( _pipe@2, fun(File) -> gleam_stdlib:string_ends_with(File, <<".json"/utf8>>) andalso (File /= <<"translations.json"/utf8>>) end ), _pipe@4 = gleam@list:try_map( _pipe@3, fun(File@1) -> Locale_code = gleam@string:drop_end(File@1, 5), File_path = filepath:join(Translations_dir, File@1), Locale_code@1 = gleam@string:replace( Locale_code, <<"-"/utf8>>, <<"_"/utf8>> ), gleam@bool:guard( (string:length(Locale_code@1) /= 2) andalso (string:length( Locale_code@1 ) /= 5), snag:error( <<"Locale code must be 2 or 5 characters (e.g., 'en' or 'en-US'): "/utf8, Locale_code@1/binary>> ), fun() -> {ok, {Locale_code@1, File_path}} end ) end ), snag:context(_pipe@4, <<"Error processing locale files"/utf8>>) end, case Locale_files of {ok, []} -> snag:error( <<<<"No locale JSON files found in "/utf8, Translations_dir/binary>>/binary, "\nLooking for files like en.json, es.json, pt.json, etc."/utf8>> ); {ok, Files@1} -> {ok, Files@1}; {error, Msg} -> {error, Msg} end; {error, _} -> snag:error( <<"Could not read translations directory: "/utf8, Translations_dir/binary>> ) end. -file("src/g18n.gleam", 1372). -spec generate_single_locale_functions(binary(), translations()) -> binary(). generate_single_locale_functions(Locale_code, Translations) -> Dict_translations = trie:fold( erlang:element(2, Translations), maps:new(), fun(Dict_acc, Key_parts, Value) -> Key = gleam@string:join(Key_parts, <<"."/utf8>>), gleam@dict:insert(Dict_acc, Key, Value) end ), Translations_list = begin _pipe = Dict_translations, _pipe@1 = maps:to_list(_pipe), _pipe@2 = gleam@list:map( _pipe@1, fun(Pair) -> <<<<<<<<" |> g18n.add_translation(\""/utf8, (erlang:element(1, Pair))/binary>>/binary, "\", \""/utf8>>/binary, (escape_string(erlang:element(2, Pair)))/binary>>/binary, "\")"/utf8>> end ), gleam@string:join(_pipe@2, <<"\n"/utf8>>) end, Translations_func = <<<<<<<<"pub fn "/utf8, Locale_code/binary>>/binary, "_translations() -> g18n.Translations {\n g18n.new_translations()\n"/utf8>>/binary, Translations_list/binary>>/binary, "\n}"/utf8>>, Locale_func = <<<<<<<<<<<<"pub fn "/utf8, Locale_code/binary>>/binary, "_locale() -> locale.Locale {\n let assert Ok(locale) = locale.new(\""/utf8>>/binary, (gleam@string:replace( Locale_code, <<"_"/utf8>>, <<"-"/utf8>> ))/binary>>/binary, "\")"/utf8>>/binary, "\nlocale"/utf8>>/binary, "\n}"/utf8>>, Translator_func = <<<<<<<<<<<<<<"pub fn "/utf8, Locale_code/binary>>/binary, "_translator() -> g18n.Translator {\n "/utf8>>/binary, "g18n.new_translator("/utf8>>/binary, Locale_code/binary>>/binary, "_locale(), "/utf8>>/binary, Locale_code/binary>>/binary, "_translations())\n\n}"/utf8>>, <<<<<<<>/binary, Locale_func/binary>>/binary, "\n\n"/utf8>>/binary, Translator_func/binary>>. -file("src/g18n.gleam", 1428). -spec generate_all_locales_function(list({binary(), translations()})) -> binary(). generate_all_locales_function(Locale_data) -> Locale_list = begin _pipe = Locale_data, _pipe@1 = gleam@list:map( _pipe, fun(Pair) -> <<<<"\""/utf8, (erlang:element(1, Pair))/binary>>/binary, "\""/utf8>> end ), gleam@string:join(_pipe@1, <<", "/utf8>>) end, <<<<"pub fn available_locales() -> List(String) {\n ["/utf8, Locale_list/binary>>/binary, "]\n}"/utf8>>. -file("src/g18n.gleam", 1356). -spec generate_module_content(list({binary(), translations()})) -> binary(). generate_module_content(Locale_data) -> Imports = <<"import g18n\nimport g18n/locale\n\n"/utf8>>, Locale_functions = begin _pipe = Locale_data, _pipe@1 = gleam@list:map( _pipe, fun(Locale_pair) -> {Locale_code, Translations} = Locale_pair, generate_single_locale_functions(Locale_code, Translations) end ), gleam@string:join(_pipe@1, <<"\n\n"/utf8>>) end, All_locales_function = generate_all_locales_function(Locale_data), <<<<<>/binary, "\n\n"/utf8>>/binary, All_locales_function/binary>>. -file("src/g18n.gleam", 1439). -spec list_fold_result( list(LOQ), LOS, fun((LOS, LOQ) -> {ok, LOS} | {error, snag:snag()}) ) -> {ok, LOS} | {error, snag:snag()}. list_fold_result(List, Initial, Func) -> case List of [] -> {ok, Initial}; [Head | Tail] -> case Func(Initial, Head) of {ok, New_acc} -> list_fold_result(Tail, New_acc, Func); {error, Err} -> {error, Err} end end. -file("src/g18n.gleam", 1317). -spec load_all_locales(list({binary(), binary()})) -> {ok, list({binary(), translations()})} | {error, snag:snag()}. load_all_locales(Locale_files) -> _pipe@2 = list_fold_result( Locale_files, [], fun(Acc, Locale_file) -> {Locale_code, File_path} = Locale_file, gleam@result:'try'( begin _pipe = simplifile:read(File_path), snag:map_error( _pipe, fun(_) -> <<"Could not read "/utf8, File_path/binary>> end ) end, fun(Content) -> gleam@result:'try'( begin _pipe@1 = translations_from_json(Content), snag:map_error( _pipe@1, fun(_) -> <<"Could not parse JSON in "/utf8, File_path/binary>> end ) end, fun(Translations) -> {ok, [{Locale_code, Translations} | Acc]} end ) end ) end ), case _pipe@2 of {ok, X} -> {ok, lists:reverse(X)}; {error, E} -> {error, E} end. -file("src/g18n.gleam", 1277). -spec write_module(binary(), list({binary(), binary()})) -> {ok, binary()} | {error, snag:snag()}. write_module(Project_name, Locale_files) -> begin Result = load_all_locales(Locale_files), case Result of {ok, X} -> Root = find_root(<<"."/utf8>>), Output_path = begin _pipe = filepath:join(Root, <<"src"/utf8>>), _pipe@1 = filepath:join(_pipe, Project_name), filepath:join(_pipe@1, <<"translations.gleam"/utf8>>) end, Module_content = generate_module_content(X), _pipe@2 = simplifile:write(Output_path, Module_content), _pipe@3 = snag:map_error( _pipe@2, fun(_) -> <<"Could not write translations module at: "/utf8, Output_path/binary>> end ), case _pipe@3 of {ok, X@1} -> {ok, begin _@1 = X@1, Output_path end}; {error, E} -> {error, E} end; {error, E@1} -> {error, E@1} end end. -file("src/g18n.gleam", 1171). -spec generate_translations() -> {ok, binary()} | {error, snag:snag()}. generate_translations() -> begin Result = get_project_name(), case Result of {ok, X} -> Project_name = X, begin Result@1 = find_locale_files(Project_name), case Result@1 of {ok, X@1} -> begin Result@2 = write_module(Project_name, X@1), case Result@2 of {ok, X@2} -> Output_path = X@2, begin Result@3 = format(), case Result@3 of {ok, X@3} -> _@1 = X@3, {ok, Output_path}; {error, E} -> {error, E} end end; {error, E@1} -> {error, E@1} end end; {error, E@2} -> {error, E@2} end end; {error, E@3} -> {error, E@3} end end. -file("src/g18n.gleam", 1112). -spec generate_command() -> nil. generate_command() -> case generate_translations() of {ok, Path} -> gleam_stdlib:println( <<"🌏Generated translation modules from flat JSON"/utf8>> ), gleam_stdlib:println(<<" "/utf8, Path/binary>>); {error, Msg} -> gleam_stdlib:println_error(snag:pretty_print(Msg)) end. -file("src/g18n.gleam", 1335). -spec load_all_locales_from_nested(list({binary(), binary()})) -> {ok, list({binary(), translations()})} | {error, snag:snag()}. load_all_locales_from_nested(Locale_files) -> _pipe@2 = list_fold_result( Locale_files, [], fun(Acc, Locale_file) -> {Locale_code, File_path} = Locale_file, gleam@result:'try'( begin _pipe = simplifile:read(File_path), snag:map_error( _pipe, fun(_) -> <<"Could not read "/utf8, File_path/binary>> end ) end, fun(Content) -> gleam@result:'try'( begin _pipe@1 = translations_from_nested_json(Content), snag:map_error( _pipe@1, fun(E) -> <<<<<<"Could not parse nested JSON in "/utf8, File_path/binary>>/binary, ": "/utf8>>/binary, E/binary>> end ) end, fun(Translations) -> {ok, [{Locale_code, Translations} | Acc]} end ) end ) end ), _pipe@3 = case _pipe@2 of {ok, X} -> {ok, lists:reverse(X)}; {error, E@1} -> {error, E@1} end, snag:context(_pipe@3, <<"Error loading nested JSON locale files"/utf8>>). -file("src/g18n.gleam", 1297). -spec write_module_from_nested(binary(), list({binary(), binary()})) -> {ok, binary()} | {error, snag:snag()}. write_module_from_nested(Project_name, Locale_files) -> begin Result = load_all_locales_from_nested(Locale_files), case Result of {ok, X} -> Root = find_root(<<"."/utf8>>), Output_path = begin _pipe = filepath:join(Root, <<"src"/utf8>>), _pipe@1 = filepath:join(_pipe, Project_name), filepath:join(_pipe@1, <<"translations.gleam"/utf8>>) end, Module_content = generate_module_content(X), _pipe@2 = simplifile:write(Output_path, Module_content), _pipe@3 = snag:map_error( _pipe@2, fun(_) -> <<"Could not write translations module from nested JSON at: "/utf8, Output_path/binary>> end ), case _pipe@3 of {ok, X@1} -> {ok, begin _@1 = X@1, Output_path end}; {error, E} -> {error, E} end; {error, E@1} -> {error, E@1} end end. -file("src/g18n.gleam", 1179). -spec generate_nested_translations() -> {ok, binary()} | {error, snag:snag()}. generate_nested_translations() -> begin Result = get_project_name(), case Result of {ok, X} -> Project_name = X, begin Result@1 = find_locale_files(Project_name), case Result@1 of {ok, X@1} -> begin Result@2 = write_module_from_nested( Project_name, X@1 ), case Result@2 of {ok, X@2} -> Output_path = X@2, begin Result@3 = format(), case Result@3 of {ok, X@3} -> _@1 = X@3, {ok, Output_path}; {error, E} -> {error, E} end end; {error, E@1} -> {error, E@1} end end; {error, E@2} -> {error, E@2} end end; {error, E@3} -> {error, E@3} end end. -file("src/g18n.gleam", 1122). -spec generate_nested_command() -> nil. generate_nested_command() -> case generate_nested_translations() of {ok, Path} -> gleam_stdlib:println( <<"🌏Generated translation modules from nested JSON"/utf8>> ), gleam_stdlib:println(<<" "/utf8, Path/binary>>); {error, Msg} -> gleam_stdlib:println(snag:pretty_print(Msg)) end. -file("src/g18n.gleam", 1100). ?DOC( " Main entry point for the g18n CLI tool.\n" " \n" " Handles command-line arguments and dispatches to appropriate command handlers.\n" " Currently supports 'generate' command to create Gleam translation modules\n" " and 'help' command to display usage information.\n" " \n" " ## Supported Commands\n" " - `generate`: Generate Gleam modules from translation JSON files\n" " - `help`: Display help information\n" " - No arguments: Display help information\n" " \n" " ## Examples\n" " Run via command line:\n" " ```bash\n" " gleam run generate # Generate translation modules\n" " gleam run help # Show help\n" " gleam run # Show help (default)\n" " ```\n" " Main entry point for the g18n CLI tool.\n" " \n" " Handles command-line arguments and dispatches to appropriate command handlers.\n" " Supports 'generate' for flat JSON, 'generate_nested' for nested JSON,\n" " and 'help' for usage information.\n" " \n" " ## Supported Commands\n" " - `generate`: Generate Gleam modules from flat JSON files\n" " - `generate_nested`: Generate Gleam modules from nested JSON files\n" " - `help`: Display help information\n" " - No arguments: Display help information\n" " \n" " ## Examples\n" " Run via command line:\n" " ```bash\n" " gleam run generate # Generate from flat JSON files\n" " gleam run generate_nested # Generate from nested JSON files \n" " gleam run help # Show help\n" " gleam run # Show help (default)\n" " ```\n" ). -spec main() -> nil. main() -> case erlang:element(4, argv:load()) of [<<"generate"/utf8>>] -> generate_command(); [<<"generate_nested"/utf8>>] -> generate_nested_command(); [<<"help"/utf8>>] -> help_command(); [] -> help_command(); _ -> gleam_stdlib:println( <<"Unknown command. Use 'help' for available commands."/utf8>> ) end. -file("src/g18n.gleam", 1473). ?DOC( " Create a new translator with the specified locale and translations.\n" "\n" " A translator combines a locale with a set of translations to provide\n" " localized text output. This is the main interface for translation operations.\n" "\n" " ## Examples\n" " ```gleam\n" " let assert Ok(locale) = g18n.locale(\"en-US\")\n" " let translations = g18n.translations()\n" " |> g18n.add_translation(\"hello\", \"Hello\")\n" " |> g18n.add_translation(\"welcome\", \"Welcome {name}!\")\n" " \n" " let translator = g18n.translator(locale, translations)\n" " \n" " g18n.translate(translator, \"hello\")\n" " // \"Hello\"\n" " ```\n" ). -spec new_translator(g18n@locale:locale(), translations()) -> translator(). new_translator(Locale, Translations) -> {translator, Locale, Translations, none, none}. -file("src/g18n.gleam", 1508). ?DOC( " Add fallback locale and translations to an existing translator.\n" "\n" " When a translation key is not found in the primary translations,\n" " the translator will fall back to the fallback translations before\n" " returning the original key.\n" "\n" " ## Examples\n" " ```gleam\n" " let assert Ok(en) = g18n.locale(\"en\")\n" " let assert Ok(es) = g18n.locale(\"es\")\n" " \n" " let en_translations = g18n.translations()\n" " |> g18n.add_translation(\"hello\", \"Hello\")\n" " \n" " let es_translations = g18n.translations()\n" " |> g18n.add_translation(\"goodbye\", \"Adiós\")\n" " \n" " let translator = g18n.translator(es, es_translations)\n" " |> g18n.with_fallback(en, en_translations)\n" " \n" " g18n.translate(translator, \"goodbye\") // \"Adiós\"\n" " g18n.translate(translator, \"hello\") // \"Hello\" (from fallback)\n" " ```\n" ). -spec with_fallback(translator(), g18n@locale:locale(), translations()) -> translator(). with_fallback(Translator, Fallback_locale, Fallback_translations) -> {translator, erlang:element(2, Translator), erlang:element(3, Translator), {some, Fallback_locale}, {some, Fallback_translations}}. -file("src/g18n.gleam", 1740). ?DOC( " Get the locale from a translator.\n" "\n" " ## Examples\n" " ```gleam\n" " let assert Ok(locale) = g18n.locale(\"en-US\")\n" " let translator = g18n.translator(locale, g18n.translations())\n" " g18n.get_locale(translator) // Locale(language: \"en\", region: Some(\"US\"))\n" " ```\n" ). -spec locale(translator()) -> g18n@locale:locale(). locale(Translator) -> erlang:element(2, Translator). -file("src/g18n.gleam", 1754). ?DOC( " Get the fallback locale from a translator, if set.\n" "\n" " ## Examples\n" " ```gleam\n" " let assert Ok(en) = g18n.locale(\"en\")\n" " let assert Ok(es) = g18n.locale(\"es\")\n" " let translator = g18n.translator(es, g18n.translations())\n" " |> g18n.with_fallback(en, g18n.translations())\n" " g18n.get_fallback_locale(translator) // Some(Locale(language: \"en\", region: None))\n" " ```\n" ). -spec fallback_locale(translator()) -> gleam@option:option(g18n@locale:locale()). fallback_locale(Translator) -> erlang:element(4, Translator). -file("src/g18n.gleam", 1767). ?DOC( " Get the translations from a translator.\n" "\n" " ## Examples\n" " ```gleam\n" " let translations = g18n.translations()\n" " |> g18n.add_translation(\"hello\", \"Hello\")\n" " let translator = g18n.translator(g18n.locale(\"en\"), translations)\n" " g18n.get_translations(translator) // Returns the translations container\n" " ```\n" ). -spec translations(translator()) -> translations(). translations(Translator) -> erlang:element(3, Translator). -file("src/g18n.gleam", 1781). ?DOC( " Get the fallback translations from a translator, if set.\n" "\n" " ## Examples\n" " ```gleam\n" " let en_translations = g18n.translations()\n" " |> g18n.add_translation(\"hello\", \"Hello\")\n" " let translator = g18n.translator(g18n.locale(\"es\"), g18n.translations())\n" " |> g18n.with_fallback(g18n.locale(\"en\"), en_translations)\n" " g18n.get_fallback_translations(translator) // Some(translations)\n" " ```\n" ). -spec fallback_translations(translator()) -> gleam@option:option(translations()). fallback_translations(Translator) -> erlang:element(5, Translator). -file("src/g18n.gleam", 1785). -spec fallback_translation(translator(), list(binary()), binary()) -> binary(). fallback_translation(Translator, Key_parts, Original_key) -> case erlang:element(5, Translator) of {some, Fallback_trans} -> case trie:get(erlang:element(2, Fallback_trans), Key_parts) of {ok, Translation} -> Translation; {error, nil} -> Original_key end; none -> Original_key end. -file("src/g18n.gleam", 1546). ?DOC( " Translate a key to localized text using the translator.\n" "\n" " Looks up the translation for the given key in the translator's \n" " translations. If not found, tries the fallback translations. \n" " If still not found, returns the original key as fallback.\n" "\n" " Supports hierarchical keys using dot notation for organization.\n" "\n" " ## Examples\n" " ```gleam\n" " let assert Ok(locale) = g18n.locale(\"en\")\n" " let translations = g18n.translations()\n" " |> g18n.add_translation(\"ui.button.save\", \"Save\")\n" " |> g18n.add_translation(\"user.greeting\", \"Hello\")\n" " \n" " let translator = g18n.translator(locale, translations)\n" " \n" " g18n.t(translator, \"ui.button.save\")\n" " // \"Save\"\n" " \n" " g18n.t(translator, \"user.greeting\") \n" " // \"Hello\"\n" " \n" " g18n.t(translator, \"missing.key\")\n" " // \"missing.key\" (fallback to key)\n" " ```\n" ). -spec translate(translator(), binary()) -> binary(). translate(Translator, Key) -> Key_parts = gleam@string:split(Key, <<"."/utf8>>), case trie:get(erlang:element(2, erlang:element(3, Translator)), Key_parts) of {ok, Translation} -> Translation; {error, nil} -> fallback_translation(Translator, Key_parts, Key) end. -file("src/g18n.gleam", 1578). ?DOC( " Translate a key with parameter substitution.\n" "\n" " Performs translation lookup and then substitutes parameters in the \n" " resulting template. Parameters are specified using `{param}` syntax\n" " in the translation template.\n" "\n" " ## Examples\n" " ```gleam\n" " let assert Ok(locale) = g18n.locale(\"en\")\n" " let translations = g18n.translations()\n" " |> g18n.add_translation(\"user.welcome\", \"Welcome {name}!\")\n" " |> g18n.add_translation(\"user.messages\", \"You have {count} new messages\")\n" " \n" " let translator = g18n.translator(locale, translations)\n" " let params = g18n.format_params()\n" " |> g18n.add_param(\"name\", \"Alice\")\n" " |> g18n.add_param(\"count\", \"5\")\n" " \n" " g18n.t_with_params(translator, \"user.welcome\", params)\n" " // \"Welcome Alice!\"\n" " \n" " g18n.t_with_params(translator, \"user.messages\", params)\n" " // \"You have 5 new messages\"\n" " ```\n" ). -spec translate_with_params( translator(), binary(), gleam@dict:dict(binary(), binary()) ) -> binary(). translate_with_params(Translator, Key, Params) -> Template = translate(Translator, Key), g18n@internal@format:format_string(Template, Params). -file("src/g18n.gleam", 1609). ?DOC( " Translate a key with context for disambiguation.\n" "\n" " Context-sensitive translations allow the same key to have different translations\n" " based on the context in which it's used. This is essential for words that have\n" " multiple meanings or grammatical forms in different situations.\n" "\n" " Context keys are stored as `key@context` in the translation files.\n" "\n" " ## Examples\n" " ```gleam\n" " let assert Ok(locale) = g18n.locale(\"en\")\n" " let translations = g18n.translations()\n" " |> g18n.add_translation(\"may\", \"may\") // auxiliary verb\n" " |> g18n.add_translation(\"may@month\", \"May\") // month name\n" " |> g18n.add_translation(\"may@permission\", \"allowed to\") // permission\n" " \n" " let translator = g18n.translator(locale, translations)\n" " \n" " g18n.t_with_context(translator, \"may\", NoContext) // \"may\"\n" " g18n.t_with_context(translator, \"may\", Context(\"month\")) // \"May\" \n" " g18n.t_with_context(translator, \"may\", Context(\"permission\")) // \"allowed to\"\n" " ```\n" ). -spec translate_with_context(translator(), binary(), translation_context()) -> binary(). translate_with_context(Translator, Key, Context) -> Context_key = case Context of no_context -> Key; {context, Ctx} -> <<<>/binary, Ctx/binary>> end, translate(Translator, Context_key). -file("src/g18n.gleam", 1644). ?DOC( " Translate a key with context and parameter substitution.\n" "\n" " Combines context-sensitive translation with parameter formatting.\n" " Useful for complex translations that need both disambiguation and dynamic values.\n" "\n" " ## Examples\n" " ```gleam\n" " let assert Ok(locale) = g18n.locale(\"en\")\n" " let translations = g18n.translations()\n" " |> g18n.add_translation(\"close\", \"close\")\n" " |> g18n.add_translation(\"close@door\", \"Close the {item}\")\n" " |> g18n.add_translation(\"close@application\", \"Close {app_name}\")\n" " \n" " let translator = g18n.translator(locale, translations)\n" " let params = g18n.format_params() |> g18n.add_param(\"item\", \"door\")\n" " \n" " g18n.translate_with_context_and_params(\n" " translator, \n" " \"close\", \n" " Context(\"door\"), \n" " params\n" " ) // \"Close the door\"\n" " ```\n" ). -spec translate_with_context_and_params( translator(), binary(), translation_context(), gleam@dict:dict(binary(), binary()) ) -> binary(). translate_with_context_and_params(Translator, Key, Context, Params) -> Template = translate_with_context(Translator, Key, Context), g18n@internal@format:format_string(Template, Params). -file("src/g18n.gleam", 1686). ?DOC( " Translate with automatic pluralization based on count and locale rules.\n" "\n" " Automatically selects the appropriate plural form based on the count\n" " and the language's pluralization rules. Supports multiple plural forms\n" " including zero, one, two, few, many, and other.\n" "\n" " ## Supported Plural Rules\n" " - **English**: 1 → `.one`, others → `.other`\n" " - **Portuguese**: 0 → `.zero`, 1 → `.one`, others → `.other` \n" " - **Russian**: Complex Slavic rules → `.one`/`.few`/`.many`\n" "\n" " ## Examples\n" " ```gleam\n" " let assert Ok(en_locale) = g18n.locale(\"en\")\n" " let assert Ok(pt_locale) = g18n.locale(\"pt\")\n" " let translations = g18n.translations()\n" " |> g18n.add_translation(\"item.one\", \"1 item\")\n" " |> g18n.add_translation(\"item.other\", \"{count} items\")\n" " |> g18n.add_translation(\"item.zero\", \"no items\")\n" " \n" " let en_translator = g18n.translator(en_locale, translations)\n" " let pt_translator = g18n.translator(pt_locale, translations)\n" " \n" " // English pluralization (1 → one, others → other)\n" " g18n.translate_plural(en_translator, \"item\", 1) // \"1 item\"\n" " g18n.translate_plural(en_translator, \"item\", 5) // \"{count} items\"\n" " \n" " // Portuguese pluralization (0 → zero, 1 → one, others → other) \n" " g18n.translate_plural(pt_translator, \"item\", 0) // \"no items\"\n" " g18n.translate_plural(pt_translator, \"item\", 1) // \"1 item\"\n" " g18n.translate_plural(pt_translator, \"item\", 3) // \"{count} items\"\n" " ```\n" ). -spec translate_plural(translator(), binary(), integer()) -> binary(). translate_plural(Translator, Key, Count) -> Language = erlang:element(2, Translator), Plural_rule = g18n@locale:locale_plural_rule(Language), Plural_key = g18n@locale:plural_key(Key, Count, Plural_rule), Template = translate(Translator, Plural_key), Params = begin _pipe = maps:new(), gleam@dict:insert( _pipe, <<"count"/utf8>>, erlang:integer_to_binary(Count) ) end, g18n@internal@format:format_string(Template, Params). -file("src/g18n.gleam", 1716). ?DOC( " Translate with pluralization and parameter substitution.\n" "\n" " Combines pluralization and parameter substitution in a single function.\n" " First determines the appropriate plural form based on count and locale,\n" " then performs parameter substitution on the resulting template.\n" "\n" " ## Examples\n" " ```gleam\n" " let params = dict.from_list([(\"name\", \"Alice\"), (\"count\", \"3\")])\n" " \n" " g18n.translate_plural_with_params(en_translator, \"user.items\", 3, params)\n" " // \"Alice has 3 items\"\n" " \n" " g18n.translate_plural_with_params(en_translator, \"user.items\", 1, params) \n" " // \"Alice has 1 item\"\n" ). -spec translate_plural_with_params( translator(), binary(), integer(), gleam@dict:dict(binary(), binary()) ) -> binary(). translate_plural_with_params(Translator, Key, Count, Params) -> Language = erlang:element(2, Translator), Plural_rule = g18n@locale:locale_plural_rule(Language), Plural_key = g18n@locale:plural_key(Key, Count, Plural_rule), Template = translate(Translator, Plural_key), All_params = begin _pipe = Params, gleam@dict:insert( _pipe, <<"count"/utf8>>, erlang:integer_to_binary(Count) ) end, g18n@internal@format:format_string(Template, All_params). -file("src/g18n.gleam", 1816). ?DOC( " Get all key-value pairs from translations within a specific namespace.\n" "\n" " Returns tuples of (key, translation) for all keys that start with the namespace prefix.\n" "\n" " ## Examples\n" " ```gleam\n" " let translations = g18n.translations()\n" " |> g18n.add_translation(\"ui.button.save\", \"Save\")\n" " |> g18n.add_translation(\"ui.button.cancel\", \"Cancel\")\n" " |> g18n.add_translation(\"user.name\", \"Name\")\n" " let translator = g18n.translator(g18n.locale(\"en\"), translations)\n" " \n" " g18n.get_namespace(translator, \"ui.button\")\n" " // [#(\"ui.button.save\", \"Save\"), #(\"ui.button.cancel\", \"Cancel\")]\n" " ```\n" ). -spec namespace(translator(), binary()) -> list({binary(), binary()}). namespace(Translator, Namespace) -> Prefix_parts = gleam@string:split(Namespace, <<"."/utf8>>), _pipe = trie:fold( erlang:element(2, erlang:element(3, Translator)), [], fun(Acc, Key_parts, Value) -> Full_key = gleam@string:join(Key_parts, <<"."/utf8>>), case g18n@internal@helpers:has_prefix(Key_parts, Prefix_parts) of true -> [{Full_key, Value} | Acc]; false -> Acc end end ), lists:reverse(_pipe). -file("src/g18n.gleam", 1843). ?DOC( " Translate with cardinal pluralization rules.\n" "\n" " Alias for `translate_plural` that explicitly indicates the use of cardinal\n" " number rules (used for counting: 1 item, 2 items, etc.). This is the\n" " standard pluralization used for most counting scenarios.\n" "\n" " ## Examples\n" " ```gleam\n" " g18n.translate_cardinal(en_translator, \"book\", 1) // \"book.one\" \n" " g18n.translate_cardinal(en_translator, \"book\", 3) // \"book.other\"\n" " g18n.translate_cardinal(pt_translator, \"livro\", 0) // \"livro.zero\" \n" " ```\n" ). -spec translate_cardinal(translator(), binary(), integer()) -> binary(). translate_cardinal(Translator, Key, Count) -> translate_plural(Translator, Key, Count). -file("src/g18n.gleam", 1864). ?DOC( " Translate with ordinal number rules.\n" "\n" " Uses ordinal pluralization rules for position-based numbers (1st, 2nd, 3rd, etc.).\n" " Different languages have different rules for ordinal endings, and this function\n" " applies the appropriate ordinal key suffix based on the position and locale.\n" "\n" " ## Examples\n" " ```gleam\n" " g18n.translate_ordinal(en_translator, \"place\", 1) // \"place.one\" → \"1st place\"\n" " g18n.translate_ordinal(en_translator, \"place\", 2) // \"place.two\" → \"2nd place\" \n" " g18n.translate_ordinal(en_translator, \"place\", 3) // \"place.few\" → \"3rd place\"\n" " g18n.translate_ordinal(en_translator, \"place\", 11) // \"place.other\" → \"11th place\"\n" " ```\n" ). -spec translate_ordinal(translator(), binary(), integer()) -> binary(). translate_ordinal(Translator, Key, Position) -> Language = erlang:element(2, Translator), Ordinal_rule = g18n@locale:ordinal_rule(Language, Position), Ordinal_key = g18n@locale:ordinal_key(Key, Ordinal_rule), translate(Translator, Ordinal_key). -file("src/g18n.gleam", 1887). ?DOC( " Translate for numeric ranges.\n" "\n" " Handles translations for numeric ranges, choosing between single value\n" " and range translations. Uses \".single\" key suffix when from equals to,\n" " and \".range\" suffix for actual ranges.\n" "\n" " ## Examples\n" " ```gleam\n" " g18n.translate_range(en_translator, \"pages\", 5, 5) // \"pages.single\" → \"Page 5\"\n" " g18n.translate_range(en_translator, \"pages\", 1, 10) // \"pages.range\" → \"Pages 1-10\"\n" " g18n.translate_range(en_translator, \"items\", 3, 7) // \"items.range\" → \"Items 3 through 7\"\n" " ```\n" ). -spec translate_range(translator(), binary(), integer(), integer()) -> binary(). translate_range(Translator, Key, From, To) -> Range_key = case {From, To} of {F, T} when F =:= T -> <>; {_, _} -> <> end, translate(Translator, Range_key). -file("src/g18n.gleam", 1917). ?DOC( " Translate ordinal numbers with parameter substitution.\n" "\n" " Combines ordinal number translation with parameter substitution.\n" " Automatically adds `position` and `ordinal` parameters to the provided\n" " parameters for convenient template formatting.\n" "\n" " ## Examples\n" " ```gleam\n" " let params = dict.from_list([(\"name\", \"Alice\")])\n" " \n" " g18n.translate_ordinal_with_params(en_translator, \"winner\", 1, params)\n" " // Template: \"{name} finished in {position}{ordinal} place\" \n" " // Result: \"Alice finished in 1st place\"\n" " \n" " g18n.translate_ordinal_with_params(en_translator, \"winner\", 3, params)\n" " // Result: \"Alice finished in 3rd place\"\n" " ```\n" ). -spec translate_ordinal_with_params( translator(), binary(), integer(), gleam@dict:dict(binary(), binary()) ) -> binary(). translate_ordinal_with_params(Translator, Key, Position, Params) -> Template = translate_ordinal(Translator, Key, Position), Enhanced_params = begin _pipe = Params, _pipe@1 = gleam@dict:insert( _pipe, <<"position"/utf8>>, erlang:integer_to_binary(Position) ), gleam@dict:insert( _pipe@1, <<"ordinal"/utf8>>, g18n@locale:ordinal_suffix(erlang:element(2, Translator), Position) ) end, g18n@internal@format:format_string(Template, Enhanced_params). -file("src/g18n.gleam", 1951). ?DOC( " Translate numeric ranges with parameter substitution.\n" "\n" " Combines range translation with parameter substitution. Automatically\n" " adds `from`, `to`, and `total` parameters to the provided parameters\n" " for convenient template formatting.\n" "\n" " ## Examples \n" " ```gleam\n" " let params = dict.from_list([(\"type\", \"chapters\")])\n" " \n" " g18n.t_range_with_params(en_translator, \"content\", 1, 5, params)\n" " // Template: \"Reading {type} {from} to {to} ({total} total)\"\n" " // Result: \"Reading chapters 1 to 5 (5 total)\"\n" " \n" " g18n.t_range_with_params(en_translator, \"content\", 3, 3, params) \n" " // Uses .single key, Result: \"Reading chapter 3\"\n" " ```\n" ). -spec translate_range_with_params( translator(), binary(), integer(), integer(), gleam@dict:dict(binary(), binary()) ) -> binary(). translate_range_with_params(Translator, Key, From, To, Params) -> Template = translate_range(Translator, Key, From, To), Enhanced_params = begin _pipe = Params, _pipe@1 = gleam@dict:insert( _pipe, <<"from"/utf8>>, erlang:integer_to_binary(From) ), _pipe@2 = gleam@dict:insert( _pipe@1, <<"to"/utf8>>, erlang:integer_to_binary(To) ), gleam@dict:insert( _pipe@2, <<"total"/utf8>>, erlang:integer_to_binary((To - From) + 1) ) end, g18n@internal@format:format_string(Template, Enhanced_params). -file("src/g18n.gleam", 2332). -spec scientific(float(), integer()) -> binary(). scientific(Number, Precision) -> Exponent = case Number of +0.0 -> 0; N -> Abs_n = case N >= +0.0 of true -> N; false -> gleam@float:negate(N) end, Logarithm@1 = case gleam_community@maths:logarithm_10(Abs_n) of {ok, Logarithm} -> Logarithm; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"g18n"/utf8>>, function => <<"scientific"/utf8>>, line => 2340, value => _assert_fail, start => 76447, 'end' => 76499, pattern_start => 76458, pattern_end => 76471}) end, _pipe = math:floor(Logarithm@1), erlang:round(_pipe) end, Power@1 = case gleam@float:power(10.0, erlang:float(Exponent)) of {ok, Power} -> Power; _assert_fail@1 -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"g18n"/utf8>>, function => <<"scientific"/utf8>>, line => 2346, value => _assert_fail@1, start => 76563, 'end' => 76627, pattern_start => 76574, pattern_end => 76583}) end, Mantissa = case Power@1 of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> Number / Gleam@denominator end, Power@3 = case gleam@float:power(10.0, erlang:float(Precision)) of {ok, Power@2} -> Power@2; _assert_fail@2 -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"g18n"/utf8>>, function => <<"scientific"/utf8>>, line => 2348, value => _assert_fail@2, start => 76663, 'end' => 76728, pattern_start => 76674, pattern_end => 76683}) end, Rounded_mantissa = case Power@3 of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator@1 -> erlang:float(erlang:round(Mantissa * Power@3)) / Gleam@denominator@1 end, <<<<(gleam_stdlib:float_to_string(Rounded_mantissa))/binary, "e"/utf8>>/binary, (erlang:integer_to_binary(Exponent))/binary>>. -file("src/g18n.gleam", 2376). -spec decimal_separator(binary()) -> binary(). decimal_separator(Language) -> case Language of <<"pt"/utf8>> -> <<","/utf8>>; <<"es"/utf8>> -> <<","/utf8>>; <<"fr"/utf8>> -> <<","/utf8>>; <<"de"/utf8>> -> <<","/utf8>>; _ -> <<"."/utf8>> end. -file("src/g18n.gleam", 2383). -spec thousands_separator(binary()) -> binary(). thousands_separator(Language) -> case Language of <<"fr"/utf8>> -> <<" "/utf8>>; <<"es"/utf8>> -> <<" "/utf8>>; <<"pt"/utf8>> -> <<" "/utf8>>; <<"it"/utf8>> -> <<" "/utf8>>; <<"de"/utf8>> -> <<"."/utf8>>; <<"at"/utf8>> -> <<"."/utf8>>; <<"ch"/utf8>> -> <<"."/utf8>>; <<"in"/utf8>> -> <<","/utf8>>; _ -> <<","/utf8>> end. -file("src/g18n.gleam", 2394). -spec currency_symbol(binary()) -> binary(). currency_symbol(Currency_code) -> case Currency_code of <<"USD"/utf8>> -> <<"$"/utf8>>; <<"EUR"/utf8>> -> <<"€"/utf8>>; <<"GBP"/utf8>> -> <<"£"/utf8>>; <<"BRL"/utf8>> -> <<"R$"/utf8>>; <<"JPY"/utf8>> -> <<"¥"/utf8>>; _ -> Currency_code end. -file("src/g18n.gleam", 2405). -spec currency_position(binary()) -> binary(). currency_position(Language) -> case Language of <<"en"/utf8>> -> <<"before"/utf8>>; <<"pt"/utf8>> -> <<"before"/utf8>>; <<"es"/utf8>> -> <<"before"/utf8>>; <<"fr"/utf8>> -> <<"before"/utf8>>; <<"de"/utf8>> -> <<"after"/utf8>>; _ -> <<"before"/utf8>> end. -file("src/g18n.gleam", 2414). -spec percent_symbol() -> binary(). percent_symbol() -> <<"%"/utf8>>. -file("src/g18n.gleam", 2418). -spec thousand_suffix(binary()) -> binary(). thousand_suffix(Language) -> case Language of <<"pt"/utf8>> -> <<"mil"/utf8>>; <<"es"/utf8>> -> <<"k"/utf8>>; <<"fr"/utf8>> -> <<"k"/utf8>>; _ -> <<"K"/utf8>> end. -file("src/g18n.gleam", 2427). -spec million_suffix(binary()) -> binary(). million_suffix(Language) -> case Language of <<"pt"/utf8>> -> <<"M"/utf8>>; <<"es"/utf8>> -> <<"M"/utf8>>; <<"fr"/utf8>> -> <<"M"/utf8>>; _ -> <<"M"/utf8>> end. -file("src/g18n.gleam", 2436). -spec billion_suffix(binary()) -> binary(). billion_suffix(Language) -> case Language of <<"pt"/utf8>> -> <<"B"/utf8>>; <<"es"/utf8>> -> <<"B"/utf8>>; <<"fr"/utf8>> -> <<"Md"/utf8>>; _ -> <<"B"/utf8>> end. -file("src/g18n.gleam", 2354). -spec compact(float(), binary()) -> binary(). compact(Number, Language) -> case Number of N when N >= 1000000000.0 -> Billions = N / 1000000000.0, <<(begin _pipe = gleam@float:to_precision(Billions, 1), gleam_stdlib:float_to_string(_pipe) end)/binary, (billion_suffix(Language))/binary>>; N@1 when N@1 >= 1000000.0 -> Millions = N@1 / 1000000.0, <<(begin _pipe@1 = gleam@float:to_precision(Millions, 1), gleam_stdlib:float_to_string(_pipe@1) end)/binary, (million_suffix(Language))/binary>>; N@2 when N@2 >= 1000.0 -> Thousands = N@2 / 1000.0, <<(begin _pipe@2 = gleam@float:to_precision(Thousands, 1), gleam_stdlib:float_to_string(_pipe@2) end)/binary, (thousand_suffix(Language))/binary>>; _ -> erlang:integer_to_binary(erlang:round(Number)) end. -file("src/g18n.gleam", 2473). -spec group_by_threes_simple(list(binary())) -> list(list(binary())). group_by_threes_simple(Chars) -> case Chars of [] -> []; [A] -> [[A]]; [A@1, B] -> [[A@1, B]]; [A@2, B@1, C | Rest] -> [[A@2, B@1, C] | group_by_threes_simple(Rest)] end. -file("src/g18n.gleam", 2460). -spec integer_with_separators(binary(), binary()) -> binary(). integer_with_separators(Integer_str, Separator) -> Chars = begin _pipe = gleam@string:to_graphemes(Integer_str), lists:reverse(_pipe) end, Grouped = group_by_threes_simple(Chars), _pipe@1 = Grouped, _pipe@2 = gleam@list:map(_pipe@1, fun lists:reverse/1), _pipe@3 = gleam@list:map(_pipe@2, fun erlang:list_to_binary/1), _pipe@4 = lists:reverse(_pipe@3), gleam@string:join(_pipe@4, Separator). -file("src/g18n.gleam", 2445). -spec add_thousands_separators(binary(), binary(), binary()) -> binary(). add_thousands_separators(Number_str, Decimal_separator, Thousands_separator) -> case gleam@string:split(Number_str, <<"."/utf8>>) of [Integer_part] -> integer_with_separators(Integer_part, Thousands_separator); [Integer_part@1, Decimal_part] -> <<<<(integer_with_separators(Integer_part@1, Thousands_separator))/binary, Decimal_separator/binary>>/binary, Decimal_part/binary>>; _ -> Number_str end. -file("src/g18n.gleam", 2291). -spec decimal(float(), integer(), binary()) -> binary(). decimal(Number, Precision, Language) -> Decimal_separator = decimal_separator(Language), Thousands_separator = thousands_separator(Language), Formatted_number = begin _pipe = gleam@float:to_precision(Number, Precision), gleam_stdlib:float_to_string(_pipe) end, add_thousands_separators( Formatted_number, Decimal_separator, Thousands_separator ). -file("src/g18n.gleam", 2304). -spec currency(float(), binary(), integer(), binary()) -> binary(). currency(Number, Currency_code, Precision, Language) -> Formatted_amount = decimal(Number, Precision, Language), Currency_symbol = currency_symbol(Currency_code), Currency_position = currency_position(Language), case Currency_position of <<"before"/utf8>> -> <>; <<"after"/utf8>> -> <<<>/binary, Currency_symbol/binary>>; _ -> <> end. -file("src/g18n.gleam", 2321). -spec percentage(float(), integer(), binary()) -> binary(). percentage(Number, Precision, Language) -> Percentage_value = Number * 100.0, Formatted = decimal(Percentage_value, Precision, Language), Percent_symbol = percent_symbol(), case Language of <<"fr"/utf8>> -> <<<>/binary, Percent_symbol/binary>>; _ -> <> end. -file("src/g18n.gleam", 2010). ?DOC( " Format numbers according to locale-specific conventions and format type.\n" "\n" " Provides comprehensive number formatting including decimal separators,\n" " thousands separators, currency symbols, percentage formatting, and \n" " compact notation. Uses proper locale conventions for each language.\n" "\n" " ## Format Types\n" " - `Decimal(precision)`: Standard decimal formatting with locale separators\n" " - `Currency(currency_code, precision)`: Currency with appropriate symbols and placement\n" " - `Percentage(precision)`: Percentage formatting with locale conventions\n" " - `Scientific(precision)`: Scientific notation (simplified)\n" " - `Compact`: Compact notation (1.5K, 2.3M, 1.2B)\n" "\n" " ## Examples\n" " ```gleam\n" " let assert Ok(en_locale) = g18n.locale(\"en\")\n" " let assert Ok(de_locale) = g18n.locale(\"de\")\n" " let translations = g18n.translations()\n" " let en_translator = g18n.translator(en_locale, translations)\n" " let de_translator = g18n.translator(de_locale, translations)\n" " \n" " // Decimal formatting (locale-aware separators)\n" " g18n.format_number(en_translator, 1234.56, g18n.Decimal(2))\n" " // \"1234.56\" (English uses . for decimal)\n" " g18n.format_number(de_translator, 1234.56, g18n.Decimal(2))\n" " // \"1234,56\" (German uses , for decimal)\n" " \n" " // Currency formatting \n" " g18n.format_number(en_translator, 29.99, g18n.Currency(\"USD\", 2))\n" " // \"$29.99\"\n" " g18n.format_number(de_translator, 29.99, g18n.Currency(\"EUR\", 2))\n" " // \"29.99 €\" (German places currency after)\n" " \n" " // Percentage\n" " g18n.format_number(en_translator, 0.75, g18n.Percentage(1))\n" " // \"75.0%\"\n" " \n" " // Compact notation\n" " g18n.format_number(en_translator, 1500000.0, g18n.Compact)\n" " // \"1.5M\"\n" " g18n.format_number(en_translator, 2500.0, g18n.Compact) \n" " // \"2.5K\"\n" " ```\n" ). -spec format_number(translator(), float(), number_format()) -> binary(). format_number(Translator, Number, Format) -> Language = begin _pipe = Translator, _pipe@1 = locale(_pipe), g18n@locale:language(_pipe@1) end, case Format of {decimal, Precision} -> decimal(Number, Precision, Language); {currency, Currency_code, Precision@1} -> currency(Number, Currency_code, Precision@1, Language); {percentage, Precision@2} -> percentage(Number, Precision@2, Language); {scientific, Precision@3} -> scientific(Number, Precision@3); compact -> compact(Number, Language) end. -file("src/g18n.gleam", 2765). -spec month_to_int(gleam@time@calendar:month()) -> integer(). month_to_int(Month) -> case Month of january -> 1; february -> 2; march -> 3; april -> 4; may -> 5; june -> 6; july -> 7; august -> 8; september -> 9; october -> 10; november -> 11; december -> 12 end. -file("src/g18n.gleam", 2864). -spec get_month_name(gleam@time@calendar:month(), binary(), boolean()) -> binary(). get_month_name(Month, Language, Full) -> case {Month, Language, Full} of {january, <<"en"/utf8>>, true} -> <<"January"/utf8>>; {january, <<"en"/utf8>>, false} -> <<"Jan"/utf8>>; {february, <<"en"/utf8>>, true} -> <<"February"/utf8>>; {february, <<"en"/utf8>>, false} -> <<"Feb"/utf8>>; {march, <<"en"/utf8>>, true} -> <<"March"/utf8>>; {march, <<"en"/utf8>>, false} -> <<"Mar"/utf8>>; {april, <<"en"/utf8>>, true} -> <<"April"/utf8>>; {april, <<"en"/utf8>>, false} -> <<"Apr"/utf8>>; {may, <<"en"/utf8>>, true} -> <<"May"/utf8>>; {may, <<"en"/utf8>>, false} -> <<"May"/utf8>>; {june, <<"en"/utf8>>, true} -> <<"June"/utf8>>; {june, <<"en"/utf8>>, false} -> <<"Jun"/utf8>>; {july, <<"en"/utf8>>, true} -> <<"July"/utf8>>; {july, <<"en"/utf8>>, false} -> <<"Jul"/utf8>>; {august, <<"en"/utf8>>, true} -> <<"August"/utf8>>; {august, <<"en"/utf8>>, false} -> <<"Aug"/utf8>>; {september, <<"en"/utf8>>, true} -> <<"September"/utf8>>; {september, <<"en"/utf8>>, false} -> <<"Sep"/utf8>>; {october, <<"en"/utf8>>, true} -> <<"October"/utf8>>; {october, <<"en"/utf8>>, false} -> <<"Oct"/utf8>>; {november, <<"en"/utf8>>, true} -> <<"November"/utf8>>; {november, <<"en"/utf8>>, false} -> <<"Nov"/utf8>>; {december, <<"en"/utf8>>, true} -> <<"December"/utf8>>; {december, <<"en"/utf8>>, false} -> <<"Dec"/utf8>>; {january, <<"pt"/utf8>>, true} -> <<"janeiro"/utf8>>; {january, <<"pt"/utf8>>, false} -> <<"jan"/utf8>>; {february, <<"pt"/utf8>>, true} -> <<"fevereiro"/utf8>>; {february, <<"pt"/utf8>>, false} -> <<"fev"/utf8>>; {march, <<"pt"/utf8>>, true} -> <<"março"/utf8>>; {march, <<"pt"/utf8>>, false} -> <<"mar"/utf8>>; {april, <<"pt"/utf8>>, true} -> <<"abril"/utf8>>; {april, <<"pt"/utf8>>, false} -> <<"abr"/utf8>>; {may, <<"pt"/utf8>>, true} -> <<"maio"/utf8>>; {may, <<"pt"/utf8>>, false} -> <<"mai"/utf8>>; {june, <<"pt"/utf8>>, true} -> <<"junho"/utf8>>; {june, <<"pt"/utf8>>, false} -> <<"jun"/utf8>>; {july, <<"pt"/utf8>>, true} -> <<"julho"/utf8>>; {july, <<"pt"/utf8>>, false} -> <<"jul"/utf8>>; {august, <<"pt"/utf8>>, true} -> <<"agosto"/utf8>>; {august, <<"pt"/utf8>>, false} -> <<"ago"/utf8>>; {september, <<"pt"/utf8>>, true} -> <<"setembro"/utf8>>; {september, <<"pt"/utf8>>, false} -> <<"set"/utf8>>; {october, <<"pt"/utf8>>, true} -> <<"outubro"/utf8>>; {october, <<"pt"/utf8>>, false} -> <<"out"/utf8>>; {november, <<"pt"/utf8>>, true} -> <<"novembro"/utf8>>; {november, <<"pt"/utf8>>, false} -> <<"nov"/utf8>>; {december, <<"pt"/utf8>>, true} -> <<"dezembro"/utf8>>; {december, <<"pt"/utf8>>, false} -> <<"dez"/utf8>>; {january, <<"es"/utf8>>, true} -> <<"enero"/utf8>>; {january, <<"es"/utf8>>, false} -> <<"ene"/utf8>>; {february, <<"es"/utf8>>, true} -> <<"febrero"/utf8>>; {february, <<"es"/utf8>>, false} -> <<"feb"/utf8>>; {march, <<"es"/utf8>>, true} -> <<"marzo"/utf8>>; {march, <<"es"/utf8>>, false} -> <<"mar"/utf8>>; {april, <<"es"/utf8>>, true} -> <<"abril"/utf8>>; {april, <<"es"/utf8>>, false} -> <<"abr"/utf8>>; {may, <<"es"/utf8>>, true} -> <<"mayo"/utf8>>; {may, <<"es"/utf8>>, false} -> <<"may"/utf8>>; {june, <<"es"/utf8>>, true} -> <<"junio"/utf8>>; {june, <<"es"/utf8>>, false} -> <<"jun"/utf8>>; {july, <<"es"/utf8>>, true} -> <<"julio"/utf8>>; {july, <<"es"/utf8>>, false} -> <<"jul"/utf8>>; {august, <<"es"/utf8>>, true} -> <<"agosto"/utf8>>; {august, <<"es"/utf8>>, false} -> <<"ago"/utf8>>; {september, <<"es"/utf8>>, true} -> <<"septiembre"/utf8>>; {september, <<"es"/utf8>>, false} -> <<"sep"/utf8>>; {october, <<"es"/utf8>>, true} -> <<"octubre"/utf8>>; {october, <<"es"/utf8>>, false} -> <<"oct"/utf8>>; {november, <<"es"/utf8>>, true} -> <<"noviembre"/utf8>>; {november, <<"es"/utf8>>, false} -> <<"nov"/utf8>>; {december, <<"es"/utf8>>, true} -> <<"diciembre"/utf8>>; {december, <<"es"/utf8>>, false} -> <<"dic"/utf8>>; {january, <<"fr"/utf8>>, true} -> <<"janvier"/utf8>>; {january, <<"fr"/utf8>>, false} -> <<"janv"/utf8>>; {february, <<"fr"/utf8>>, true} -> <<"février"/utf8>>; {february, <<"fr"/utf8>>, false} -> <<"févr"/utf8>>; {march, <<"fr"/utf8>>, true} -> <<"mars"/utf8>>; {march, <<"fr"/utf8>>, false} -> <<"mars"/utf8>>; {april, <<"fr"/utf8>>, true} -> <<"avril"/utf8>>; {april, <<"fr"/utf8>>, false} -> <<"avr"/utf8>>; {may, <<"fr"/utf8>>, true} -> <<"mai"/utf8>>; {may, <<"fr"/utf8>>, false} -> <<"mai"/utf8>>; {june, <<"fr"/utf8>>, true} -> <<"juin"/utf8>>; {june, <<"fr"/utf8>>, false} -> <<"juin"/utf8>>; {july, <<"fr"/utf8>>, true} -> <<"juillet"/utf8>>; {july, <<"fr"/utf8>>, false} -> <<"juil"/utf8>>; {august, <<"fr"/utf8>>, true} -> <<"août"/utf8>>; {august, <<"fr"/utf8>>, false} -> <<"août"/utf8>>; {september, <<"fr"/utf8>>, true} -> <<"septembre"/utf8>>; {september, <<"fr"/utf8>>, false} -> <<"sept"/utf8>>; {october, <<"fr"/utf8>>, true} -> <<"octobre"/utf8>>; {october, <<"fr"/utf8>>, false} -> <<"oct"/utf8>>; {november, <<"fr"/utf8>>, true} -> <<"novembre"/utf8>>; {november, <<"fr"/utf8>>, false} -> <<"nov"/utf8>>; {december, <<"fr"/utf8>>, true} -> <<"décembre"/utf8>>; {december, <<"fr"/utf8>>, false} -> <<"déc"/utf8>>; {january, <<"de"/utf8>>, true} -> <<"Januar"/utf8>>; {january, <<"de"/utf8>>, false} -> <<"Jan"/utf8>>; {february, <<"de"/utf8>>, true} -> <<"Februar"/utf8>>; {february, <<"de"/utf8>>, false} -> <<"Feb"/utf8>>; {march, <<"de"/utf8>>, true} -> <<"März"/utf8>>; {march, <<"de"/utf8>>, false} -> <<"Mär"/utf8>>; {april, <<"de"/utf8>>, true} -> <<"April"/utf8>>; {april, <<"de"/utf8>>, false} -> <<"Apr"/utf8>>; {may, <<"de"/utf8>>, true} -> <<"Mai"/utf8>>; {may, <<"de"/utf8>>, false} -> <<"Mai"/utf8>>; {june, <<"de"/utf8>>, true} -> <<"Juni"/utf8>>; {june, <<"de"/utf8>>, false} -> <<"Jun"/utf8>>; {july, <<"de"/utf8>>, true} -> <<"Juli"/utf8>>; {july, <<"de"/utf8>>, false} -> <<"Jul"/utf8>>; {august, <<"de"/utf8>>, true} -> <<"August"/utf8>>; {august, <<"de"/utf8>>, false} -> <<"Aug"/utf8>>; {september, <<"de"/utf8>>, true} -> <<"September"/utf8>>; {september, <<"de"/utf8>>, false} -> <<"Sep"/utf8>>; {october, <<"de"/utf8>>, true} -> <<"Oktober"/utf8>>; {october, <<"de"/utf8>>, false} -> <<"Okt"/utf8>>; {november, <<"de"/utf8>>, true} -> <<"November"/utf8>>; {november, <<"de"/utf8>>, false} -> <<"Nov"/utf8>>; {december, <<"de"/utf8>>, true} -> <<"Dezember"/utf8>>; {december, <<"de"/utf8>>, false} -> <<"Dez"/utf8>>; {january, <<"it"/utf8>>, true} -> <<"gennaio"/utf8>>; {january, <<"it"/utf8>>, false} -> <<"gen"/utf8>>; {february, <<"it"/utf8>>, true} -> <<"febbraio"/utf8>>; {february, <<"it"/utf8>>, false} -> <<"feb"/utf8>>; {march, <<"it"/utf8>>, true} -> <<"marzo"/utf8>>; {march, <<"it"/utf8>>, false} -> <<"mar"/utf8>>; {april, <<"it"/utf8>>, true} -> <<"aprile"/utf8>>; {april, <<"it"/utf8>>, false} -> <<"apr"/utf8>>; {may, <<"it"/utf8>>, true} -> <<"maggio"/utf8>>; {may, <<"it"/utf8>>, false} -> <<"mag"/utf8>>; {june, <<"it"/utf8>>, true} -> <<"giugno"/utf8>>; {june, <<"it"/utf8>>, false} -> <<"giu"/utf8>>; {july, <<"it"/utf8>>, true} -> <<"luglio"/utf8>>; {july, <<"it"/utf8>>, false} -> <<"lug"/utf8>>; {august, <<"it"/utf8>>, true} -> <<"agosto"/utf8>>; {august, <<"it"/utf8>>, false} -> <<"ago"/utf8>>; {september, <<"it"/utf8>>, true} -> <<"settembre"/utf8>>; {september, <<"it"/utf8>>, false} -> <<"set"/utf8>>; {october, <<"it"/utf8>>, true} -> <<"ottobre"/utf8>>; {october, <<"it"/utf8>>, false} -> <<"ott"/utf8>>; {november, <<"it"/utf8>>, true} -> <<"novembre"/utf8>>; {november, <<"it"/utf8>>, false} -> <<"nov"/utf8>>; {december, <<"it"/utf8>>, true} -> <<"dicembre"/utf8>>; {december, <<"it"/utf8>>, false} -> <<"dic"/utf8>>; {january, <<"ru"/utf8>>, true} -> <<"январь"/utf8>>; {january, <<"ru"/utf8>>, false} -> <<"янв"/utf8>>; {february, <<"ru"/utf8>>, true} -> <<"февраль"/utf8>>; {february, <<"ru"/utf8>>, false} -> <<"фев"/utf8>>; {march, <<"ru"/utf8>>, true} -> <<"март"/utf8>>; {march, <<"ru"/utf8>>, false} -> <<"мар"/utf8>>; {april, <<"ru"/utf8>>, true} -> <<"апрель"/utf8>>; {april, <<"ru"/utf8>>, false} -> <<"апр"/utf8>>; {may, <<"ru"/utf8>>, true} -> <<"май"/utf8>>; {may, <<"ru"/utf8>>, false} -> <<"май"/utf8>>; {june, <<"ru"/utf8>>, true} -> <<"июнь"/utf8>>; {june, <<"ru"/utf8>>, false} -> <<"июн"/utf8>>; {july, <<"ru"/utf8>>, true} -> <<"июль"/utf8>>; {july, <<"ru"/utf8>>, false} -> <<"июл"/utf8>>; {august, <<"ru"/utf8>>, true} -> <<"август"/utf8>>; {august, <<"ru"/utf8>>, false} -> <<"авг"/utf8>>; {september, <<"ru"/utf8>>, true} -> <<"сентябрь"/utf8>>; {september, <<"ru"/utf8>>, false} -> <<"сен"/utf8>>; {october, <<"ru"/utf8>>, true} -> <<"октябрь"/utf8>>; {october, <<"ru"/utf8>>, false} -> <<"окт"/utf8>>; {november, <<"ru"/utf8>>, true} -> <<"ноябрь"/utf8>>; {november, <<"ru"/utf8>>, false} -> <<"ноя"/utf8>>; {december, <<"ru"/utf8>>, true} -> <<"декабрь"/utf8>>; {december, <<"ru"/utf8>>, false} -> <<"дек"/utf8>>; {january, <<"zh"/utf8>>, true} -> <<"一月"/utf8>>; {january, <<"zh"/utf8>>, false} -> <<"1月"/utf8>>; {february, <<"zh"/utf8>>, true} -> <<"二月"/utf8>>; {february, <<"zh"/utf8>>, false} -> <<"2月"/utf8>>; {march, <<"zh"/utf8>>, true} -> <<"三月"/utf8>>; {march, <<"zh"/utf8>>, false} -> <<"3月"/utf8>>; {april, <<"zh"/utf8>>, true} -> <<"四月"/utf8>>; {april, <<"zh"/utf8>>, false} -> <<"4月"/utf8>>; {may, <<"zh"/utf8>>, true} -> <<"五月"/utf8>>; {may, <<"zh"/utf8>>, false} -> <<"5月"/utf8>>; {june, <<"zh"/utf8>>, true} -> <<"六月"/utf8>>; {june, <<"zh"/utf8>>, false} -> <<"6月"/utf8>>; {july, <<"zh"/utf8>>, true} -> <<"七月"/utf8>>; {july, <<"zh"/utf8>>, false} -> <<"7月"/utf8>>; {august, <<"zh"/utf8>>, true} -> <<"八月"/utf8>>; {august, <<"zh"/utf8>>, false} -> <<"8月"/utf8>>; {september, <<"zh"/utf8>>, true} -> <<"九月"/utf8>>; {september, <<"zh"/utf8>>, false} -> <<"9月"/utf8>>; {october, <<"zh"/utf8>>, true} -> <<"十月"/utf8>>; {october, <<"zh"/utf8>>, false} -> <<"10月"/utf8>>; {november, <<"zh"/utf8>>, true} -> <<"十一月"/utf8>>; {november, <<"zh"/utf8>>, false} -> <<"11月"/utf8>>; {december, <<"zh"/utf8>>, true} -> <<"十二月"/utf8>>; {december, <<"zh"/utf8>>, false} -> <<"12月"/utf8>>; {january, <<"ja"/utf8>>, true} -> <<"一月"/utf8>>; {january, <<"ja"/utf8>>, false} -> <<"1月"/utf8>>; {february, <<"ja"/utf8>>, true} -> <<"二月"/utf8>>; {february, <<"ja"/utf8>>, false} -> <<"2月"/utf8>>; {march, <<"ja"/utf8>>, true} -> <<"三月"/utf8>>; {march, <<"ja"/utf8>>, false} -> <<"3月"/utf8>>; {april, <<"ja"/utf8>>, true} -> <<"四月"/utf8>>; {april, <<"ja"/utf8>>, false} -> <<"4月"/utf8>>; {may, <<"ja"/utf8>>, true} -> <<"五月"/utf8>>; {may, <<"ja"/utf8>>, false} -> <<"5月"/utf8>>; {june, <<"ja"/utf8>>, true} -> <<"六月"/utf8>>; {june, <<"ja"/utf8>>, false} -> <<"6月"/utf8>>; {july, <<"ja"/utf8>>, true} -> <<"七月"/utf8>>; {july, <<"ja"/utf8>>, false} -> <<"7月"/utf8>>; {august, <<"ja"/utf8>>, true} -> <<"八月"/utf8>>; {august, <<"ja"/utf8>>, false} -> <<"8月"/utf8>>; {september, <<"ja"/utf8>>, true} -> <<"九月"/utf8>>; {september, <<"ja"/utf8>>, false} -> <<"9月"/utf8>>; {october, <<"ja"/utf8>>, true} -> <<"十月"/utf8>>; {october, <<"ja"/utf8>>, false} -> <<"10月"/utf8>>; {november, <<"ja"/utf8>>, true} -> <<"十一月"/utf8>>; {november, <<"ja"/utf8>>, false} -> <<"11月"/utf8>>; {december, <<"ja"/utf8>>, true} -> <<"十二月"/utf8>>; {december, <<"ja"/utf8>>, false} -> <<"12月"/utf8>>; {january, <<"ko"/utf8>>, true} -> <<"일월"/utf8>>; {january, <<"ko"/utf8>>, false} -> <<"1월"/utf8>>; {february, <<"ko"/utf8>>, true} -> <<"이월"/utf8>>; {february, <<"ko"/utf8>>, false} -> <<"2월"/utf8>>; {march, <<"ko"/utf8>>, true} -> <<"삼월"/utf8>>; {march, <<"ko"/utf8>>, false} -> <<"3월"/utf8>>; {april, <<"ko"/utf8>>, true} -> <<"사월"/utf8>>; {april, <<"ko"/utf8>>, false} -> <<"4월"/utf8>>; {may, <<"ko"/utf8>>, true} -> <<"오월"/utf8>>; {may, <<"ko"/utf8>>, false} -> <<"5월"/utf8>>; {june, <<"ko"/utf8>>, true} -> <<"유월"/utf8>>; {june, <<"ko"/utf8>>, false} -> <<"6월"/utf8>>; {july, <<"ko"/utf8>>, true} -> <<"칠월"/utf8>>; {july, <<"ko"/utf8>>, false} -> <<"7월"/utf8>>; {august, <<"ko"/utf8>>, true} -> <<"팔월"/utf8>>; {august, <<"ko"/utf8>>, false} -> <<"8월"/utf8>>; {september, <<"ko"/utf8>>, true} -> <<"구월"/utf8>>; {september, <<"ko"/utf8>>, false} -> <<"9월"/utf8>>; {october, <<"ko"/utf8>>, true} -> <<"시월"/utf8>>; {october, <<"ko"/utf8>>, false} -> <<"10월"/utf8>>; {november, <<"ko"/utf8>>, true} -> <<"십일월"/utf8>>; {november, <<"ko"/utf8>>, false} -> <<"11월"/utf8>>; {december, <<"ko"/utf8>>, true} -> <<"십이월"/utf8>>; {december, <<"ko"/utf8>>, false} -> <<"12월"/utf8>>; {january, <<"ar"/utf8>>, true} -> <<"يناير"/utf8>>; {january, <<"ar"/utf8>>, false} -> <<"ينا"/utf8>>; {february, <<"ar"/utf8>>, true} -> <<"فبراير"/utf8>>; {february, <<"ar"/utf8>>, false} -> <<"فبر"/utf8>>; {march, <<"ar"/utf8>>, true} -> <<"مارس"/utf8>>; {march, <<"ar"/utf8>>, false} -> <<"مار"/utf8>>; {april, <<"ar"/utf8>>, true} -> <<"أبريل"/utf8>>; {april, <<"ar"/utf8>>, false} -> <<"أبر"/utf8>>; {may, <<"ar"/utf8>>, true} -> <<"مايو"/utf8>>; {may, <<"ar"/utf8>>, false} -> <<"ماي"/utf8>>; {june, <<"ar"/utf8>>, true} -> <<"يونيو"/utf8>>; {june, <<"ar"/utf8>>, false} -> <<"يون"/utf8>>; {july, <<"ar"/utf8>>, true} -> <<"يوليو"/utf8>>; {july, <<"ar"/utf8>>, false} -> <<"يول"/utf8>>; {august, <<"ar"/utf8>>, true} -> <<"أغسطس"/utf8>>; {august, <<"ar"/utf8>>, false} -> <<"أغس"/utf8>>; {september, <<"ar"/utf8>>, true} -> <<"سبتمبر"/utf8>>; {september, <<"ar"/utf8>>, false} -> <<"سبت"/utf8>>; {october, <<"ar"/utf8>>, true} -> <<"أكتوبر"/utf8>>; {october, <<"ar"/utf8>>, false} -> <<"أكت"/utf8>>; {november, <<"ar"/utf8>>, true} -> <<"نوفمبر"/utf8>>; {november, <<"ar"/utf8>>, false} -> <<"نوف"/utf8>>; {december, <<"ar"/utf8>>, true} -> <<"ديسمبر"/utf8>>; {december, <<"ar"/utf8>>, false} -> <<"ديس"/utf8>>; {january, <<"hi"/utf8>>, true} -> <<"जनवरी"/utf8>>; {january, <<"hi"/utf8>>, false} -> <<"जन"/utf8>>; {february, <<"hi"/utf8>>, true} -> <<"फरवरी"/utf8>>; {february, <<"hi"/utf8>>, false} -> <<"फर"/utf8>>; {march, <<"hi"/utf8>>, true} -> <<"मार्च"/utf8>>; {march, <<"hi"/utf8>>, false} -> <<"मार"/utf8>>; {april, <<"hi"/utf8>>, true} -> <<"अप्रैल"/utf8>>; {april, <<"hi"/utf8>>, false} -> <<"अप्र"/utf8>>; {may, <<"hi"/utf8>>, true} -> <<"मई"/utf8>>; {may, <<"hi"/utf8>>, false} -> <<"मई"/utf8>>; {june, <<"hi"/utf8>>, true} -> <<"जून"/utf8>>; {june, <<"hi"/utf8>>, false} -> <<"जून"/utf8>>; {july, <<"hi"/utf8>>, true} -> <<"जुलाई"/utf8>>; {july, <<"hi"/utf8>>, false} -> <<"जुल"/utf8>>; {august, <<"hi"/utf8>>, true} -> <<"अगस्त"/utf8>>; {august, <<"hi"/utf8>>, false} -> <<"अग"/utf8>>; {september, <<"hi"/utf8>>, true} -> <<"सितम्बर"/utf8>>; {september, <<"hi"/utf8>>, false} -> <<"सित"/utf8>>; {october, <<"hi"/utf8>>, true} -> <<"अक्टूबर"/utf8>>; {october, <<"hi"/utf8>>, false} -> <<"अक्ट"/utf8>>; {november, <<"hi"/utf8>>, true} -> <<"नवम्बर"/utf8>>; {november, <<"hi"/utf8>>, false} -> <<"नव"/utf8>>; {december, <<"hi"/utf8>>, true} -> <<"दिसम्बर"/utf8>>; {december, <<"hi"/utf8>>, false} -> <<"दिस"/utf8>>; {_, _, _} -> case Month of january -> <<"01"/utf8>>; february -> <<"02"/utf8>>; march -> <<"03"/utf8>>; april -> <<"04"/utf8>>; may -> <<"05"/utf8>>; june -> <<"06"/utf8>>; july -> <<"07"/utf8>>; august -> <<"08"/utf8>>; september -> <<"09"/utf8>>; october -> <<"10"/utf8>>; november -> <<"11"/utf8>>; december -> <<"12"/utf8>> end end. -file("src/g18n.gleam", 2497). -spec date_medium(gleam@time@calendar:date(), binary()) -> binary(). date_medium(Date, Language) -> Month_name = get_month_name(erlang:element(3, Date), Language, false), case Language of <<"en"/utf8>> -> <<<<<<<>/binary, (erlang:integer_to_binary(erlang:element(4, Date)))/binary>>/binary, ", "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>; <<"pt"/utf8>> -> <<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, " de "/utf8>>/binary, Month_name/binary>>/binary, " de "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>; <<"es"/utf8>> -> <<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, " de "/utf8>>/binary, Month_name/binary>>/binary, " de "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>; <<"fr"/utf8>> -> <<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>; <<"de"/utf8>> -> <<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, ". "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>; <<"it"/utf8>> -> <<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>; <<"ru"/utf8>> -> <<<<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " г."/utf8>>; <<"zh"/utf8>> -> <<<<<<<<(erlang:integer_to_binary(erlang:element(2, Date)))/binary, "年"/utf8>>/binary, Month_name/binary>>/binary, (erlang:integer_to_binary(erlang:element(4, Date)))/binary>>/binary, "日"/utf8>>; <<"ja"/utf8>> -> <<<<<<<<(erlang:integer_to_binary(erlang:element(2, Date)))/binary, "年"/utf8>>/binary, Month_name/binary>>/binary, (erlang:integer_to_binary(erlang:element(4, Date)))/binary>>/binary, "日"/utf8>>; <<"ko"/utf8>> -> <<<<<<<<<<(erlang:integer_to_binary(erlang:element(2, Date)))/binary, "년 "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(4, Date)))/binary>>/binary, "일"/utf8>>; <<"ar"/utf8>> -> <<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>; <<"hi"/utf8>> -> <<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>; _ -> <<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>> end. -file("src/g18n.gleam", 2571). -spec date_long(gleam@time@calendar:date(), binary()) -> binary(). date_long(Date, Language) -> Month_name = get_month_name(erlang:element(3, Date), Language, true), case Language of <<"en"/utf8>> -> <<<<<<<<<>/binary, (erlang:integer_to_binary(erlang:element(4, Date)))/binary>>/binary, ", "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>>; <<"pt"/utf8>> -> <<<<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, " de "/utf8>>/binary, Month_name/binary>>/binary, " de "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>>; <<"es"/utf8>> -> <<<<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, " de "/utf8>>/binary, Month_name/binary>>/binary, " de "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>>; <<"fr"/utf8>> -> <<<<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>>; <<"de"/utf8>> -> <<<<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, ". "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>>; <<"it"/utf8>> -> <<<<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>>; <<"ru"/utf8>> -> <<<<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " г. GMT"/utf8>>; <<"zh"/utf8>> -> <<<<<<<<(erlang:integer_to_binary(erlang:element(2, Date)))/binary, "年"/utf8>>/binary, Month_name/binary>>/binary, (erlang:integer_to_binary(erlang:element(4, Date)))/binary>>/binary, "日 GMT"/utf8>>; <<"ja"/utf8>> -> <<<<<<<<(erlang:integer_to_binary(erlang:element(2, Date)))/binary, "年"/utf8>>/binary, Month_name/binary>>/binary, (erlang:integer_to_binary(erlang:element(4, Date)))/binary>>/binary, "日 GMT"/utf8>>; <<"ko"/utf8>> -> <<<<<<<<<<(erlang:integer_to_binary(erlang:element(2, Date)))/binary, "년 "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(4, Date)))/binary>>/binary, "일 GMT"/utf8>>; <<"ar"/utf8>> -> <<<<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>>; <<"hi"/utf8>> -> <<<<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>>; _ -> <<<<<<<<<<(erlang:integer_to_binary(erlang:element(4, Date)))/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>> end. -file("src/g18n.gleam", 3216). -spec format_time_unit(binary(), binary(), integer()) -> binary(). format_time_unit(Language, Unit, Count) -> Count_str = erlang:integer_to_binary(Count), case Language of <<"en"/utf8>> -> case {Unit, Count} of {<<"second"/utf8>>, 1} -> <<"1 second"/utf8>>; {<<"second"/utf8>>, _} -> <>; {<<"minute"/utf8>>, 1} -> <<"1 minute"/utf8>>; {<<"minute"/utf8>>, _} -> <>; {<<"hour"/utf8>>, 1} -> <<"1 hour"/utf8>>; {<<"hour"/utf8>>, _} -> <>; {<<"day"/utf8>>, 1} -> <<"1 day"/utf8>>; {<<"day"/utf8>>, _} -> <>; {<<"week"/utf8>>, 1} -> <<"1 week"/utf8>>; {<<"week"/utf8>>, _} -> <>; {<<"month"/utf8>>, 1} -> <<"1 month"/utf8>>; {<<"month"/utf8>>, _} -> <>; {<<"year"/utf8>>, 1} -> <<"1 year"/utf8>>; {<<"year"/utf8>>, _} -> <>; {_, _} -> <<<>/binary, Unit/binary>> end; <<"pt"/utf8>> -> case {Unit, Count} of {<<"second"/utf8>>, 1} -> <<"1 segundo"/utf8>>; {<<"second"/utf8>>, _} -> <>; {<<"minute"/utf8>>, 1} -> <<"1 minuto"/utf8>>; {<<"minute"/utf8>>, _} -> <>; {<<"hour"/utf8>>, 1} -> <<"1 hora"/utf8>>; {<<"hour"/utf8>>, _} -> <>; {<<"day"/utf8>>, 1} -> <<"1 dia"/utf8>>; {<<"day"/utf8>>, _} -> <>; {<<"week"/utf8>>, 1} -> <<"1 semana"/utf8>>; {<<"week"/utf8>>, _} -> <>; {<<"month"/utf8>>, 1} -> <<"1 mês"/utf8>>; {<<"month"/utf8>>, _} -> <>; {<<"year"/utf8>>, 1} -> <<"1 ano"/utf8>>; {<<"year"/utf8>>, _} -> <>; {_, _} -> <<<>/binary, Unit/binary>> end; <<"es"/utf8>> -> case {Unit, Count} of {<<"second"/utf8>>, 1} -> <<"1 segundo"/utf8>>; {<<"second"/utf8>>, _} -> <>; {<<"minute"/utf8>>, 1} -> <<"1 minuto"/utf8>>; {<<"minute"/utf8>>, _} -> <>; {<<"hour"/utf8>>, 1} -> <<"1 hora"/utf8>>; {<<"hour"/utf8>>, _} -> <>; {<<"day"/utf8>>, 1} -> <<"1 día"/utf8>>; {<<"day"/utf8>>, _} -> <>; {<<"week"/utf8>>, 1} -> <<"1 semana"/utf8>>; {<<"week"/utf8>>, _} -> <>; {<<"month"/utf8>>, 1} -> <<"1 mes"/utf8>>; {<<"month"/utf8>>, _} -> <>; {<<"year"/utf8>>, 1} -> <<"1 año"/utf8>>; {<<"year"/utf8>>, _} -> <>; {_, _} -> <<<>/binary, Unit/binary>> end; <<"fr"/utf8>> -> case {Unit, Count} of {<<"second"/utf8>>, 1} -> <<"1 seconde"/utf8>>; {<<"second"/utf8>>, _} -> <>; {<<"minute"/utf8>>, 1} -> <<"1 minute"/utf8>>; {<<"minute"/utf8>>, _} -> <>; {<<"hour"/utf8>>, 1} -> <<"1 heure"/utf8>>; {<<"hour"/utf8>>, _} -> <>; {<<"day"/utf8>>, 1} -> <<"1 jour"/utf8>>; {<<"day"/utf8>>, _} -> <>; {<<"week"/utf8>>, 1} -> <<"1 semaine"/utf8>>; {<<"week"/utf8>>, _} -> <>; {<<"month"/utf8>>, 1} -> <<"1 mois"/utf8>>; {<<"month"/utf8>>, _} -> <>; {<<"year"/utf8>>, 1} -> <<"1 an"/utf8>>; {<<"year"/utf8>>, _} -> <>; {_, _} -> <<<>/binary, Unit/binary>> end; <<"de"/utf8>> -> case {Unit, Count} of {<<"second"/utf8>>, 1} -> <<"1 Sekunde"/utf8>>; {<<"second"/utf8>>, _} -> <>; {<<"minute"/utf8>>, 1} -> <<"1 Minute"/utf8>>; {<<"minute"/utf8>>, _} -> <>; {<<"hour"/utf8>>, 1} -> <<"1 Stunde"/utf8>>; {<<"hour"/utf8>>, _} -> <>; {<<"day"/utf8>>, 1} -> <<"1 Tag"/utf8>>; {<<"day"/utf8>>, _} -> <>; {<<"week"/utf8>>, 1} -> <<"1 Woche"/utf8>>; {<<"week"/utf8>>, _} -> <>; {<<"month"/utf8>>, 1} -> <<"1 Monat"/utf8>>; {<<"month"/utf8>>, _} -> <>; {<<"year"/utf8>>, 1} -> <<"1 Jahr"/utf8>>; {<<"year"/utf8>>, _} -> <>; {_, _} -> <<<>/binary, Unit/binary>> end; <<"it"/utf8>> -> case {Unit, Count} of {<<"second"/utf8>>, 1} -> <<"1 secondo"/utf8>>; {<<"second"/utf8>>, _} -> <>; {<<"minute"/utf8>>, 1} -> <<"1 minuto"/utf8>>; {<<"minute"/utf8>>, _} -> <>; {<<"hour"/utf8>>, 1} -> <<"1 ora"/utf8>>; {<<"hour"/utf8>>, _} -> <>; {<<"day"/utf8>>, 1} -> <<"1 giorno"/utf8>>; {<<"day"/utf8>>, _} -> <>; {<<"week"/utf8>>, 1} -> <<"1 settimana"/utf8>>; {<<"week"/utf8>>, _} -> <>; {<<"month"/utf8>>, 1} -> <<"1 mese"/utf8>>; {<<"month"/utf8>>, _} -> <>; {<<"year"/utf8>>, 1} -> <<"1 anno"/utf8>>; {<<"year"/utf8>>, _} -> <>; {_, _} -> <<<>/binary, Unit/binary>> end; <<"ru"/utf8>> -> case {Unit, Count} of {<<"second"/utf8>>, 1} -> <<"1 секунда"/utf8>>; {<<"second"/utf8>>, N} when (N >= 2) andalso (N =< 4) -> <>; {<<"second"/utf8>>, _} -> <>; {<<"minute"/utf8>>, 1} -> <<"1 минута"/utf8>>; {<<"minute"/utf8>>, N@1} when (N@1 >= 2) andalso (N@1 =< 4) -> <>; {<<"minute"/utf8>>, _} -> <>; {<<"hour"/utf8>>, 1} -> <<"1 час"/utf8>>; {<<"hour"/utf8>>, N@2} when (N@2 >= 2) andalso (N@2 =< 4) -> <>; {<<"hour"/utf8>>, _} -> <>; {<<"day"/utf8>>, 1} -> <<"1 день"/utf8>>; {<<"day"/utf8>>, N@3} when (N@3 >= 2) andalso (N@3 =< 4) -> <>; {<<"day"/utf8>>, _} -> <>; {<<"week"/utf8>>, 1} -> <<"1 неделя"/utf8>>; {<<"week"/utf8>>, N@4} when (N@4 >= 2) andalso (N@4 =< 4) -> <>; {<<"week"/utf8>>, _} -> <>; {<<"month"/utf8>>, 1} -> <<"1 месяц"/utf8>>; {<<"month"/utf8>>, N@5} when (N@5 >= 2) andalso (N@5 =< 4) -> <>; {<<"month"/utf8>>, _} -> <>; {<<"year"/utf8>>, 1} -> <<"1 год"/utf8>>; {<<"year"/utf8>>, N@6} when (N@6 >= 2) andalso (N@6 =< 4) -> <>; {<<"year"/utf8>>, _} -> <>; {_, _} -> <<<>/binary, Unit/binary>> end; <<"zh"/utf8>> -> case {Unit, Count} of {<<"second"/utf8>>, _} -> <>; {<<"minute"/utf8>>, _} -> <>; {<<"hour"/utf8>>, _} -> <>; {<<"day"/utf8>>, _} -> <>; {<<"week"/utf8>>, _} -> <>; {<<"month"/utf8>>, _} -> <>; {<<"year"/utf8>>, _} -> <>; {_, _} -> <> end; <<"ja"/utf8>> -> case {Unit, Count} of {<<"second"/utf8>>, _} -> <>; {<<"minute"/utf8>>, _} -> <>; {<<"hour"/utf8>>, _} -> <>; {<<"day"/utf8>>, _} -> <>; {<<"week"/utf8>>, _} -> <>; {<<"month"/utf8>>, _} -> <>; {<<"year"/utf8>>, _} -> <>; {_, _} -> <> end; <<"ko"/utf8>> -> case {Unit, Count} of {<<"second"/utf8>>, _} -> <>; {<<"minute"/utf8>>, _} -> <>; {<<"hour"/utf8>>, _} -> <>; {<<"day"/utf8>>, _} -> <>; {<<"week"/utf8>>, _} -> <>; {<<"month"/utf8>>, _} -> <>; {<<"year"/utf8>>, _} -> <>; {_, _} -> <> end; <<"ar"/utf8>> -> case {Unit, Count} of {<<"second"/utf8>>, 1} -> <<"ثانية واحدة"/utf8>>; {<<"second"/utf8>>, 2} -> <<"ثانيتان"/utf8>>; {<<"second"/utf8>>, N@7} when (N@7 >= 3) andalso (N@7 =< 10) -> <>; {<<"second"/utf8>>, _} -> <>; {<<"minute"/utf8>>, 1} -> <<"دقيقة واحدة"/utf8>>; {<<"minute"/utf8>>, 2} -> <<"دقيقتان"/utf8>>; {<<"minute"/utf8>>, N@8} when (N@8 >= 3) andalso (N@8 =< 10) -> <>; {<<"minute"/utf8>>, _} -> <>; {<<"hour"/utf8>>, 1} -> <<"ساعة واحدة"/utf8>>; {<<"hour"/utf8>>, 2} -> <<"ساعتان"/utf8>>; {<<"hour"/utf8>>, N@9} when (N@9 >= 3) andalso (N@9 =< 10) -> <>; {<<"hour"/utf8>>, _} -> <>; {<<"day"/utf8>>, 1} -> <<"يوم واحد"/utf8>>; {<<"day"/utf8>>, 2} -> <<"يومان"/utf8>>; {<<"day"/utf8>>, N@10} when (N@10 >= 3) andalso (N@10 =< 10) -> <>; {<<"day"/utf8>>, _} -> <>; {<<"week"/utf8>>, 1} -> <<"أسبوع واحد"/utf8>>; {<<"week"/utf8>>, 2} -> <<"أسبوعان"/utf8>>; {<<"week"/utf8>>, N@11} when (N@11 >= 3) andalso (N@11 =< 10) -> <>; {<<"week"/utf8>>, _} -> <>; {<<"month"/utf8>>, 1} -> <<"شهر واحد"/utf8>>; {<<"month"/utf8>>, 2} -> <<"شهران"/utf8>>; {<<"month"/utf8>>, N@12} when (N@12 >= 3) andalso (N@12 =< 10) -> <>; {<<"month"/utf8>>, _} -> <>; {<<"year"/utf8>>, 1} -> <<"سنة واحدة"/utf8>>; {<<"year"/utf8>>, 2} -> <<"سنتان"/utf8>>; {<<"year"/utf8>>, N@13} when (N@13 >= 3) andalso (N@13 =< 10) -> <>; {<<"year"/utf8>>, _} -> <>; {_, _} -> <<<>/binary, Unit/binary>> end; <<"hi"/utf8>> -> case {Unit, Count} of {<<"second"/utf8>>, 1} -> <<"1 सेकंड"/utf8>>; {<<"second"/utf8>>, _} -> <>; {<<"minute"/utf8>>, 1} -> <<"1 मिनट"/utf8>>; {<<"minute"/utf8>>, _} -> <>; {<<"hour"/utf8>>, 1} -> <<"1 घंटा"/utf8>>; {<<"hour"/utf8>>, _} -> <>; {<<"day"/utf8>>, 1} -> <<"1 दिन"/utf8>>; {<<"day"/utf8>>, _} -> <>; {<<"week"/utf8>>, 1} -> <<"1 सप्ताह"/utf8>>; {<<"week"/utf8>>, _} -> <>; {<<"month"/utf8>>, 1} -> <<"1 महीना"/utf8>>; {<<"month"/utf8>>, _} -> <>; {<<"year"/utf8>>, 1} -> <<"1 साल"/utf8>>; {<<"year"/utf8>>, _} -> <>; {_, _} -> <<<>/binary, Unit/binary>> end; _ -> <<<>/binary, Unit/binary>> end. -file("src/g18n.gleam", 2240). ?DOC( " Format relative time expressions like \"2 hours ago\" or \"in 5 minutes\".\n" "\n" " Generates culturally appropriate relative time expressions using proper\n" " pluralization rules and language-specific constructions. Supports past\n" " and future expressions in 12 languages.\n" "\n" " ## Supported Languages & Expressions\n" " - **English**: \"2 hours ago\", \"in 5 minutes\"\n" " - **Spanish**: \"hace 2 horas\", \"en 5 minutos\" \n" " - **Portuguese**: \"há 2 horas\", \"em 5 minutos\"\n" " - **French**: \"il y a 2 heures\", \"dans 5 minutes\"\n" " - **German**: \"vor 2 Stunden\", \"in 5 Minuten\"\n" " - **Russian**: \"2 часа назад\", \"через 5 минут\" (with complex pluralization)\n" " - **Chinese**: \"2小时前\", \"5分钟后\" (no pluralization)\n" " - **Japanese**: \"2時間前\", \"5分後\"\n" " - **Korean**: \"2시간 전\", \"5분 후\"\n" " - **Arabic**: \"منذ ساعتان\", \"خلال 5 دقائق\" (dual/plural forms)\n" " - **Hindi**: \"2 घंटे पहले\", \"5 मिनट में\"\n" " - **Italian**: \"2 ore fa\", \"tra 5 minuti\"\n" "\n" " ## Examples\n" " ```gleam\n" " let assert Ok(en_locale) = g18n.locale(\"en\")\n" " let assert Ok(es_locale) = g18n.locale(\"es\")\n" " let assert Ok(ru_locale) = g18n.locale(\"ru\")\n" " let translations = g18n.translations()\n" " let en_translator = g18n.translator(en_locale, translations)\n" " let es_translator = g18n.translator(es_locale, translations) \n" " let ru_translator = g18n.translator(ru_locale, translations)\n" " \n" " // English\n" " g18n.format_relative_time(en_translator, g18n.Hours(2), g18n.Past)\n" " // \"2 hours ago\"\n" " g18n.format_relative_time(en_translator, g18n.Minutes(30), g18n.Future)\n" " // \"in 30 minutes\"\n" " \n" " // Spanish \n" " g18n.format_relative_time(es_translator, g18n.Days(3), g18n.Past)\n" " // \"hace 3 días\"\n" " \n" " // Russian (complex pluralization)\n" " g18n.format_relative_time(ru_translator, g18n.Hours(1), g18n.Past) \n" " // \"1 час назад\"\n" " g18n.format_relative_time(ru_translator, g18n.Hours(2), g18n.Past)\n" " // \"2 часа назад\" \n" " g18n.format_relative_time(ru_translator, g18n.Hours(5), g18n.Past)\n" " // \"5 часов назад\"\n" " ```\n" ). -spec format_relative_time(translator(), relative_duration(), time_relative()) -> binary(). format_relative_time(Translator, Duration, Relative) -> Language = begin _pipe = Translator, _pipe@1 = locale(_pipe), g18n@locale:language(_pipe@1) end, _ = case Relative of past -> <<"ago"/utf8>>; future -> <<"in"/utf8>> end, Unit_text = case Duration of {seconds, N} -> format_time_unit(Language, <<"second"/utf8>>, N); {minutes, N@1} -> format_time_unit(Language, <<"minute"/utf8>>, N@1); {hours, N@2} -> format_time_unit(Language, <<"hour"/utf8>>, N@2); {days, N@3} -> format_time_unit(Language, <<"day"/utf8>>, N@3); {weeks, N@4} -> format_time_unit(Language, <<"week"/utf8>>, N@4); {months, N@5} -> format_time_unit(Language, <<"month"/utf8>>, N@5); {years, N@6} -> format_time_unit(Language, <<"year"/utf8>>, N@6) end, case {Relative, Language} of {past, <<"en"/utf8>>} -> <>; {future, <<"en"/utf8>>} -> <<"in "/utf8, Unit_text/binary>>; {past, <<"pt"/utf8>>} -> <<"há "/utf8, Unit_text/binary>>; {future, <<"pt"/utf8>>} -> <<"em "/utf8, Unit_text/binary>>; {past, <<"es"/utf8>>} -> <<"hace "/utf8, Unit_text/binary>>; {future, <<"es"/utf8>>} -> <<"en "/utf8, Unit_text/binary>>; {past, <<"fr"/utf8>>} -> <<"il y a "/utf8, Unit_text/binary>>; {future, <<"fr"/utf8>>} -> <<"dans "/utf8, Unit_text/binary>>; {past, <<"de"/utf8>>} -> <<"vor "/utf8, Unit_text/binary>>; {future, <<"de"/utf8>>} -> <<"in "/utf8, Unit_text/binary>>; {past, <<"it"/utf8>>} -> <>; {future, <<"it"/utf8>>} -> <<"tra "/utf8, Unit_text/binary>>; {past, <<"ru"/utf8>>} -> <>; {future, <<"ru"/utf8>>} -> <<"через "/utf8, Unit_text/binary>>; {past, <<"zh"/utf8>>} -> <>; {future, <<"zh"/utf8>>} -> <>; {past, <<"ja"/utf8>>} -> <>; {future, <<"ja"/utf8>>} -> <>; {past, <<"ko"/utf8>>} -> <>; {future, <<"ko"/utf8>>} -> <>; {past, <<"ar"/utf8>>} -> <<"منذ "/utf8, Unit_text/binary>>; {future, <<"ar"/utf8>>} -> <<"خلال "/utf8, Unit_text/binary>>; {past, <<"hi"/utf8>>} -> <>; {future, <<"hi"/utf8>>} -> <>; {_, _} -> <> end. -file("src/g18n.gleam", 3439). -spec pad_zero(integer()) -> binary(). pad_zero(Number) -> case Number < 10 of true -> <<"0"/utf8, (erlang:integer_to_binary(Number))/binary>>; false -> erlang:integer_to_binary(Number) end. -file("src/g18n.gleam", 2483). -spec date_short(gleam@time@calendar:date(), binary()) -> binary(). date_short(Date, Language) -> Year = begin _pipe = erlang:element(2, Date) rem 100, pad_zero(_pipe) end, Month = begin _pipe@1 = erlang:element(3, Date), _pipe@2 = month_to_int(_pipe@1), pad_zero(_pipe@2) end, Day = begin _pipe@3 = erlang:element(4, Date), pad_zero(_pipe@3) end, case Language of <<"en"/utf8>> -> <<<<<<<>/binary, Day/binary>>/binary, "/"/utf8>>/binary, Year/binary>>; <<"pt"/utf8>> -> <<<<<<<>/binary, Month/binary>>/binary, "/"/utf8>>/binary, Year/binary>>; <<"es"/utf8>> -> <<<<<<<>/binary, Month/binary>>/binary, "/"/utf8>>/binary, Year/binary>>; <<"it"/utf8>> -> <<<<<<<>/binary, Month/binary>>/binary, "/"/utf8>>/binary, Year/binary>>; <<"fr"/utf8>> -> <<<<<<<>/binary, Month/binary>>/binary, "/"/utf8>>/binary, Year/binary>>; <<"de"/utf8>> -> <<<<<<<>/binary, Month/binary>>/binary, "."/utf8>>/binary, Year/binary>>; <<"ru"/utf8>> -> <<<<<<<>/binary, Month/binary>>/binary, "."/utf8>>/binary, Year/binary>>; <<"zh"/utf8>> -> <<<<<<<>/binary, Month/binary>>/binary, "/"/utf8>>/binary, Day/binary>>; <<"ja"/utf8>> -> <<<<<<<>/binary, Month/binary>>/binary, "/"/utf8>>/binary, Day/binary>>; <<"ko"/utf8>> -> <<<<<<<>/binary, Month/binary>>/binary, "/"/utf8>>/binary, Day/binary>>; <<"ar"/utf8>> -> <<<<<<<>/binary, Month/binary>>/binary, "-"/utf8>>/binary, Year/binary>>; <<"hi"/utf8>> -> <<<<<<<>/binary, Month/binary>>/binary, "-"/utf8>>/binary, Year/binary>>; _ -> <<<<<<<>/binary, Month/binary>>/binary, "-"/utf8>>/binary, Year/binary>> end. -file("src/g18n.gleam", 2758). -spec date_custom(gleam@time@calendar:date(), binary()) -> binary(). date_custom(Date, Pattern) -> _pipe = Pattern, _pipe@2 = gleam@string:replace( _pipe, <<"YYYY"/utf8>>, begin _pipe@1 = erlang:element(2, Date), erlang:integer_to_binary(_pipe@1) end ), _pipe@4 = gleam@string:replace( _pipe@2, <<"MM"/utf8>>, pad_zero( begin _pipe@3 = erlang:element(3, Date), month_to_int(_pipe@3) end ) ), gleam@string:replace( _pipe@4, <<"DD"/utf8>>, pad_zero(erlang:element(4, Date)) ). -file("src/g18n.gleam", 2804). -spec time_custom(gleam@time@calendar:time_of_day(), binary()) -> binary(). time_custom(Time, Pattern) -> _pipe = Pattern, _pipe@1 = gleam@string:replace( _pipe, <<"HH"/utf8>>, pad_zero(erlang:element(2, Time)) ), _pipe@2 = gleam@string:replace( _pipe@1, <<"mm"/utf8>>, pad_zero(erlang:element(3, Time)) ), gleam@string:replace( _pipe@2, <<"ss"/utf8>>, pad_zero(erlang:element(4, Time)) ). -file("src/g18n.gleam", 2849). -spec datetime_custom( gleam@time@calendar:date(), gleam@time@calendar:time_of_day(), binary() ) -> binary(). datetime_custom(Date, Time, Pattern) -> _pipe = Pattern, _pipe@1 = gleam@string:replace( _pipe, <<"YYYY"/utf8>>, erlang:integer_to_binary(erlang:element(2, Date)) ), _pipe@3 = gleam@string:replace( _pipe@1, <<"MM"/utf8>>, pad_zero( begin _pipe@2 = erlang:element(3, Date), month_to_int(_pipe@2) end ) ), _pipe@4 = gleam@string:replace( _pipe@3, <<"DD"/utf8>>, pad_zero(erlang:element(4, Date)) ), _pipe@5 = gleam@string:replace( _pipe@4, <<"HH"/utf8>>, pad_zero(erlang:element(2, Time)) ), _pipe@6 = gleam@string:replace( _pipe@5, <<"mm"/utf8>>, pad_zero(erlang:element(3, Time)) ), gleam@string:replace( _pipe@6, <<"ss"/utf8>>, pad_zero(erlang:element(4, Time)) ). -file("src/g18n.gleam", 3172). -spec format_12_hour(gleam@time@calendar:time_of_day()) -> binary(). format_12_hour(Time) -> Hour_12 = case erlang:element(2, Time) of 0 -> 12; H when H > 12 -> H - 12; H@1 -> H@1 end, Ampm = case erlang:element(2, Time) of H@2 when H@2 >= 12 -> <<"PM"/utf8>>; _ -> <<"AM"/utf8>> end, <<<<<<<<(erlang:integer_to_binary(Hour_12))/binary, ":"/utf8>>/binary, (pad_zero(erlang:element(3, Time)))/binary>>/binary, " "/utf8>>/binary, Ampm/binary>>. -file("src/g18n.gleam", 3185). -spec format_24_hour(gleam@time@calendar:time_of_day()) -> binary(). format_24_hour(Time) -> <<<<(pad_zero(erlang:element(2, Time)))/binary, ":"/utf8>>/binary, (pad_zero(erlang:element(3, Time)))/binary>>. -file("src/g18n.gleam", 2782). -spec time_short(gleam@time@calendar:time_of_day(), binary()) -> binary(). time_short(Time, Language) -> case Language of <<"en"/utf8>> -> format_12_hour(Time); _ -> format_24_hour(Time) end. -file("src/g18n.gleam", 2811). -spec datetime_short( gleam@time@calendar:date(), gleam@time@calendar:time_of_day(), binary() ) -> binary(). datetime_short(Date, Time, Language) -> Date_part = date_short(Date, Language), Time_part = time_short(Time, Language), <<<>/binary, Time_part/binary>>. -file("src/g18n.gleam", 3189). -spec format_12_hour_with_seconds(gleam@time@calendar:time_of_day()) -> binary(). format_12_hour_with_seconds(Time) -> Hour_12 = case erlang:element(2, Time) of 0 -> 12; H when H > 12 -> H - 12; H@1 -> H@1 end, Ampm = case erlang:element(2, Time) of H@2 when H@2 >= 12 -> <<"PM"/utf8>>; _ -> <<"AM"/utf8>> end, <<<<<<<<<<<<(erlang:integer_to_binary(Hour_12))/binary, ":"/utf8>>/binary, (pad_zero(erlang:element(3, Time)))/binary>>/binary, ":"/utf8>>/binary, (pad_zero(erlang:element(4, Time)))/binary>>/binary, " "/utf8>>/binary, Ampm/binary>>. -file("src/g18n.gleam", 3208). -spec format_24_hour_with_seconds(gleam@time@calendar:time_of_day()) -> binary(). format_24_hour_with_seconds(Time) -> <<<<<<<<(pad_zero(erlang:element(2, Time)))/binary, ":"/utf8>>/binary, (pad_zero(erlang:element(3, Time)))/binary>>/binary, ":"/utf8>>/binary, (pad_zero(erlang:element(4, Time)))/binary>>. -file("src/g18n.gleam", 2789). -spec time_medium(gleam@time@calendar:time_of_day(), binary()) -> binary(). time_medium(Time, Language) -> case Language of <<"en"/utf8>> -> format_12_hour_with_seconds(Time); _ -> format_24_hour_with_seconds(Time) end. -file("src/g18n.gleam", 2796). -spec time_long(gleam@time@calendar:time_of_day(), binary()) -> binary(). time_long(Time, Language) -> <<(time_medium(Time, Language))/binary, " GMT"/utf8>>. -file("src/g18n.gleam", 2800). -spec time_full(gleam@time@calendar:time_of_day(), binary()) -> binary(). time_full(Time, Language) -> time_long(Time, Language). -file("src/g18n.gleam", 2118). ?DOC( " Format a time according to the translator's locale and specified format.\n" "\n" " Supports multiple format levels from compact numeric formats to full text\n" " with timezone information. Automatically uses locale-appropriate time formatting\n" " including 12-hour vs 24-hour notation based on cultural conventions.\n" "\n" " ## Format Types\n" " - `Short`: Compact time format (e.g., \"3:45 PM\", \"15:45\")\n" " - `Medium`: Time with seconds (e.g., \"3:45:30 PM\", \"15:45:30\")\n" " - `Long`: Time with timezone (e.g., \"3:45:30 PM GMT\", \"15:45:30 GMT\")\n" " - `Full`: Complete time format (e.g., \"3:45:30 PM GMT\", \"15:45:30 GMT\")\n" " - `Custom(pattern)`: Custom pattern with HH, mm, ss placeholders\n" "\n" " ## Examples\n" " ```gleam\n" " let assert Ok(en_locale) = g18n.locale(\"en\")\n" " let assert Ok(pt_locale) = g18n.locale(\"pt\")\n" " let en_translator = g18n.translator(en_locale, g18n.translations())\n" " let pt_translator = g18n.translator(pt_locale, g18n.translations())\n" " let time = calendar.TimeOfDay(hours: 15, minutes: 30, seconds: 45)\n" "\n" " // English uses 12-hour format\n" " g18n.format_time(en_translator, time, g18n.Short) // \"3:30 PM\"\n" " g18n.format_time(en_translator, time, g18n.Medium) // \"3:30:45 PM\"\n" " g18n.format_time(en_translator, time, g18n.Long) // \"3:30:45 PM GMT\"\n" "\n" " // Portuguese uses 24-hour format \n" " g18n.format_time(pt_translator, time, g18n.Short) // \"15:30\"\n" " g18n.format_time(pt_translator, time, g18n.Medium) // \"15:30:45\"\n" "\n" " // Custom formatting\n" " g18n.format_time(en_translator, time, g18n.Custom(\"HH:mm\")) // \"15:30\"\n" " ```\n" ). -spec format_time( translator(), gleam@time@calendar:time_of_day(), date_time_format() ) -> binary(). format_time(Translator, Time, Format) -> Language = begin _pipe = Translator, _pipe@1 = locale(_pipe), g18n@locale:language(_pipe@1) end, case Format of short -> time_short(Time, Language); medium -> time_medium(Time, Language); long -> time_long(Time, Language); full -> time_full(Time, Language); {custom, Pattern} -> time_custom(Time, Pattern) end. -file("src/g18n.gleam", 2821). -spec datetime_medium( gleam@time@calendar:date(), gleam@time@calendar:time_of_day(), binary() ) -> binary(). datetime_medium(Date, Time, Language) -> Date_part = date_medium(Date, Language), Time_part = time_medium(Time, Language), <<<>/binary, Time_part/binary>>. -file("src/g18n.gleam", 2831). -spec datetime_long( gleam@time@calendar:date(), gleam@time@calendar:time_of_day(), binary() ) -> binary(). datetime_long(Date, Time, Language) -> Date_part = date_long(Date, Language), Time_part = time_long(Time, Language), <<<>/binary, Time_part/binary>>. -file("src/g18n.gleam", 2841). -spec datetime_full( gleam@time@calendar:date(), gleam@time@calendar:time_of_day(), binary() ) -> binary(). datetime_full(Date, Time, Language) -> datetime_long(Date, Time, Language). -file("src/g18n.gleam", 2176). ?DOC( " Format a date and time together according to the translator's locale and specified format.\n" "\n" " Combines date and time formatting into a single localized string, using appropriate\n" " separators and conventions for each language. Supports all format levels from\n" " compact numeric formats to full descriptive text.\n" "\n" " ## Format Types\n" " - `Short`: Compact format (e.g., \"12/25/23, 3:45 PM\", \"25/12/23, 15:45\")\n" " - `Medium`: Readable format (e.g., \"Dec 25, 2023, 3:45:30 PM\", \"25 dez 2023, 15:45:30\")\n" " - `Long`: Full format with timezone (e.g., \"December 25, 2023, 3:45:30 PM GMT\")\n" " - `Full`: Complete descriptive format (e.g., \"Monday, December 25, 2023, 3:45:30 PM GMT\")\n" " - `Custom(pattern)`: Custom pattern combining date and time placeholders\n" "\n" " ## Examples\n" " ```gleam\n" " let assert Ok(en_locale) = g18n.locale(\"en\")\n" " let assert Ok(pt_locale) = g18n.locale(\"pt\")\n" " let en_translator = g18n.translator(en_locale, g18n.translations())\n" " let pt_translator = g18n.translator(pt_locale, g18n.translations())\n" " let date = calendar.Date(year: 2023, month: calendar.December, day: 25)\n" " let time = calendar.TimeOfDay(hours: 15, minutes: 30, seconds: 0)\n" "\n" " // English formatting\n" " g18n.format_datetime(en_translator, date, time, g18n.Short)\n" " // \"12/25/23, 3:30 PM\"\n" "\n" " g18n.format_datetime(en_translator, date, time, g18n.Medium)\n" " // \"Dec 25, 2023, 3:30:00 PM\"\n" "\n" " g18n.format_datetime(en_translator, date, time, g18n.Long)\n" " // \"December 25, 2023, 3:30:00 PM GMT\"\n" "\n" " // Portuguese formatting\n" " g18n.format_datetime(pt_translator, date, time, g18n.Short)\n" " // \"25/12/23, 15:30\"\n" "\n" " g18n.format_datetime(pt_translator, date, time, g18n.Medium) \n" " // \"25 dez 2023, 15:30:00\"\n" "\n" " // Custom formatting\n" " g18n.format_datetime(en_translator, date, time, g18n.Custom(\"YYYY-MM-DD HH:mm\"))\n" " // \"2023-12-25 15:30\"\n" " ```\n" ). -spec format_datetime( translator(), gleam@time@calendar:date(), gleam@time@calendar:time_of_day(), date_time_format() ) -> binary(). format_datetime(Translator, Date, Time, Format) -> Language = begin _pipe = Translator, _pipe@1 = locale(_pipe), g18n@locale:language(_pipe@1) end, case Format of short -> datetime_short(Date, Time, Language); medium -> datetime_medium(Date, Time, Language); long -> datetime_long(Date, Time, Language); full -> datetime_full(Date, Time, Language); {custom, Pattern} -> datetime_custom(Date, Time, Pattern) end. -file("src/g18n.gleam", 3537). -spec calculate_day_of_week(gleam@time@calendar:date()) -> integer(). calculate_day_of_week(Date) -> Adjusted_month = case erlang:element(3, Date) of january -> 13; february -> 14; march -> 3; april -> 4; may -> 5; june -> 6; july -> 7; august -> 8; september -> 9; october -> 10; november -> 11; december -> 12 end, Adjusted_year = case erlang:element(3, Date) of january -> erlang:element(2, Date) - 1; february -> erlang:element(2, Date) - 1; _ -> erlang:element(2, Date) end, Century = Adjusted_year div 100, Year_of_century = Adjusted_year rem 100, Zeller = ((((erlang:element(4, Date) + ((13 * (Adjusted_month + 1)) div 5)) + Year_of_century) + (Year_of_century div 4)) + (Century div 4)) - (2 * Century), Day_zeller = ((Zeller rem 7) + 7) rem 7, case Day_zeller of 0 -> 6; 1 -> 0; 2 -> 1; 3 -> 2; 4 -> 3; 5 -> 4; 6 -> 5; _ -> 0 end. -file("src/g18n.gleam", 3446). -spec get_day_of_week_name(gleam@time@calendar:date(), binary()) -> binary(). get_day_of_week_name(Date, Language) -> Day_of_week = calculate_day_of_week(Date), case {Day_of_week, Language} of {0, <<"en"/utf8>>} -> <<"Sunday"/utf8>>; {1, <<"en"/utf8>>} -> <<"Monday"/utf8>>; {2, <<"en"/utf8>>} -> <<"Tuesday"/utf8>>; {3, <<"en"/utf8>>} -> <<"Wednesday"/utf8>>; {4, <<"en"/utf8>>} -> <<"Thursday"/utf8>>; {5, <<"en"/utf8>>} -> <<"Friday"/utf8>>; {6, <<"en"/utf8>>} -> <<"Saturday"/utf8>>; {0, <<"pt"/utf8>>} -> <<"domingo"/utf8>>; {1, <<"pt"/utf8>>} -> <<"segunda-feira"/utf8>>; {2, <<"pt"/utf8>>} -> <<"terça-feira"/utf8>>; {3, <<"pt"/utf8>>} -> <<"quarta-feira"/utf8>>; {4, <<"pt"/utf8>>} -> <<"quinta-feira"/utf8>>; {5, <<"pt"/utf8>>} -> <<"sexta-feira"/utf8>>; {6, <<"pt"/utf8>>} -> <<"sábado"/utf8>>; {0, <<"es"/utf8>>} -> <<"domingo"/utf8>>; {1, <<"es"/utf8>>} -> <<"lunes"/utf8>>; {2, <<"es"/utf8>>} -> <<"martes"/utf8>>; {3, <<"es"/utf8>>} -> <<"miércoles"/utf8>>; {4, <<"es"/utf8>>} -> <<"jueves"/utf8>>; {5, <<"es"/utf8>>} -> <<"viernes"/utf8>>; {6, <<"es"/utf8>>} -> <<"sábado"/utf8>>; {0, <<"fr"/utf8>>} -> <<"dimanche"/utf8>>; {1, <<"fr"/utf8>>} -> <<"lundi"/utf8>>; {2, <<"fr"/utf8>>} -> <<"mardi"/utf8>>; {3, <<"fr"/utf8>>} -> <<"mercredi"/utf8>>; {4, <<"fr"/utf8>>} -> <<"jeudi"/utf8>>; {5, <<"fr"/utf8>>} -> <<"vendredi"/utf8>>; {6, <<"fr"/utf8>>} -> <<"samedi"/utf8>>; {0, <<"de"/utf8>>} -> <<"Sonntag"/utf8>>; {1, <<"de"/utf8>>} -> <<"Montag"/utf8>>; {2, <<"de"/utf8>>} -> <<"Dienstag"/utf8>>; {3, <<"de"/utf8>>} -> <<"Mittwoch"/utf8>>; {4, <<"de"/utf8>>} -> <<"Donnerstag"/utf8>>; {5, <<"de"/utf8>>} -> <<"Freitag"/utf8>>; {6, <<"de"/utf8>>} -> <<"Samstag"/utf8>>; {0, <<"it"/utf8>>} -> <<"domenica"/utf8>>; {1, <<"it"/utf8>>} -> <<"lunedì"/utf8>>; {2, <<"it"/utf8>>} -> <<"martedì"/utf8>>; {3, <<"it"/utf8>>} -> <<"mercoledì"/utf8>>; {4, <<"it"/utf8>>} -> <<"giovedì"/utf8>>; {5, <<"it"/utf8>>} -> <<"venerdì"/utf8>>; {6, <<"it"/utf8>>} -> <<"sabato"/utf8>>; {0, <<"ru"/utf8>>} -> <<"воскресенье"/utf8>>; {1, <<"ru"/utf8>>} -> <<"понедельник"/utf8>>; {2, <<"ru"/utf8>>} -> <<"вторник"/utf8>>; {3, <<"ru"/utf8>>} -> <<"среда"/utf8>>; {4, <<"ru"/utf8>>} -> <<"четверг"/utf8>>; {5, <<"ru"/utf8>>} -> <<"пятница"/utf8>>; {6, <<"ru"/utf8>>} -> <<"суббота"/utf8>>; {0, <<"zh"/utf8>>} -> <<"星期日"/utf8>>; {1, <<"zh"/utf8>>} -> <<"星期一"/utf8>>; {2, <<"zh"/utf8>>} -> <<"星期二"/utf8>>; {3, <<"zh"/utf8>>} -> <<"星期三"/utf8>>; {4, <<"zh"/utf8>>} -> <<"星期四"/utf8>>; {5, <<"zh"/utf8>>} -> <<"星期五"/utf8>>; {6, <<"zh"/utf8>>} -> <<"星期六"/utf8>>; {0, <<"ja"/utf8>>} -> <<"日曜日"/utf8>>; {1, <<"ja"/utf8>>} -> <<"月曜日"/utf8>>; {2, <<"ja"/utf8>>} -> <<"火曜日"/utf8>>; {3, <<"ja"/utf8>>} -> <<"水曜日"/utf8>>; {4, <<"ja"/utf8>>} -> <<"木曜日"/utf8>>; {5, <<"ja"/utf8>>} -> <<"金曜日"/utf8>>; {6, <<"ja"/utf8>>} -> <<"土曜日"/utf8>>; {0, <<"ko"/utf8>>} -> <<"일요일"/utf8>>; {1, <<"ko"/utf8>>} -> <<"월요일"/utf8>>; {2, <<"ko"/utf8>>} -> <<"화요일"/utf8>>; {3, <<"ko"/utf8>>} -> <<"수요일"/utf8>>; {4, <<"ko"/utf8>>} -> <<"목요일"/utf8>>; {5, <<"ko"/utf8>>} -> <<"금요일"/utf8>>; {6, <<"ko"/utf8>>} -> <<"토요일"/utf8>>; {0, <<"ar"/utf8>>} -> <<"الأحد"/utf8>>; {1, <<"ar"/utf8>>} -> <<"الإثنين"/utf8>>; {2, <<"ar"/utf8>>} -> <<"الثلاثاء"/utf8>>; {3, <<"ar"/utf8>>} -> <<"الأربعاء"/utf8>>; {4, <<"ar"/utf8>>} -> <<"الخميس"/utf8>>; {5, <<"ar"/utf8>>} -> <<"الجمعة"/utf8>>; {6, <<"ar"/utf8>>} -> <<"السبت"/utf8>>; {0, <<"hi"/utf8>>} -> <<"रविवार"/utf8>>; {1, <<"hi"/utf8>>} -> <<"सोमवार"/utf8>>; {2, <<"hi"/utf8>>} -> <<"मंगलवार"/utf8>>; {3, <<"hi"/utf8>>} -> <<"बुधवार"/utf8>>; {4, <<"hi"/utf8>>} -> <<"गुरुवार"/utf8>>; {5, <<"hi"/utf8>>} -> <<"शुक्रवार"/utf8>>; {6, <<"hi"/utf8>>} -> <<"शनिवार"/utf8>>; {_, _} -> <<"Day "/utf8, (erlang:integer_to_binary(Day_of_week))/binary>> end. -file("src/g18n.gleam", 2653). -spec date_full(gleam@time@calendar:date(), binary()) -> binary(). date_full(Date, Language) -> Day_of_week = get_day_of_week_name(Date, Language), Month_name = get_month_name(erlang:element(3, Date), Language, true), case Language of <<"en"/utf8>> -> <<<<<<<<<<<<<>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(4, Date)))/binary>>/binary, ", "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>>; <<"pt"/utf8>> -> <<<<<<<<<<<<<>/binary, (erlang:integer_to_binary( erlang:element(4, Date) ))/binary>>/binary, " de "/utf8>>/binary, Month_name/binary>>/binary, " de "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>>; <<"es"/utf8>> -> <<<<<<<<<<<<<>/binary, (erlang:integer_to_binary( erlang:element(4, Date) ))/binary>>/binary, " de "/utf8>>/binary, Month_name/binary>>/binary, " de "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>>; <<"fr"/utf8>> -> <<<<<<<<<<<<<>/binary, (erlang:integer_to_binary( erlang:element(4, Date) ))/binary>>/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>>; <<"de"/utf8>> -> <<<<<<<<<<<<<>/binary, (erlang:integer_to_binary( erlang:element(4, Date) ))/binary>>/binary, ". "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>>; <<"it"/utf8>> -> <<<<<<<<<<<<<>/binary, (erlang:integer_to_binary( erlang:element(4, Date) ))/binary>>/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>>; <<"ru"/utf8>> -> <<<<<<<<<<<<<>/binary, (erlang:integer_to_binary( erlang:element(4, Date) ))/binary>>/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " г. GMT"/utf8>>; <<"zh"/utf8>> -> <<<<<<<<<<<<(erlang:integer_to_binary(erlang:element(2, Date)))/binary, "年"/utf8>>/binary, Month_name/binary>>/binary, (erlang:integer_to_binary(erlang:element(4, Date)))/binary>>/binary, "日"/utf8>>/binary, Day_of_week/binary>>/binary, " GMT"/utf8>>; <<"ja"/utf8>> -> <<<<<<<<<<<<(erlang:integer_to_binary(erlang:element(2, Date)))/binary, "年"/utf8>>/binary, Month_name/binary>>/binary, (erlang:integer_to_binary(erlang:element(4, Date)))/binary>>/binary, "日"/utf8>>/binary, Day_of_week/binary>>/binary, " GMT"/utf8>>; <<"ko"/utf8>> -> <<<<<<<<<<<<<<(erlang:integer_to_binary(erlang:element(2, Date)))/binary, "년 "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(4, Date)))/binary>>/binary, "일 "/utf8>>/binary, Day_of_week/binary>>/binary, " GMT"/utf8>>; <<"ar"/utf8>> -> <<<<<<<<<<<<<>/binary, (erlang:integer_to_binary( erlang:element(4, Date) ))/binary>>/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>>; <<"hi"/utf8>> -> <<<<<<<<<<<<<>/binary, (erlang:integer_to_binary( erlang:element(4, Date) ))/binary>>/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>>; _ -> <<<<<<<<<<<<<>/binary, (erlang:integer_to_binary( erlang:element(4, Date) ))/binary>>/binary, " "/utf8>>/binary, Month_name/binary>>/binary, " "/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Date)))/binary>>/binary, " GMT"/utf8>> end. -file("src/g18n.gleam", 2070). ?DOC( " Format a date according to the translator's locale and specified format.\n" " \n" " Supports multiple format levels from short numeric formats to full text \n" " with day-of-week names. Automatically uses locale-appropriate formatting\n" " including proper date separators, month names, and cultural conventions.\n" "\n" " ## Supported Languages\n" " English, Spanish, Portuguese, French, German, Italian, Russian, \n" " Chinese, Japanese, Korean, Arabic, Hindi (with fallback for others)\n" "\n" " ## Format Types\n" " - `Short`: Compact numeric format (e.g., \"12/25/23\", \"25/12/23\") \n" " - `Medium`: Month abbreviation (e.g., \"Dec 25, 2023\", \"25 dez 2023\")\n" " - `Long`: Full month names with GMT (e.g., \"December 25, 2023 GMT\")\n" " - `Full`: Complete with day-of-week (e.g., \"Monday, December 25, 2023 GMT\")\n" " - `Custom(pattern)`: Custom pattern with YYYY, MM, DD placeholders\n" "\n" " ## Examples\n" " ```gleam\n" " import gleam/time/calendar\n" " \n" " let assert Ok(en_locale) = g18n.locale(\"en\")\n" " let assert Ok(pt_locale) = g18n.locale(\"pt\") \n" " let translations = g18n.translations()\n" " let en_translator = g18n.translator(en_locale, translations)\n" " let pt_translator = g18n.translator(pt_locale, translations)\n" " \n" " let date = calendar.Date(2024, calendar.January, 15)\n" " \n" " g18n.format_date(en_translator, date, g18n.Short)\n" " // \"01/15/24\"\n" " \n" " g18n.format_date(pt_translator, date, g18n.Short) \n" " // \"15/01/24\"\n" " \n" " g18n.format_date(en_translator, date, g18n.Medium)\n" " // \"Jan 15, 2024\"\n" " \n" " g18n.format_date(en_translator, date, g18n.Full)\n" " // \"Monday, January 15, 2024 GMT\"\n" " \n" " g18n.format_date(en_translator, date, g18n.Custom(\"YYYY-MM-DD\"))\n" " // \"2024-01-15\"\n" " ```\n" ). -spec format_date(translator(), gleam@time@calendar:date(), date_time_format()) -> binary(). format_date(Translator, Date, Format) -> Language = begin _pipe = Translator, _pipe@1 = locale(_pipe), g18n@locale:language(_pipe@1) end, case Format of short -> date_short(Date, Language); medium -> date_medium(Date, Language); long -> date_long(Date, Language); full -> date_full(Date, Language); {custom, Pattern} -> date_custom(Date, Pattern) end. -file("src/g18n.gleam", 3600). ?DOC( " Create a new empty parameter container for string formatting.\n" "\n" " ## Examples\n" " ```gleam\n" " let params = g18n.new()\n" " |> g18n.add_param(\"name\", \"Alice\")\n" " |> g18n.add_param(\"count\", \"5\")\n" " ```\n" ). -spec new_format_params() -> gleam@dict:dict(binary(), binary()). new_format_params() -> maps:new(). -file("src/g18n.gleam", 3614). ?DOC( " Add a parameter key-value pair to a format parameters container.\n" "\n" " Used for template substitution in translations.\n" "\n" " ## Examples\n" " ```gleam\n" " let params = g18n.format_params()\n" " |> g18n.add_param(\"user\", \"Alice\")\n" " |> g18n.add_param(\"item_count\", \"3\")\n" " ```\n" ). -spec add_param(gleam@dict:dict(binary(), binary()), binary(), binary()) -> gleam@dict:dict(binary(), binary()). add_param(Params, Key, Value) -> gleam@dict:insert(Params, Key, Value).