Packages
phoenix_kit
1.7.66
1.7.208
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/modules/publishing/language_helpers.ex
defmodule PhoenixKit.Modules.Publishing.LanguageHelpers do
@moduledoc """
Pure language utility functions for the Publishing module.
Provides language detection, display ordering, language info lookup,
and primary language management.
"""
alias PhoenixKit.Modules.Languages
alias PhoenixKit.Modules.Languages.DialectMapper
alias PhoenixKit.Settings
@doc """
Returns all enabled language codes for multi-language support.
Falls back to content language if Languages module is disabled.
"""
@spec enabled_language_codes() :: [String.t()]
def enabled_language_codes do
if Languages.enabled?() do
Languages.get_enabled_language_codes()
else
[Settings.get_content_language()]
end
end
@doc """
Returns the primary/canonical language for versioning.
Uses Settings.get_content_language().
"""
@spec get_primary_language() :: String.t()
def get_primary_language do
Settings.get_content_language()
end
@doc """
Gets language details (name, flag) for a given language code.
Searches in order:
1. Predefined languages (BeamLabCountries) - for full locale details
2. User-configured languages - for custom/less common languages
"""
@spec get_language_info(String.t()) ::
%{code: String.t(), name: String.t(), flag: String.t()} | nil
def get_language_info(language_code) do
find_in_predefined_languages(language_code) ||
find_in_configured_languages(language_code)
end
@doc """
Checks if a language code is enabled, considering base code matching.
Handles cases where:
- The code is `en` and enabled languages has `"en-US"` -> matches
- The code is `en-US` and enabled languages has `"en"` -> matches
"""
@spec language_enabled?(String.t(), [String.t()]) :: boolean()
def language_enabled?(language_code, enabled_languages) do
if language_code in enabled_languages do
true
else
base_code = DialectMapper.extract_base(language_code)
Enum.any?(enabled_languages, fn enabled_lang ->
enabled_lang == language_code or
DialectMapper.extract_base(enabled_lang) == base_code
end)
end
end
@doc """
Determines the display code for a language based on whether multiple dialects
of the same base language are enabled.
If only one dialect of a base language is enabled (e.g., just "en-US"),
returns the base code ("en") for cleaner display.
If multiple dialects are enabled (e.g., "en-US" and "en-GB"),
returns the full dialect code ("en-US") to distinguish them.
"""
@spec get_display_code(String.t(), [String.t()]) :: String.t()
def get_display_code(language_code, enabled_languages) do
base_code = DialectMapper.extract_base(language_code)
dialects_count =
Enum.count(enabled_languages, fn lang ->
DialectMapper.extract_base(lang) == base_code
end)
if dialects_count > 1 do
language_code
else
base_code
end
end
@doc """
Orders languages for display in the language switcher.
Order: primary language first, then languages with translations (sorted),
then languages without translations (sorted).
"""
@spec order_languages_for_display([String.t()], [String.t()], String.t() | nil) :: [String.t()]
def order_languages_for_display(available_languages, enabled_languages, primary_language \\ nil) do
primary_lang = primary_language || get_primary_language()
langs_with_content =
available_languages
|> Enum.reject(&(&1 == primary_lang))
|> Enum.sort()
langs_without_content =
enabled_languages
|> Enum.reject(&(&1 in available_languages or &1 == primary_lang))
|> Enum.sort()
[primary_lang] ++ langs_with_content ++ langs_without_content
end
@doc """
Checks if a language code is reserved (cannot be used as a slug).
"""
@spec reserved_language_code?(String.t()) :: boolean()
def reserved_language_code?(slug) do
language_codes =
try do
Languages.get_language_codes()
rescue
_ -> []
end
slug in language_codes
end
# ===========================================================================
# Private Helpers
# ===========================================================================
defp find_in_predefined_languages(language_code) do
case Languages.get_available_language_by_code(language_code) do
nil ->
base_code = DialectMapper.extract_base(language_code)
is_base_code = language_code == base_code and not String.contains?(language_code, "-")
default_dialect = DialectMapper.base_to_dialect(base_code)
case Languages.get_available_language_by_code(default_dialect) do
nil ->
all_languages = Languages.get_available_languages()
Enum.find(all_languages, fn lang ->
DialectMapper.extract_base(lang.code) == base_code
end)
default_match ->
if is_base_code do
%{default_match | name: extract_base_language_name(default_match.name)}
else
default_match
end
end
exact_match ->
exact_match
end
end
defp extract_base_language_name(name) when is_binary(name) do
case String.split(name, " (", parts: 2) do
[base_name, _region] -> base_name
[base_name] -> base_name
end
end
defp extract_base_language_name(name), do: name
defp find_in_configured_languages(language_code) do
configured_languages = Languages.get_languages()
exact_match =
Enum.find(configured_languages, fn lang -> lang.code == language_code end)
result =
if exact_match do
exact_match
else
base_code = DialectMapper.extract_base(language_code)
default_dialect = DialectMapper.base_to_dialect(base_code)
default_match =
Enum.find(configured_languages, fn lang -> lang.code == default_dialect end)
if default_match do
default_match
else
Enum.find(configured_languages, fn lang ->
DialectMapper.extract_base(lang.code) == base_code
end)
end
end
if result do
%{
code: result.code,
name: result.name || result.code,
flag: result.flag || ""
}
else
nil
end
end
end