- ✗ {gettext("Invalid examples")}: Blog,
- product_updates
- , -news,
-
- my blog
-
+ ✗ {gettext("Invalid examples")}: Blog, product_updates, -news,
+ my blog
<% slug_value = @form[:slug].value || "" %>
From 444344bbe7964e2e3ad7530beb999aa521eee977 Mon Sep 17 00:00:00 2001
From: Max Don
Date: Tue, 17 Mar 2026 17:44:52 +0200
Subject: [PATCH 04/15] Hide preview button when creating a new post
The preview button requires a saved post UUID to build the preview URL.
On the new post page the UUID is nil, making the button non-functional.
Co-Authored-By: Claude Opus 4.6 (1M context)
---
lib/modules/publishing/web/editor.html.heex | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/lib/modules/publishing/web/editor.html.heex b/lib/modules/publishing/web/editor.html.heex
index 3fbe0eaa4..41da52434 100644
--- a/lib/modules/publishing/web/editor.html.heex
+++ b/lib/modules/publishing/web/editor.html.heex
@@ -159,14 +159,16 @@
)}
@@ -104,12 +104,12 @@
<%= if page_num == @page do %>
{page_num}
<% else %>
-
{page_num}
-
+
<% end %>
<% end %>
diff --git a/lib/modules/publishing/web/templates/show.html.heex b/lib/modules/publishing/web/templates/show.html.heex
index 604c7bfea..4d91536e5 100644
--- a/lib/modules/publishing/web/templates/show.html.heex
+++ b/lib/modules/publishing/web/templates/show.html.heex
@@ -12,7 +12,7 @@
<%= for breadcrumb <- @breadcrumbs do %>
<%= if breadcrumb.url do %>
- {breadcrumb.label}
+ <.link navigate={breadcrumb.url}>{breadcrumb.label}
<% else %>
{breadcrumb.label}
<% end %>
@@ -77,15 +77,15 @@
>
<%= for v <- @version_dropdown.versions do %>
<%!-- Post Footer --%>
From ceaa22e10e0e1accee4bd84ebf6705f9cddf1c1d Mon Sep 17 00:00:00 2001
From: Max Don
Date: Tue, 17 Mar 2026 20:49:14 +0200
Subject: [PATCH 11/15] Show indeterminate state for translation progress until
data arrives
Display "Starting..." with an animated progress bar instead of "0/0"
when translation is enqueued but progress info hasn't been received yet.
Co-Authored-By: Claude Opus 4.6 (1M context)
---
lib/modules/publishing/web/editor.html.heex | 30 +++++++++++++--------
1 file changed, 19 insertions(+), 11 deletions(-)
diff --git a/lib/modules/publishing/web/editor.html.heex b/lib/modules/publishing/web/editor.html.heex
index d7006261c..fa8c51bd5 100644
--- a/lib/modules/publishing/web/editor.html.heex
+++ b/lib/modules/publishing/web/editor.html.heex
@@ -537,19 +537,27 @@
<% end %>
- {@ai_translation_progress || 0} / {@ai_translation_total || 0}
+ <%= if @ai_translation_total && @ai_translation_total > 0 do %>
+ {@ai_translation_progress || 0} / {@ai_translation_total}
+ <% else %>
+ {gettext("Starting...")}
+ <% end %>
-
+ <%= if @ai_translation_total && @ai_translation_total > 0 do %>
+
+ <% else %>
+
+ <% end %>
<% end %>
From 6af64c321a84546df145b1bf8d69d7e4a85287f5 Mon Sep 17 00:00:00 2001
From: Max Don
Date: Tue, 17 Mar 2026 21:08:44 +0200
Subject: [PATCH 12/15] Fix PubSub subscription mismatch for translation and
version events
The editor subscribed to translation/version topics using post.slug
only, but the worker broadcasts using slug || uuid. Timestamp-mode
posts (no slug) never received translation events because the topic
didn't match.
- Add PubSub.broadcast_id/1 as the single source of truth for post
identifier resolution (slug || uuid)
- Update collaborative subscriptions to use broadcast_id
- Update translation worker to use broadcast_id
- Fix translation_created handler to pass current_language
- Remove unused default on re_read_post (compiler enforces language arg)
- Add tests for broadcast_id and topic consistency
Co-Authored-By: Claude Opus 4.6 (1M context)
---
lib/modules/publishing/pubsub.ex | 15 ++++
lib/modules/publishing/web/editor.ex | 4 +-
.../publishing/web/editor/collaborative.ex | 12 ++--
.../workers/translate_post_worker.ex | 2 +-
.../publishing/pubsub_broadcast_id_test.exs | 71 +++++++++++++++++++
5 files changed, 95 insertions(+), 9 deletions(-)
create mode 100644 test/modules/publishing/pubsub_broadcast_id_test.exs
diff --git a/lib/modules/publishing/pubsub.ex b/lib/modules/publishing/pubsub.ex
index 493e6d6d3..0a3b626d6 100644
--- a/lib/modules/publishing/pubsub.ex
+++ b/lib/modules/publishing/pubsub.ex
@@ -18,6 +18,21 @@ defmodule PhoenixKit.Modules.Publishing.PubSub do
@topic_editor_forms "publishing:editor_forms"
@topic_groups "publishing:groups"
+ # ============================================================================
+ # Post Identifier Resolution
+ # ============================================================================
+
+ @doc """
+ Returns the broadcast identifier for a post.
+
+ Uses slug when available, falls back to uuid. This identifier is used
+ for PubSub topic construction and must be consistent between broadcasters
+ (e.g. translation worker) and subscribers (e.g. editor).
+ """
+ def broadcast_id(post) do
+ post[:slug] || post[:uuid]
+ end
+
# ============================================================================
# Group-Level Updates (group creation/deletion)
# ============================================================================
diff --git a/lib/modules/publishing/web/editor.ex b/lib/modules/publishing/web/editor.ex
index b958eae35..de7b29999 100644
--- a/lib/modules/publishing/web/editor.ex
+++ b/lib/modules/publishing/web/editor.ex
@@ -1320,7 +1320,7 @@ defmodule PhoenixKit.Modules.Publishing.Web.Editor do
def handle_info({:translation_created, group_slug, post_identifier, language}, socket) do
if socket.assigns[:group_slug] == group_slug && post_matches?(socket, post_identifier) do
- case re_read_post(socket) do
+ case re_read_post(socket, socket.assigns[:current_language]) do
{:ok, updated_post} ->
socket =
socket
@@ -1553,7 +1553,7 @@ defmodule PhoenixKit.Modules.Publishing.Web.Editor do
assign(socket, :autosave_timer, timer_ref)
end
- defp re_read_post(socket, language \\ nil) do
+ defp re_read_post(socket, language) do
case socket.assigns[:post] do
nil -> {:error, :no_post}
%{uuid: nil} -> {:error, :no_uuid}
diff --git a/lib/modules/publishing/web/editor/collaborative.ex b/lib/modules/publishing/web/editor/collaborative.ex
index 68a7e1e6c..1a44ff109 100644
--- a/lib/modules/publishing/web/editor/collaborative.ex
+++ b/lib/modules/publishing/web/editor/collaborative.ex
@@ -184,9 +184,9 @@ defmodule PhoenixKit.Modules.Publishing.Web.Editor.Collaborative do
end
defp subscribe_to_post_translations(socket) do
- case socket.assigns[:post] && socket.assigns.post[:slug] do
- post_slug when is_binary(post_slug) ->
- PublishingPubSub.subscribe_to_post_translations(socket.assigns.group_slug, post_slug)
+ case PublishingPubSub.broadcast_id(socket.assigns[:post]) do
+ id when is_binary(id) ->
+ PublishingPubSub.subscribe_to_post_translations(socket.assigns.group_slug, id)
_ ->
:ok
@@ -194,9 +194,9 @@ defmodule PhoenixKit.Modules.Publishing.Web.Editor.Collaborative do
end
defp subscribe_to_post_versions(socket) do
- case socket.assigns[:post] && socket.assigns.post[:slug] do
- post_slug when is_binary(post_slug) ->
- PublishingPubSub.subscribe_to_post_versions(socket.assigns.group_slug, post_slug)
+ case PublishingPubSub.broadcast_id(socket.assigns[:post]) do
+ id when is_binary(id) ->
+ PublishingPubSub.subscribe_to_post_versions(socket.assigns.group_slug, id)
_ ->
:ok
diff --git a/lib/modules/publishing/workers/translate_post_worker.ex b/lib/modules/publishing/workers/translate_post_worker.ex
index f56100c09..1f80a67cf 100644
--- a/lib/modules/publishing/workers/translate_post_worker.ex
+++ b/lib/modules/publishing/workers/translate_post_worker.ex
@@ -184,7 +184,7 @@ defmodule PhoenixKit.Modules.Publishing.Workers.TranslatePostWorker do
) do
group_slug = source_post.group
total = length(target_languages)
- broadcast_id = source_post.slug || source_post[:uuid]
+ broadcast_id = PublishingPubSub.broadcast_id(source_post)
# Broadcast that translation has started
PublishingPubSub.broadcast_translation_started(group_slug, broadcast_id, target_languages)
diff --git a/test/modules/publishing/pubsub_broadcast_id_test.exs b/test/modules/publishing/pubsub_broadcast_id_test.exs
new file mode 100644
index 000000000..b526747d4
--- /dev/null
+++ b/test/modules/publishing/pubsub_broadcast_id_test.exs
@@ -0,0 +1,71 @@
+defmodule PhoenixKit.Modules.Publishing.PubSubBroadcastIdTest do
+ use ExUnit.Case, async: true
+
+ alias PhoenixKit.Modules.Publishing.PubSub, as: PublishingPubSub
+
+ # ============================================================================
+ # broadcast_id/1
+ # ============================================================================
+
+ describe "broadcast_id/1" do
+ test "returns slug when present" do
+ post = %{slug: "my-post", uuid: "019cfcf7-8234-7ea5-b8fb-f6d5ae74ea18"}
+ assert PublishingPubSub.broadcast_id(post) == "my-post"
+ end
+
+ test "falls back to uuid when slug is nil" do
+ post = %{slug: nil, uuid: "019cfcf7-8234-7ea5-b8fb-f6d5ae74ea18"}
+ assert PublishingPubSub.broadcast_id(post) == "019cfcf7-8234-7ea5-b8fb-f6d5ae74ea18"
+ end
+
+ test "falls back to uuid when slug key is missing" do
+ post = %{uuid: "019cfcf7-8234-7ea5-b8fb-f6d5ae74ea18"}
+ assert PublishingPubSub.broadcast_id(post) == "019cfcf7-8234-7ea5-b8fb-f6d5ae74ea18"
+ end
+
+ test "returns nil when both slug and uuid are nil" do
+ post = %{slug: nil, uuid: nil}
+ assert PublishingPubSub.broadcast_id(post) == nil
+ end
+
+ test "returns nil for nil post" do
+ assert PublishingPubSub.broadcast_id(nil) == nil
+ end
+
+ test "prefers slug over uuid" do
+ post = %{slug: "hello-world", uuid: "019cfcf7-0000-0000-0000-000000000000"}
+ assert PublishingPubSub.broadcast_id(post) == "hello-world"
+ end
+ end
+
+ # ============================================================================
+ # Topic consistency
+ # ============================================================================
+
+ describe "topic consistency" do
+ test "subscription and broadcast use the same topic for slug-mode posts" do
+ post = %{slug: "my-post", uuid: "019cfcf7-8234-7ea5-b8fb-f6d5ae74ea18"}
+ broadcast_id = PublishingPubSub.broadcast_id(post)
+
+ # The subscription topic should match what the worker would broadcast to
+ topic = PublishingPubSub.post_translations_topic("blog", broadcast_id)
+ assert topic == "publishing:blog:post:my-post:translations"
+ end
+
+ test "subscription and broadcast use the same topic for timestamp-mode posts (no slug)" do
+ post = %{slug: nil, uuid: "019cfcf7-8234-7ea5-b8fb-f6d5ae74ea18"}
+ broadcast_id = PublishingPubSub.broadcast_id(post)
+
+ topic = PublishingPubSub.post_translations_topic("news", broadcast_id)
+ assert topic == "publishing:news:post:019cfcf7-8234-7ea5-b8fb-f6d5ae74ea18:translations"
+ end
+
+ test "version topic uses same broadcast_id pattern" do
+ post = %{slug: nil, uuid: "019cfcf7-8234-7ea5-b8fb-f6d5ae74ea18"}
+ broadcast_id = PublishingPubSub.broadcast_id(post)
+
+ topic = PublishingPubSub.post_versions_topic("news", broadcast_id)
+ assert topic == "publishing:news:post:019cfcf7-8234-7ea5-b8fb-f6d5ae74ea18:versions"
+ end
+ end
+end
From 09a4d6b94f53d2276e1fed1e3537016da07d13a3 Mon Sep 17 00:00:00 2001
From: Max Don
Date: Tue, 17 Mar 2026 21:29:49 +0200
Subject: [PATCH 13/15] Add Open Graph and Twitter Card meta tags for public
publishing pages
Render og:title, og:description, og:image, og:url, og:locale, og:type,
canonical link, and Twitter Card tags in the root layout when @og assign
is present. Build OG data from post metadata, description, and featured
image in the publishing controller for posts, versioned posts, date-only
URLs, and group listings.
Use conn.scheme/host/port for base URL instead of Endpoint.url() to
avoid compile-time dependency on the parent app endpoint.
Co-Authored-By: Claude Opus 4.6 (1M context)
---
lib/modules/publishing/web/controller.ex | 51 +++++++++++++++++++
.../publishing/web/templates/show.html.heex | 1 -
.../components/layouts/root.html.heex | 27 ++++++++++
3 files changed, 78 insertions(+), 1 deletion(-)
diff --git a/lib/modules/publishing/web/controller.ex b/lib/modules/publishing/web/controller.ex
index d33f3e6cf..66c865e71 100644
--- a/lib/modules/publishing/web/controller.ex
+++ b/lib/modules/publishing/web/controller.ex
@@ -31,6 +31,7 @@ defmodule PhoenixKit.Modules.Publishing.Web.Controller do
alias PhoenixKit.Modules.Publishing.Web.Controller.Listing
alias PhoenixKit.Modules.Publishing.Web.Controller.PostRendering
alias PhoenixKit.Modules.Publishing.Web.Controller.Routing
+ alias PhoenixKit.Modules.Publishing.Web.HTML, as: PublishingHTML
alias PhoenixKit.Settings
# ============================================================================
@@ -160,6 +161,11 @@ defmodule PhoenixKit.Modules.Publishing.Web.Controller do
defp handle_group_listing(conn, group_slug, language) do
case Listing.render_group_listing(conn, group_slug, language, conn.params) do
{:ok, assigns} ->
+ listing_url = PublishingHTML.group_listing_path(assigns.current_language, group_slug)
+
+ base_url =
+ "#{conn.scheme}://#{conn.host}#{if conn.port in [80, 443], do: "", else: ":#{conn.port}"}"
+
conn
|> assign(:page_title, assigns.page_title)
|> assign(:group, assigns.group)
@@ -171,6 +177,12 @@ defmodule PhoenixKit.Modules.Publishing.Web.Controller do
|> assign(:total_count, assigns.total_count)
|> assign(:total_pages, assigns.total_pages)
|> assign(:breadcrumbs, assigns.breadcrumbs)
+ |> assign(:og, %{
+ title: assigns.group["name"],
+ url: base_url <> listing_url,
+ locale: assigns.current_language,
+ type: "website"
+ })
|> render(:index)
{:redirect, url} ->
@@ -188,6 +200,9 @@ defmodule PhoenixKit.Modules.Publishing.Web.Controller do
defp handle_post(conn, group_slug, identifier, language) do
case PostRendering.render_post(conn, group_slug, identifier, language) do
{:ok, assigns} ->
+ canonical_url =
+ PublishingHTML.build_post_url(group_slug, assigns.post, assigns.current_language)
+
conn
|> assign(:page_title, assigns.page_title)
|> assign(:group_slug, assigns.group_slug)
@@ -197,6 +212,7 @@ defmodule PhoenixKit.Modules.Publishing.Web.Controller do
|> assign(:translations, assigns.translations)
|> assign(:breadcrumbs, assigns.breadcrumbs)
|> assign(:version_dropdown, assigns.version_dropdown)
+ |> assign(:og, build_og_data(conn, assigns.post, canonical_url, assigns.current_language))
|> render(:show)
{:redirect, url} ->
@@ -228,6 +244,10 @@ defmodule PhoenixKit.Modules.Publishing.Web.Controller do
|> assign(:is_live_version, assigns.is_live_version)
|> assign(:version, assigns.version)
|> assign(:version_dropdown, assigns.version_dropdown)
+ |> assign(
+ :og,
+ build_og_data(conn, assigns.post, assigns.canonical_url, assigns.current_language)
+ )
|> render(:show)
{:error, reason} ->
@@ -238,6 +258,9 @@ defmodule PhoenixKit.Modules.Publishing.Web.Controller do
defp handle_date_only_url(conn, group_slug, date, language) do
case PostRendering.handle_date_only_url(conn, group_slug, date, language) do
{:ok, assigns} ->
+ canonical_url =
+ PublishingHTML.build_post_url(group_slug, assigns.post, assigns.current_language)
+
conn
|> assign(:page_title, assigns.page_title)
|> assign(:group_slug, assigns.group_slug)
@@ -247,6 +270,7 @@ defmodule PhoenixKit.Modules.Publishing.Web.Controller do
|> assign(:translations, assigns.translations)
|> assign(:breadcrumbs, assigns.breadcrumbs)
|> assign(:version_dropdown, assigns.version_dropdown)
+ |> assign(:og, build_og_data(conn, assigns.post, canonical_url, assigns.current_language))
|> render(:show)
{:redirect, url} ->
@@ -292,4 +316,31 @@ defmodule PhoenixKit.Modules.Publishing.Web.Controller do
defp set_gettext_locale(language) do
Gettext.put_locale(PhoenixKitWeb.Gettext, language)
end
+
+ defp build_og_data(conn, post, canonical_url, language) do
+ seo = Map.get(post.metadata, :seo) || Map.get(post, :seo) || %{}
+ description = seo["og_description"] || Map.get(post.metadata, :description)
+ image = seo["og_image"] || PublishingHTML.featured_image_url(post, "large")
+
+ base_url =
+ "#{conn.scheme}://#{conn.host}#{if conn.port in [80, 443], do: "", else: ":#{conn.port}"}"
+
+ %{
+ title: seo["og_title"] || post.metadata.title,
+ description: description,
+ image: absolute_url(base_url, image),
+ url: absolute_url(base_url, canonical_url),
+ locale: language,
+ type: "article"
+ }
+ end
+
+ defp absolute_url(_base, nil), do: nil
+ defp absolute_url(_base, ""), do: nil
+
+ defp absolute_url(base, url) when is_binary(url) do
+ if String.starts_with?(url, "http"), do: url, else: base <> url
+ end
+
+ defp absolute_url(_base, _url), do: nil
end
diff --git a/lib/modules/publishing/web/templates/show.html.heex b/lib/modules/publishing/web/templates/show.html.heex
index 4d91536e5..35bbbe113 100644
--- a/lib/modules/publishing/web/templates/show.html.heex
+++ b/lib/modules/publishing/web/templates/show.html.heex
@@ -4,7 +4,6 @@
current_path={@conn.request_path}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
>
- <%!-- TODO: Add SEO meta tags (description, canonical) when layout system supports meta_tags slot --%>
<%!-- Breadcrumb Navigation --%>
diff --git a/lib/phoenix_kit_web/components/layouts/root.html.heex b/lib/phoenix_kit_web/components/layouts/root.html.heex
index 168dffe5f..10e3cc477 100644
--- a/lib/phoenix_kit_web/components/layouts/root.html.heex
+++ b/lib/phoenix_kit_web/components/layouts/root.html.heex
@@ -12,6 +12,33 @@
<% end %>
+ <%= if og = assigns[:og] do %>
+
+ <%= if og[:title] do %>
+
+
+ <% end %>
+ <%= if og[:description] do %>
+
+
+
+ <% end %>
+ <%= if og[:image] do %>
+
+
+ <% end %>
+ <%= if og[:url] do %>
+
+
+ <% end %>
+ <%= if og[:locale] do %>
+
+ <% end %>
+
+ <% end %>
<.live_title
default={PhoenixKit.Settings.get_project_title()}
suffix=" · Phoenix Framework"
From 0c461d086120eb2a6ce973edd934474199c54611 Mon Sep 17 00:00:00 2001
From: Max Don
Date: Tue, 17 Mar 2026 22:19:01 +0200
Subject: [PATCH 14/15] Fix language map lookup when canonical URL uses base
code
When only one dialect is enabled (e.g., "en-US"), canonical URLs use the base
code ("en") for cleaner paths. However, language maps (language_titles,
language_excerpts, language_slugs) store data using full dialect codes as keys.
This caused Map.get lookups to fail when the language param was a base code but
the map only had dialect code keys.
Added resolve_language_key/2 helper to try exact match first, then match by
base code when looking up language-specific data in listing and URL generation.
---
.../publishing/web/controller/listing.ex | 20 +++++++++++++++++--
lib/modules/publishing/web/html.ex | 18 ++++++++++++++++-
2 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/lib/modules/publishing/web/controller/listing.ex b/lib/modules/publishing/web/controller/listing.ex
index 57c1f2fee..13064bfc0 100644
--- a/lib/modules/publishing/web/controller/listing.ex
+++ b/lib/modules/publishing/web/controller/listing.ex
@@ -182,8 +182,13 @@ defmodule PhoenixKit.Modules.Publishing.Web.Controller.Listing do
lang_excerpts = post[:language_excerpts] || %{}
metadata = post[:metadata] || %{}
- title = Map.get(lang_titles, language, metadata[:title] || Constants.default_title())
- excerpt = Map.get(lang_excerpts, language)
+ # Language map keys are full dialect codes (e.g., "en-US") but the
+ # canonical_language may be a base code (e.g., "en") when only one
+ # dialect is enabled. Try exact match first, then match by base code.
+ resolved_key = resolve_language_key(language, Map.keys(lang_titles))
+
+ title = Map.get(lang_titles, resolved_key, metadata[:title] || Constants.default_title())
+ excerpt = Map.get(lang_excerpts, resolved_key)
post
|> Map.update(:metadata, %{title: title}, &Map.put(&1, :title, title))
@@ -192,6 +197,17 @@ defmodule PhoenixKit.Modules.Publishing.Web.Controller.Listing do
end)
end
+ # Resolves a display language code to a key in the language map.
+ # Handles base code ("en") → dialect code ("en-US") matching.
+ defp resolve_language_key(language, available_keys) do
+ if language in available_keys do
+ language
+ else
+ base = DialectMapper.extract_base(language)
+ Enum.find(available_keys, language, fn key -> DialectMapper.extract_base(key) == base end)
+ end
+ end
+
# ============================================================================
# Filtering Functions
# ============================================================================
diff --git a/lib/modules/publishing/web/html.ex b/lib/modules/publishing/web/html.ex
index 94e99b039..bc198221a 100644
--- a/lib/modules/publishing/web/html.ex
+++ b/lib/modules/publishing/web/html.ex
@@ -6,6 +6,7 @@ defmodule PhoenixKit.Modules.Publishing.Web.HTML do
alias PhoenixKit.Config
alias PhoenixKit.Modules.Languages
+ alias PhoenixKit.Modules.Languages.DialectMapper
alias PhoenixKit.Modules.Publishing
alias PhoenixKit.Modules.Publishing.Constants
alias PhoenixKit.Modules.Publishing.Renderer
@@ -115,7 +116,11 @@ defmodule PhoenixKit.Modules.Publishing.Web.HTML do
# language_slugs map from cache
map_size(Map.get(post, :language_slugs, %{})) > 0 ->
- Map.get(post.language_slugs, language, post.slug)
+ # Language map keys are full dialect codes (e.g., "en-US") but the
+ # language param may be a base code (e.g., "en") when only one
+ # dialect is enabled. Try exact match first, then match by base code.
+ resolved_key = resolve_language_key(language, Map.keys(post.language_slugs))
+ Map.get(post.language_slugs, resolved_key, post.slug)
# metadata.url_slug
is_map(Map.get(post, :metadata)) and Map.get(post.metadata, :url_slug) not in [nil, ""] ->
@@ -127,6 +132,17 @@ defmodule PhoenixKit.Modules.Publishing.Web.HTML do
end
end
+ # Resolves a display language code to a key in the language map.
+ # Handles base code ("en") → dialect code ("en-US") matching.
+ defp resolve_language_key(language, available_keys) do
+ if language in available_keys do
+ language
+ else
+ base = DialectMapper.extract_base(language)
+ Enum.find(available_keys, language, fn key -> DialectMapper.extract_base(key) == base end)
+ end
+ end
+
@doc """
Builds a public path with explicit date and time (always includes time).
Used when redirecting from date-only URLs to full timestamp URLs.
From 9a4af7533eee6a552f64408996153d34c4824538 Mon Sep 17 00:00:00 2001
From: Max Don
Date: Tue, 17 Mar 2026 22:22:32 +0200
Subject: [PATCH 15/15] Increase language switcher selection visibility on dark
themes
Changed bg-primary/10 to bg-primary/30 for selected language background,
making it more visible on dark themes where 10% opacity was nearly invisible.
---
lib/modules/publishing/web/components/language_switcher.ex | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/modules/publishing/web/components/language_switcher.ex b/lib/modules/publishing/web/components/language_switcher.ex
index de4d08188..728303f53 100644
--- a/lib/modules/publishing/web/components/language_switcher.ex
+++ b/lib/modules/publishing/web/components/language_switcher.ex
@@ -326,13 +326,13 @@ defmodule PhoenixKit.Modules.Publishing.Web.Components.LanguageSwitcher do
cond do
# Disabled or unknown languages: grey styling but still clickable
is_current and (!enabled or !known) ->
- "bg-base-content/10 text-base-content/50 font-semibold cursor-pointer"
+ "bg-base-content/30 text-base-content/50 font-semibold cursor-pointer"
(!enabled or !known) and exists ->
"text-base-content/40 hover:bg-base-200/50 cursor-pointer"
is_current ->
- "bg-primary/10 text-primary font-semibold"
+ "bg-primary/30 text-primary font-semibold"
!exists && show_add ->
"text-success hover:bg-success/10 cursor-pointer"