+ );
+}
diff --git a/apps/compliance-portal/src/components/MailingListUpdateListItem/MailingListUpdateListItem.tsx b/apps/compliance-portal/src/components/MailingListUpdateListItem/MailingListUpdateListItem.tsx
index 29fbd6800..238e03f2a 100644
--- a/apps/compliance-portal/src/components/MailingListUpdateListItem/MailingListUpdateListItem.tsx
+++ b/apps/compliance-portal/src/components/MailingListUpdateListItem/MailingListUpdateListItem.tsx
@@ -25,6 +25,7 @@ import { Link } from "react-router";
import { ComplianceArticleItem } from "#/components/ComplianceArticleItem/ComplianceArticleItem";
import { formatRelativeTime } from "#/lib/datetime/relativeTime";
+import { useLocalizedPath } from "#/lib/i18n/useLocale";
import type { MailingListUpdateListItem_update$key } from "./__generated__/MailingListUpdateListItem_update.graphql";
@@ -44,10 +45,11 @@ interface MailingListUpdateListItemProps {
// no category, so we show the title and relative date with a generic icon.
export function MailingListUpdateListItem({ updateKey }: MailingListUpdateListItemProps) {
const { i18n } = useTranslation();
+ const localizedPath = useLocalizedPath();
const update = useFragment(mailingListUpdateListItemFragment, updateKey);
return (
-
+
}
title={update.title}
diff --git a/apps/compliance-portal/src/components/RecentUpdates/RecentUpdatesSection.tsx b/apps/compliance-portal/src/components/RecentUpdates/RecentUpdatesSection.tsx
index 424d62324..ae6c359e3 100644
--- a/apps/compliance-portal/src/components/RecentUpdates/RecentUpdatesSection.tsx
+++ b/apps/compliance-portal/src/components/RecentUpdates/RecentUpdatesSection.tsx
@@ -27,6 +27,7 @@ import { InlineErrorCard } from "#/components/errors/InlineErrorCard";
import { HomeSection } from "#/components/HomeSection/HomeSection";
import { MailingListUpdateListItem } from "#/components/MailingListUpdateListItem/MailingListUpdateListItem";
import { dotPatternStyle } from "#/components/MediaTile/variants";
+import { useLocalizedPath } from "#/lib/i18n/useLocale";
import type { RecentUpdatesSection_compliancePortal$key } from "./__generated__/RecentUpdatesSection_compliancePortal.graphql";
@@ -71,6 +72,7 @@ export function RecentUpdatesSection({ compliancePortalKey }: RecentUpdatesSecti
function RecentUpdatesSectionContent({ compliancePortalKey }: RecentUpdatesSectionProps) {
const { t } = useTranslation();
+ const localizedPath = useLocalizedPath();
const data = useFragment(recentUpdatesSectionFragment, compliancePortalKey);
const updates = data.updates.edges.map(edge => edge.node);
@@ -82,7 +84,7 @@ function RecentUpdatesSectionContent({ compliancePortalKey }: RecentUpdatesSecti
+
{t("home.recentUpdates.viewAll")}
)}
diff --git a/apps/compliance-portal/src/components/TopBar/LocaleSelect.tsx b/apps/compliance-portal/src/components/TopBar/LocaleSelect.tsx
new file mode 100644
index 000000000..6095aa6ef
--- /dev/null
+++ b/apps/compliance-portal/src/components/TopBar/LocaleSelect.tsx
@@ -0,0 +1,81 @@
+// Copyright (c) 2026 Probo Inc .
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+import { GlobeIcon } from "@phosphor-icons/react";
+import { Select } from "@probo/ui/src/v2/Select/Select";
+import { SelectItem } from "@probo/ui/src/v2/Select/SelectItem";
+import { SelectPopup } from "@probo/ui/src/v2/Select/SelectPopup";
+import { SelectTrigger } from "@probo/ui/src/v2/Select/SelectTrigger";
+import { useTranslation } from "react-i18next";
+
+import {
+ URL_LOCALE_LABELS,
+ URL_LOCALES,
+ type UrlLocale,
+} from "#/lib/i18n/locale";
+import { useChangeLocale } from "#/lib/i18n/useChangeLocale";
+import { useLocale } from "#/lib/i18n/useLocale";
+
+interface LocaleSelectProps {
+ // Persist the choice on the signed-in identity when true.
+ persist?: boolean;
+}
+
+// Compact locale control for the top bar (guest and mobile). Uses the v2 Select
+// (Figma Select / ghost + globe) rather than a custom dropdown.
+export function LocaleSelect({ persist = false }: LocaleSelectProps) {
+ const { t } = useTranslation();
+ const locale = useLocale();
+ const [changeLocale, isChanging] = useChangeLocale();
+
+ return (
+
+ );
+}
diff --git a/apps/compliance-portal/src/components/TopBar/TopBar.tsx b/apps/compliance-portal/src/components/TopBar/TopBar.tsx
index 1ba7ad163..085bb6e27 100644
--- a/apps/compliance-portal/src/components/TopBar/TopBar.tsx
+++ b/apps/compliance-portal/src/components/TopBar/TopBar.tsx
@@ -28,8 +28,10 @@ import { graphql, useFragment } from "react-relay";
import { Link as RouterLink, useLocation } from "react-router";
import { buildRequestAllContinueUrl, redirectToInitiate } from "#/lib/auth/continueUrl";
+import { useLocalizedPath } from "#/lib/i18n/useLocale";
import type { TopBar_query$key } from "./__generated__/TopBar_query.graphql";
+import { LocaleSelect } from "./LocaleSelect";
import { TOP_BAR_NAV_ITEMS } from "./navItems";
import { TopBarMobileNav } from "./TopBarMobileNav";
import { TopBarUserMenu } from "./TopBarUserMenu";
@@ -61,17 +63,19 @@ export function TopBar({ queryKey }: TopBarProps) {
const data = useFragment(topBarFragment, queryKey);
const location = useLocation();
const { pathname } = location;
+ const localizedPath = useLocalizedPath();
const { currentCompliancePortal } = data;
const title = currentCompliancePortal.title;
const logoUrl = currentCompliancePortal.themedLogoUrl ?? undefined;
const slots = topBar();
+ const homePath = localizedPath("/");
return (
-
+
diff --git a/apps/compliance-portal/src/components/TopBar/TopBarMobileNav.tsx b/apps/compliance-portal/src/components/TopBar/TopBarMobileNav.tsx
index ab58721df..4c62dd350 100644
--- a/apps/compliance-portal/src/components/TopBar/TopBarMobileNav.tsx
+++ b/apps/compliance-portal/src/components/TopBar/TopBarMobileNav.tsx
@@ -45,9 +45,11 @@ import { useLocation } from "react-router";
import { buildRequestAllContinueUrl, redirectToInitiate } from "#/lib/auth/continueUrl";
import { useSignOut } from "#/lib/auth/useSignOut";
+import { useLocalizedPath } from "#/lib/i18n/useLocale";
import { useSubscribeDialog } from "#/lib/mailingList/subscribeDialogContext";
import type { TopBarMobileNav_identity$key } from "./__generated__/TopBarMobileNav_identity.graphql";
+import { LocaleSelect } from "./LocaleSelect";
import { TOP_BAR_NAV_ITEMS } from "./navItems";
import { topBar } from "./variants";
@@ -75,6 +77,7 @@ export function TopBarMobileNav({ identityKey }: TopBarMobileNavProps) {
const [signOut, isSigningOut] = useSignOut();
const [open, setOpen] = useState(false);
const identity = useFragment(topBarMobileNavFragment, identityKey);
+ const localizedPath = useLocalizedPath();
const barSlots = topBar();
@@ -119,24 +122,30 @@ export function TopBarMobileNav({ identityKey }: TopBarMobileNavProps) {
+