diff --git a/lib/modules/maintenance/settings.ex b/lib/modules/maintenance/settings.ex
index 1061e5c67..cc56d6d58 100644
--- a/lib/modules/maintenance/settings.ex
+++ b/lib/modules/maintenance/settings.ex
@@ -9,8 +9,20 @@ defmodule PhoenixKitWeb.Live.Modules.Maintenance.Settings do
alias PhoenixKit.Modules.Maintenance
alias PhoenixKit.Settings
+ alias PhoenixKit.Utils.Routes
+
+ def mount(params, _session, socket) do
+ # Set locale for LiveView process FIRST - preserve from params or socket assigns
+ locale = params["locale"] || socket.assigns[:current_locale]
+
+ if locale do
+ Gettext.put_locale(PhoenixKitWeb.Gettext, locale)
+ Process.put(:phoenix_kit_current_locale, locale)
+ end
+
+ # Get current path for navigation (after locale is set)
+ current_path = Routes.path("/admin/settings/maintenance")
- def mount(_params, _session, socket) do
# Get current settings
config = Maintenance.get_config()
@@ -18,6 +30,8 @@ defmodule PhoenixKitWeb.Live.Modules.Maintenance.Settings do
socket
|> assign(:page_title, "Maintenance Mode Settings")
|> assign(:project_title, Settings.get_setting("project_title", "PhoenixKit"))
+ |> assign(:current_path, current_path)
+ |> assign(:current_locale, locale)
|> assign(:header, config.header)
|> assign(:subtext, config.subtext)
|> assign(:enabled, config.enabled)
@@ -108,9 +122,9 @@ defmodule PhoenixKitWeb.Live.Modules.Maintenance.Settings do
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title="{@project_title} - Maintenance Mode Settings"
- current_path={@url_path}
+ current_path={@current_path}
project_title={@project_title}
- current_locale={assigns[:current_locale] || "en"}
+ current_locale={@current_locale}
>
<%!-- Header Section --%>
diff --git a/lib/phoenix_kit/modules/languages.ex b/lib/phoenix_kit/modules/languages.ex
index 9c702ce6d..6720c870a 100644
--- a/lib/phoenix_kit/modules/languages.ex
+++ b/lib/phoenix_kit/modules/languages.ex
@@ -8,8 +8,8 @@ defmodule PhoenixKit.Modules.Languages do
## Language Structure
Each language has the following structure:
- - `code`: Language code (e.g., "en", "es", "fr")
- - `name`: Full language name (e.g., "English", "Spanish", "French")
+ - `code`: Language code (e.g., "en-US", "es-ES", "fr-FR")
+ - `name`: Full language name (e.g., "English (United States)", "Spanish (Spain)", "French (France)")
- `is_default`: Boolean indicating if this is the default language
- `is_enabled`: Boolean indicating if this language is active
@@ -52,22 +52,22 @@ defmodule PhoenixKit.Modules.Languages do
# Get all languages
languages = PhoenixKit.Modules.Languages.get_languages()
- # => [%{code: "en", name: "English", is_default: true, is_enabled: true}, ...]
+ # => [%{code: "en-US", name: "English (United States)", is_default: true, is_enabled: true}, ...]
# Get only enabled languages (most common use case)
enabled_languages = PhoenixKit.Modules.Languages.get_enabled_languages()
- # => [%{code: "en", name: "English", ...}, %{code: "es", name: "Spanish", ...}]
+ # => [%{code: "en-US", name: "English (United States)", ...}, %{code: "es-ES", name: "Spanish (Spain)", ...}]
# Get a specific language by code
- spanish = PhoenixKit.Modules.Languages.get_language("es")
- # => %{code: "es", name: "Spanish", is_enabled: true}
+ spanish = PhoenixKit.Modules.Languages.get_language("es-ES")
+ # => %{code: "es-ES", name: "Spanish (Spain)", is_enabled: true}
# Get just the language codes
codes = PhoenixKit.Modules.Languages.get_enabled_language_codes()
- # => ["en", "es", "fr"]
+ # => ["en-US", "es-ES", "fr-FR"]
# Check if a language is valid and enabled
- if PhoenixKit.Modules.Languages.language_enabled?("es") do
+ if PhoenixKit.Modules.Languages.language_enabled?("es-ES") do
# Use Spanish language
end
@@ -79,14 +79,14 @@ defmodule PhoenixKit.Modules.Languages do
{
"languages": [
{
- "code": "en",
- "name": "English",
+ "code": "en-US",
+ "name": "English (United States)",
"is_default": true,
"is_enabled": true
},
{
- "code": "es",
- "name": "Spanish",
+ "code": "es-ES",
+ "name": "Spanish (Spain)",
"is_default": false,
"is_enabled": true
}
@@ -119,14 +119,20 @@ defmodule PhoenixKit.Modules.Languages do
%{code: "cs", name: "Czech", native: "Čeština", flag: "🇨🇿"},
%{code: "da", name: "Danish", native: "Dansk", flag: "🇩🇰"},
%{code: "nl", name: "Dutch", native: "Nederlands", flag: "🇳🇱"},
- %{code: "en", name: "English", native: "English", flag: "🇺🇸"},
+ %{code: "en-US", name: "English (United States)", native: "English (US)", flag: "🇺🇸"},
+ %{code: "en-GB", name: "English (United Kingdom)", native: "English (UK)", flag: "🇬🇧"},
+ %{code: "en-CA", name: "English (Canada)", native: "English (CA)", flag: "🇨🇦"},
+ %{code: "en-AU", name: "English (Australia)", native: "English (AU)", flag: "🇦🇺"},
%{code: "et", name: "Estonian", native: "Eesti", flag: "🇪🇪"},
%{code: "tl", name: "Filipino", native: "Filipino", flag: "🇵🇭"},
%{code: "fi", name: "Finnish", native: "Suomi", flag: "🇫🇮"},
- %{code: "fr", name: "French", native: "Français", flag: "🇫🇷"},
+ %{code: "fr-FR", name: "French (France)", native: "Français (France)", flag: "🇫🇷"},
+ %{code: "fr-CA", name: "French (Canada)", native: "Français (Canada)", flag: "🇨🇦"},
%{code: "gl", name: "Galician", native: "Galego", flag: "🏴"},
%{code: "ka", name: "Georgian", native: "ქართული", flag: "🇬🇪"},
- %{code: "de", name: "German", native: "Deutsch", flag: "🇩🇪"},
+ %{code: "de-DE", name: "German (Germany)", native: "Deutsch (Deutschland)", flag: "🇩🇪"},
+ %{code: "de-AT", name: "German (Austria)", native: "Deutsch (Österreich)", flag: "🇦🇹"},
+ %{code: "de-CH", name: "German (Switzerland)", native: "Deutsch (Schweiz)", flag: "🇨🇭"},
%{code: "gu", name: "Gujarati", native: "ગુજરાતી", flag: "🇮🇳"},
%{code: "he", name: "Hebrew", native: "עברית", flag: "🇮🇱"},
%{code: "hi", name: "Hindi", native: "हिन्दी", flag: "🇮🇳"},
@@ -156,7 +162,8 @@ defmodule PhoenixKit.Modules.Languages do
%{code: "no", name: "Norwegian", native: "Norsk", flag: "🇳🇴"},
%{code: "fa", name: "Persian", native: "فارسی", flag: "🇮🇷"},
%{code: "pl", name: "Polish", native: "Polski", flag: "🇵🇱"},
- %{code: "pt", name: "Portuguese", native: "Português", flag: "🇵🇹"},
+ %{code: "pt-PT", name: "Portuguese (Portugal)", native: "Português (Portugal)", flag: "🇵🇹"},
+ %{code: "pt-BR", name: "Portuguese (Brazil)", native: "Português (Brasil)", flag: "🇧🇷"},
%{code: "pa", name: "Punjabi", native: "ਪੰਜਾਬੀ", flag: "🇮🇳"},
%{code: "ro", name: "Romanian", native: "Română", flag: "🇷🇴"},
%{code: "ru", name: "Russian", native: "Русский", flag: "🇷🇺"},
@@ -165,7 +172,10 @@ defmodule PhoenixKit.Modules.Languages do
%{code: "si", name: "Sinhala", native: "සිංහල", flag: "🇱🇰"},
%{code: "sk", name: "Slovak", native: "Slovenčina", flag: "🇸🇰"},
%{code: "sl", name: "Slovenian", native: "Slovenščina", flag: "🇸🇮"},
- %{code: "es", name: "Spanish", native: "Español", flag: "🇪🇸"},
+ %{code: "es-ES", name: "Spanish (Spain)", native: "Español (España)", flag: "🇪🇸"},
+ %{code: "es-MX", name: "Spanish (Mexico)", native: "Español (México)", flag: "🇲🇽"},
+ %{code: "es-AR", name: "Spanish (Argentina)", native: "Español (Argentina)", flag: "🇦🇷"},
+ %{code: "es-CO", name: "Spanish (Colombia)", native: "Español (Colombia)", flag: "🇨🇴"},
%{code: "sw", name: "Swahili", native: "Kiswahili", flag: "🇰🇪"},
%{code: "sv", name: "Swedish", native: "Svenska", flag: "🇸🇪"},
%{code: "ta", name: "Tamil", native: "தமிழ்", flag: "🇱🇰"},
@@ -183,8 +193,8 @@ defmodule PhoenixKit.Modules.Languages do
@default_config %{
"languages" => [
%{
- "code" => "en",
- "name" => "English",
+ "code" => "en-US",
+ "name" => "English (United States)",
"is_default" => true,
"is_enabled" => true
}
@@ -193,12 +203,37 @@ defmodule PhoenixKit.Modules.Languages do
# Top 10 most common languages for permanent display in admin
@top_10_languages [
- %{"code" => "en", "name" => "English", "is_default" => true, "is_enabled" => true},
- %{"code" => "es", "name" => "Spanish", "is_default" => false, "is_enabled" => true},
- %{"code" => "fr", "name" => "French", "is_default" => false, "is_enabled" => true},
- %{"code" => "de", "name" => "German", "is_default" => false, "is_enabled" => true},
+ %{
+ "code" => "en-US",
+ "name" => "English (United States)",
+ "is_default" => true,
+ "is_enabled" => true
+ },
+ %{
+ "code" => "es-ES",
+ "name" => "Spanish (Spain)",
+ "is_default" => false,
+ "is_enabled" => true
+ },
+ %{
+ "code" => "fr-FR",
+ "name" => "French (France)",
+ "is_default" => false,
+ "is_enabled" => true
+ },
+ %{
+ "code" => "de-DE",
+ "name" => "German (Germany)",
+ "is_default" => false,
+ "is_enabled" => true
+ },
%{"code" => "ja", "name" => "Japanese", "is_default" => false, "is_enabled" => true},
- %{"code" => "pt", "name" => "Portuguese", "is_default" => false, "is_enabled" => true},
+ %{
+ "code" => "pt-BR",
+ "name" => "Portuguese (Brazil)",
+ "is_default" => false,
+ "is_enabled" => true
+ },
%{"code" => "it", "name" => "Italian", "is_default" => false, "is_enabled" => true},
%{"code" => "ko", "name" => "Korean", "is_default" => false, "is_enabled" => true},
%{"code" => "ru", "name" => "Russian", "is_default" => false, "is_enabled" => true},
@@ -240,7 +275,7 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
iex> PhoenixKit.Modules.Languages.enable_system()
- {:ok, %{"languages" => [%{"code" => "en", ...}]}}
+ {:ok, %{"languages" => [%{"code" => "en-US", ...}]}}
"""
def enable_system do
# Enable the system
@@ -287,10 +322,10 @@ defmodule PhoenixKit.Modules.Languages do
iex> PhoenixKit.Modules.Languages.get_config()
%{
enabled: true,
- languages: [%{"code" => "en", "name" => "English", ...}],
+ languages: [%{"code" => "en-US", "name" => "English (United States)", ...}],
language_count: 1,
enabled_count: 1,
- default_language: %{"code" => "en", "name" => "English", ...}
+ default_language: %{"code" => "en-US", "name" => "English (United States)", ...}
}
"""
def get_config do
@@ -318,7 +353,7 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
iex> PhoenixKit.Modules.Languages.get_languages()
- [%{"code" => "en", "name" => "English", "is_default" => true, ...}]
+ [%{"code" => "en-US", "name" => "English (United States)", "is_default" => true, ...}]
# When system is disabled:
iex> PhoenixKit.Modules.Languages.get_languages()
@@ -343,7 +378,7 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
iex> PhoenixKit.Modules.Languages.get_enabled_languages()
- [%{"code" => "en", "name" => "English", ...}]
+ [%{"code" => "en-US", "name" => "English (United States)", ...}]
"""
def get_enabled_languages do
get_languages()
@@ -359,7 +394,7 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
iex> PhoenixKit.Modules.Languages.get_default_language()
- %{"code" => "en", "name" => "English", "is_default" => true, ...}
+ %{"code" => "en-US", "name" => "English (United States)", "is_default" => true, ...}
# When system is disabled:
iex> PhoenixKit.Modules.Languages.get_default_language()
@@ -381,8 +416,8 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
- iex> PhoenixKit.Modules.Languages.get_language("es")
- %{"code" => "es", "name" => "Spanish", "is_enabled" => true}
+ iex> PhoenixKit.Modules.Languages.get_language("es-ES")
+ %{"code" => "es-ES", "name" => "Spanish (Spain)", "is_enabled" => true}
iex> PhoenixKit.Modules.Languages.get_language("invalid")
nil
@@ -404,7 +439,7 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
iex> PhoenixKit.Modules.Languages.get_language_codes()
- ["en", "es", "fr"]
+ ["en-US", "es-ES", "fr-FR"]
"""
def get_language_codes do
get_languages()
@@ -419,7 +454,7 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
iex> PhoenixKit.Modules.Languages.get_enabled_language_codes()
- ["en", "es"]
+ ["en-US", "es-ES"]
"""
def get_enabled_language_codes do
get_enabled_languages()
@@ -435,21 +470,21 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
iex> PhoenixKit.Modules.Languages.enabled_locale_codes()
- ["en", "es", "fr"]
+ ["en-US", "es-ES", "fr-FR"]
# When system is disabled:
iex> PhoenixKit.Modules.Languages.enabled_locale_codes()
- ["en"]
+ ["en-US"]
"""
def enabled_locale_codes do
# Return enabled language codes from the frontend language module only
# Admin navbar languages are managed separately in settings
if enabled?() do
codes = get_enabled_language_codes()
- # Ensure we always have at least "en" as a fallback
- if Enum.empty?(codes), do: ["en"], else: codes
+ # Ensure we always have at least "en-US" as a fallback
+ if Enum.empty?(codes), do: ["en-US"], else: codes
else
- ["en"]
+ ["en-US"]
end
end
@@ -466,11 +501,11 @@ defmodule PhoenixKit.Modules.Languages do
# When enabled (any number of languages)
iex> PhoenixKit.Modules.Languages.get_display_languages()
- [%{"code" => "en", "name" => "English", ...}]
+ [%{"code" => "en-US", "name" => "English (United States)", ...}]
# When disabled
iex> PhoenixKit.Modules.Languages.get_display_languages()
- [%{"code" => "en", ...}, %{"code" => "es", ...}, ...] # Top 12 default
+ [%{"code" => "en-US", ...}, %{"code" => "es-ES", ...}, ...] # Top 12 default
"""
def get_display_languages do
if enabled?() do
@@ -507,7 +542,7 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
- iex> PhoenixKit.Modules.Languages.valid_language?("es")
+ iex> PhoenixKit.Modules.Languages.valid_language?("es-ES")
true
iex> PhoenixKit.Modules.Languages.valid_language?("invalid")
@@ -524,7 +559,7 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
- iex> PhoenixKit.Modules.Languages.language_enabled?("es")
+ iex> PhoenixKit.Modules.Languages.language_enabled?("es-ES")
true
iex> PhoenixKit.Modules.Languages.language_enabled?("disabled_lang")
@@ -547,7 +582,7 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
iex> PhoenixKit.Modules.Languages.get_default_language_codes()
- ["en", "es", "fr", "de", "pt", "it", "nl", "ru", "ja", "ko", "zh-CN", "ar"]
+ ["en-US", "es-ES", "fr-FR", "de-DE", "pt-BR", "it", "nl", "ru", "ja", "ko", "zh-CN", "ar"]
"""
def get_default_language_codes do
@top_10_languages
@@ -563,7 +598,7 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
iex> PhoenixKit.Modules.Languages.get_available_languages()
- [%{code: "en", name: "English", native: "English", flag: "🇺🇸"}, ...]
+ [%{code: "en-US", name: "English (United States)", native: "English (US)", flag: "🇺🇸"}, ...]
"""
def get_available_languages do
@available_languages
@@ -578,7 +613,7 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
iex> PhoenixKit.Modules.Languages.get_available_languages_for_selection()
- [%{code: "es", name: "Spanish", native: "Español", flag: "🇪🇸"}, ...]
+ [%{code: "es-ES", name: "Spanish (Spain)", native: "Español (España)", flag: "🇪🇸"}, ...]
"""
def get_available_languages_for_selection do
if enabled?() do
@@ -598,8 +633,8 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
- iex> PhoenixKit.Modules.Languages.get_predefined_language("es")
- %{code: "es", name: "Spanish", native: "Español", flag: "🇪🇸"}
+ iex> PhoenixKit.Modules.Languages.get_predefined_language("es-ES")
+ %{code: "es-ES", name: "Spanish (Spain)", native: "Español (España)", flag: "🇪🇸"}
iex> PhoenixKit.Modules.Languages.get_predefined_language("invalid")
nil
@@ -617,10 +652,10 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
- iex> PhoenixKit.Modules.Languages.add_language("es")
+ iex> PhoenixKit.Modules.Languages.add_language("es-ES")
{:ok, updated_config}
- iex> PhoenixKit.Modules.Languages.add_language("en") # if already exists
+ iex> PhoenixKit.Modules.Languages.add_language("en-US") # if already exists
{:error, "Language already exists"}
iex> PhoenixKit.Modules.Languages.add_language("invalid")
@@ -668,7 +703,7 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
- iex> PhoenixKit.Modules.Languages.update_language("es", %{name: "Español"})
+ iex> PhoenixKit.Modules.Languages.update_language("es-ES", %{name: "Español"})
{:ok, updated_config}
iex> PhoenixKit.Modules.Languages.update_language("nonexistent", %{name: "Test"})
@@ -694,10 +729,10 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
- iex> PhoenixKit.Modules.Languages.remove_language("es")
+ iex> PhoenixKit.Modules.Languages.remove_language("es-ES")
{:ok, updated_config}
- iex> PhoenixKit.Modules.Languages.remove_language("en") # if it's default
+ iex> PhoenixKit.Modules.Languages.remove_language("en-US") # if it's default
{:error, "Cannot remove default language"}
"""
def remove_language(code) when is_binary(code) do
@@ -735,7 +770,7 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
- iex> PhoenixKit.Modules.Languages.set_default_language("es")
+ iex> PhoenixKit.Modules.Languages.set_default_language("es-ES")
{:ok, updated_config}
iex> PhoenixKit.Modules.Languages.set_default_language("nonexistent")
@@ -750,7 +785,7 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
- iex> PhoenixKit.Modules.Languages.enable_language("es")
+ iex> PhoenixKit.Modules.Languages.enable_language("es-ES")
{:ok, updated_config}
"""
def enable_language(code) when is_binary(code) do
@@ -764,10 +799,10 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
- iex> PhoenixKit.Modules.Languages.disable_language("es")
+ iex> PhoenixKit.Modules.Languages.disable_language("es-ES")
{:ok, updated_config}
- iex> PhoenixKit.Modules.Languages.disable_language("en") # if it's default
+ iex> PhoenixKit.Modules.Languages.disable_language("en-US") # if it's default
{:error, "Cannot disable default language"}
"""
def disable_language(code) when is_binary(code) do
@@ -788,10 +823,10 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
- iex> PhoenixKit.Modules.Languages.move_language_up("es")
+ iex> PhoenixKit.Modules.Languages.move_language_up("es-ES")
{:ok, updated_config}
- iex> PhoenixKit.Modules.Languages.move_language_up("en") # if first in array
+ iex> PhoenixKit.Modules.Languages.move_language_up("en-US") # if first in array
{:error, "Language is already at the top"}
"""
def move_language_up(code) when is_binary(code) do
@@ -835,10 +870,10 @@ defmodule PhoenixKit.Modules.Languages do
## Examples
- iex> PhoenixKit.Modules.Languages.move_language_down("en")
+ iex> PhoenixKit.Modules.Languages.move_language_down("en-US")
{:ok, updated_config}
- iex> PhoenixKit.Modules.Languages.move_language_down("es") # if last in array
+ iex> PhoenixKit.Modules.Languages.move_language_down("es-ES") # if last in array
{:error, "Language is already at the bottom"}
"""
def move_language_down(code) when is_binary(code) do
diff --git a/lib/phoenix_kit/settings/settings.ex b/lib/phoenix_kit/settings/settings.ex
index 25483d92d..3c8adc7db 100644
--- a/lib/phoenix_kit/settings/settings.ex
+++ b/lib/phoenix_kit/settings/settings.ex
@@ -740,30 +740,34 @@ defmodule PhoenixKit.Settings do
],
"time_zone" => [
{"UTC-12 (Baker Island)", "-12"},
- {"UTC-11 (American Samoa)", "-11"},
- {"UTC-10 (Hawaii)", "-10"},
- {"UTC-9 (Alaska)", "-9"},
- {"UTC-8 (Pacific Time)", "-8"},
- {"UTC-7 (Mountain Time)", "-7"},
- {"UTC-6 (Central Time)", "-6"},
- {"UTC-5 (Eastern Time)", "-5"},
- {"UTC-4 (Atlantic Time)", "-4"},
- {"UTC-3 (Argentina)", "-3"},
- {"UTC-2 (Mid-Atlantic)", "-2"},
- {"UTC-1 (Cape Verde)", "-1"},
- {"UTC+0 (GMT/London)", "0"},
- {"UTC+1 (Central Europe)", "1"},
- {"UTC+2 (Eastern Europe)", "2"},
- {"UTC+3 (Moscow)", "3"},
- {"UTC+4 (Dubai)", "4"},
- {"UTC+5 (Pakistan)", "5"},
- {"UTC+6 (Bangladesh)", "6"},
- {"UTC+7 (Thailand)", "7"},
- {"UTC+8 (China/Singapore)", "8"},
- {"UTC+9 (Japan/Korea)", "9"},
- {"UTC+10 (Australia East)", "10"},
- {"UTC+11 (Solomon Islands)", "11"},
- {"UTC+12 (New Zealand)", "12"}
+ {"UTC-11 (Pago Pago, Niue)", "-11"},
+ {"UTC-10 (Honolulu, Tahiti)", "-10"},
+ {"UTC-9 (Anchorage, Juneau)", "-9"},
+ {"UTC-8 (Los Angeles, Vancouver, Seattle)", "-8"},
+ {"UTC-7 (Denver, Phoenix, Calgary)", "-7"},
+ {"UTC-6 (Chicago, Mexico City, Guatemala)", "-6"},
+ {"UTC-5 (New York, Toronto, Bogotá, Lima)", "-5"},
+ {"UTC-4 (Halifax, Caracas, Santiago)", "-4"},
+ {"UTC-3 (Buenos Aires, São Paulo, Montevideo)", "-3"},
+ {"UTC-2 (South Georgia)", "-2"},
+ {"UTC-1 (Azores, Cape Verde)", "-1"},
+ {"UTC+0 (London, Dublin, Lisbon, Accra)", "0"},
+ {"UTC+1 (Paris, Berlin, Rome, Madrid, Lagos)", "1"},
+ {"UTC+2 (Kyiv, Athens, Helsinki, Cairo, Johannesburg)", "2"},
+ {"UTC+3 (Istanbul, Riyadh, Nairobi, Baghdad, Moscow)", "3"},
+ {"UTC+4 (Dubai, Baku, Tbilisi)", "4"},
+ {"UTC+5 (Karachi, Tashkent, Yekaterinburg)", "5"},
+ {"UTC+5:30 (Mumbai, Delhi, Kolkata, Colombo)", "5.5"},
+ {"UTC+6 (Dhaka, Almaty, Bishkek)", "6"},
+ {"UTC+7 (Bangkok, Jakarta, Ho Chi Minh City)", "7"},
+ {"UTC+8 (Beijing, Singapore, Hong Kong, Perth)", "8"},
+ {"UTC+9 (Tokyo, Seoul, Pyongyang)", "9"},
+ {"UTC+9:30 (Adelaide, Darwin)", "9.5"},
+ {"UTC+10 (Sydney, Melbourne, Brisbane)", "10"},
+ {"UTC+11 (Honiara, Noumea)", "11"},
+ {"UTC+12 (Auckland, Fiji, Wellington)", "12"},
+ {"UTC+13 (Nuku'alofa, Apia)", "13"},
+ {"UTC+14 (Kiritimati)", "14"}
],
"date_format" => UtilsDate.get_date_format_options(),
"time_format" => UtilsDate.get_time_format_options()
diff --git a/lib/phoenix_kit/utils/routes.ex b/lib/phoenix_kit/utils/routes.ex
index 42b90f945..3ab469fcb 100644
--- a/lib/phoenix_kit/utils/routes.ex
+++ b/lib/phoenix_kit/utils/routes.ex
@@ -23,7 +23,6 @@ defmodule PhoenixKit.Utils.Routes do
case locale do
:none -> "#{base_path}#{url_path}"
- "en" -> "#{base_path}#{url_path}"
locale_value -> "#{base_path}/#{locale_value}#{url_path}"
end
else
diff --git a/lib/phoenix_kit_web/components/admin_nav.ex b/lib/phoenix_kit_web/components/admin_nav.ex
index da24e0807..2eb566f24 100644
--- a/lib/phoenix_kit_web/components/admin_nav.ex
+++ b/lib/phoenix_kit_web/components/admin_nav.ex
@@ -44,6 +44,7 @@ defmodule PhoenixKitWeb.Components.AdminNav do
attr(:nested, :boolean, default: false)
attr(:disable_active, :boolean, default: false)
attr(:exact_match_only, :boolean, default: false)
+ attr(:submenu_open, :boolean, default: false)
def admin_nav_item(assigns) do
active =
@@ -64,10 +65,16 @@ defmodule PhoenixKitWeb.Components.AdminNav do
navigate={@href}
class={[
"flex items-center py-2 rounded-lg text-sm font-medium transition-colors group",
- if(@active,
- do: "bg-primary text-primary-content hover:bg-primary/90",
- else: "text-base-content hover:bg-base-200 hover:text-primary"
- ),
+ cond do
+ @active ->
+ "bg-primary text-primary-content hover:bg-primary/90"
+
+ @submenu_open ->
+ "bg-base-200/50 text-base-content hover:bg-base-200 hover:text-primary"
+
+ true ->
+ "text-base-content hover:bg-base-200 hover:text-primary"
+ end,
if(@mobile, do: "w-full", else: ""),
if(@nested, do: "pl-8 pr-3", else: "px-3")
]}
@@ -131,6 +138,8 @@ defmodule PhoenixKitWeb.Components.AdminNav do
<.icon name="hero-wrench-screwdriver" class="w-5 h-5" />
<% "storage" -> %>
<.icon name="hero-folder" class="w-5 h-5" />
+ <% "photo" -> %>
+ <.icon name="hero-photo" class="w-5 h-5" />
<% _ -> %>
<.icon name="hero-squares-2x2" class="w-5 h-5" />
<% end %>
@@ -588,7 +597,7 @@ defmodule PhoenixKitWeb.Components.AdminNav do
end
defp locale_candidate?(locale) do
- String.length(locale) in 2..5 and Regex.match?(~r/^[a-z]{2}(?:-[A-Za-z0-9]{2,})?$/, locale)
+ String.length(locale) in 2..6 and Regex.match?(~r/^[a-z]{2}(?:-[A-Z]{2})?$/, locale)
end
# Helper function to get admin languages from settings
diff --git a/lib/phoenix_kit_web/components/layout_wrapper.ex b/lib/phoenix_kit_web/components/layout_wrapper.ex
index d061e75b2..d807153da 100644
--- a/lib/phoenix_kit_web/components/layout_wrapper.ex
+++ b/lib/phoenix_kit_web/components/layout_wrapper.ex
@@ -244,6 +244,15 @@ defmodule PhoenixKitWeb.Components.LayoutWrapper do
label="Users"
current_path={@current_path || ""}
disable_active={true}
+ submenu_open={
+ submenu_open?(@current_path, [
+ "/admin/users",
+ "/admin/users/live_sessions",
+ "/admin/users/sessions",
+ "/admin/users/roles",
+ "/admin/users/referral-codes"
+ ])
+ }
/>
<%= if submenu_open?(@current_path, ["/admin/users", "/admin/users/live_sessions", "/admin/users/sessions", "/admin/users/roles", "/admin/users/referral-codes"]) do %>
@@ -309,6 +318,15 @@ defmodule PhoenixKitWeb.Components.LayoutWrapper do
label="Emails"
current_path={@current_path || ""}
disable_active={true}
+ submenu_open={
+ submenu_open?(@current_path, [
+ "/admin/emails",
+ "/admin/emails/dashboard",
+ "/admin/modules/emails/templates",
+ "/admin/emails/queue",
+ "/admin/emails/blocklist"
+ ])
+ }
/>
<%= if submenu_open?(@current_path, ["/admin/emails", "/admin/emails/dashboard", "/admin/modules/emails/templates", "/admin/emails/queue", "/admin/emails/blocklist"]) do %>
@@ -365,6 +383,7 @@ defmodule PhoenixKitWeb.Components.LayoutWrapper do
label="Entities"
current_path={@current_path || ""}
disable_active={true}
+ submenu_open={submenu_open?(@current_path, ["/admin/entities"])}
/>
<%= if submenu_open?(@current_path, ["/admin/entities"]) do %>
@@ -405,6 +424,7 @@ defmodule PhoenixKitWeb.Components.LayoutWrapper do
label="Blogging"
current_path={@current_path || ""}
exact_match_only={true}
+ submenu_open={submenu_open?(@current_path, ["/admin/blogging"])}
/>
<%= if submenu_open?(@current_path, ["/admin/blogging"]) do %>
@@ -436,6 +456,21 @@ defmodule PhoenixKitWeb.Components.LayoutWrapper do
label="Settings"
current_path={@current_path || ""}
disable_active={true}
+ submenu_open={
+ submenu_open?(@current_path, [
+ "/admin/settings",
+ "/admin/settings/users",
+ "/admin/settings/referral-codes",
+ "/admin/settings/emails",
+ "/admin/settings/languages",
+ "/admin/settings/entities",
+ "/admin/settings/media",
+ "/admin/settings/storage/dimensions",
+ "/admin/settings/maintenance",
+ "/admin/settings/blogging",
+ "/admin/settings/seo"
+ ])
+ }
/>
<%= if submenu_open?(@current_path, ["/admin/settings", "/admin/settings/users", "/admin/settings/referral-codes", "/admin/settings/emails", "/admin/settings/languages", "/admin/settings/entities", "/admin/settings/media", "/admin/settings/storage/dimensions", "/admin/settings/maintenance", "/admin/settings/blogging", "/admin/settings/seo"]) do %>
@@ -522,7 +557,7 @@ defmodule PhoenixKitWeb.Components.LayoutWrapper do
<%!-- Media section with submenu --%>
<.admin_nav_item
href={Routes.locale_aware_path(assigns, "/admin/settings/media")}
- icon="storage"
+ icon="photo"
label="Media"
current_path={@current_path || ""}
nested={true}
@@ -840,7 +875,10 @@ defmodule PhoenixKitWeb.Components.LayoutWrapper do
end
end
- defp looks_like_locale?(locale), do: String.length(locale) <= 3
+ defp looks_like_locale?(locale) do
+ # Match 2-letter codes (en, es) or regional variants (en-US, es-ES, zh-CN)
+ String.length(locale) <= 6 and String.match?(locale, ~r/^[a-z]{2}(-[A-Z]{2})?$/)
+ end
defp path_matches_any?(normalized_path, paths) do
Enum.any?(paths, fn path ->
diff --git a/lib/phoenix_kit_web/live/modules/languages.ex b/lib/phoenix_kit_web/live/modules/languages.ex
index c85e54e74..06804f4b6 100644
--- a/lib/phoenix_kit_web/live/modules/languages.ex
+++ b/lib/phoenix_kit_web/live/modules/languages.ex
@@ -39,6 +39,7 @@ defmodule PhoenixKitWeb.Live.Modules.Languages do
|> assign(:language_count, length(display_languages))
|> assign(:enabled_count, Enum.count(display_languages, & &1["is_enabled"]))
|> assign(:default_language, Enum.find(display_languages, & &1["is_default"]))
+ |> assign(:unknown_language_codes, get_unknown_language_codes(display_languages))
|> assign(
:available_languages_for_selection,
Languages.get_available_languages_for_selection()
@@ -285,6 +286,7 @@ defmodule PhoenixKitWeb.Live.Modules.Languages do
|> assign(:language_count, length(display_languages))
|> assign(:enabled_count, Enum.count(display_languages, & &1["is_enabled"]))
|> assign(:default_language, Enum.find(display_languages, & &1["is_default"]))
+ |> assign(:unknown_language_codes, get_unknown_language_codes(display_languages))
end
# Helper function to generate the language switcher code based on current settings
@@ -308,4 +310,13 @@ defmodule PhoenixKitWeb.Live.Modules.Languages do
nil -> "🌐"
end
end
+
+ # Helper function to identify unknown/deprecated language codes
+ defp get_unknown_language_codes(languages) do
+ languages
+ |> Enum.map(& &1["code"])
+ |> Enum.filter(fn code ->
+ Languages.get_predefined_language(code) == nil
+ end)
+ end
end
diff --git a/lib/phoenix_kit_web/live/modules/languages.html.heex b/lib/phoenix_kit_web/live/modules/languages.html.heex
index 43871bddd..6cf65ff25 100644
--- a/lib/phoenix_kit_web/live/modules/languages.html.heex
+++ b/lib/phoenix_kit_web/live/modules/languages.html.heex
@@ -111,6 +111,9 @@
<%= if language["is_default"] do %>
Default
<% end %>
+ <%= if language["code"] in @unknown_language_codes do %>
+ Unknown
+ <% end %>
<%!-- Move buttons only in configured mode --%>
@@ -170,8 +173,8 @@
<% end %>
- <%!-- Remove Button (only in configured mode, and not the default language) --%>
- <%= if @in_configured_mode and not language["is_default"] do %>
+ <%!-- Remove Button (show for unknown codes or in configured mode, but not for default language) --%>
+ <%= if (language["code"] in @unknown_language_codes and not language["is_default"]) or (@in_configured_mode and not language["is_default"]) do %>
+ <%!-- Unknown/Deprecated Languages Section (if any exist) --%>
+ <% unknown_selected_codes =
+ Enum.filter(@admin_languages, fn code ->
+ PhoenixKit.Modules.Languages.get_predefined_language(code) == nil
+ end) %>
+
+ <%= if not Enum.empty?(unknown_selected_codes) do %>
+
+ <%= for language_code <- unknown_selected_codes do %>
+
+
+
+
+
+
+
🌐
+
+ {language_code}
+
+ Unknown code - click to remove
+
+
+
+
+
+ <% end %>
+
+ <% end %>
+
<%!-- Popular Languages Section --%>
+
<%= for language <- Enum.filter(PhoenixKit.Modules.Languages.get_available_languages(), & &1.code in PhoenixKit.Modules.Languages.get_default_language_codes()) do %>
<% is_selected = language.code in @admin_languages %>
diff --git a/lib/phoenix_kit_web/live/settings/storage.ex b/lib/phoenix_kit_web/live/settings/storage.ex
index 24614c542..86726edda 100644
--- a/lib/phoenix_kit_web/live/settings/storage.ex
+++ b/lib/phoenix_kit_web/live/settings/storage.ex
@@ -16,13 +16,16 @@ defmodule PhoenixKitWeb.Live.Settings.Storage do
alias PhoenixKit.Utils.Routes
def mount(params, session, socket) do
- # Get current path for navigation
- current_path = get_current_path(socket, session)
+ # Set locale for LiveView process FIRST - preserve from params or socket assigns
+ locale = params["locale"] || socket.assigns[:current_locale]
+
+ if locale do
+ Gettext.put_locale(PhoenixKitWeb.Gettext, locale)
+ Process.put(:phoenix_kit_current_locale, locale)
+ end
- # Set locale for LiveView process
- locale = params["locale"] || socket.assigns[:current_locale] || "en"
- Gettext.put_locale(PhoenixKitWeb.Gettext, locale)
- Process.put(:phoenix_kit_current_locale, locale)
+ # Get current path for navigation (after locale is set)
+ current_path = get_current_path(socket, session)
# Get project title from settings
project_title = Settings.get_setting("project_title", "PhoenixKit")