fix(i18n): working locale switcher + RTL category sidebar

- add src/i18n/navigation.ts (next-intl createNavigation) and rewire
  LanguageSwitcher to router.replace(pathname, { locale }) — the manual
  next/navigation path-munging didn't switch locale reliably
- VideoTemplatesCategorySidebar: text-left → text-start, ml-auto → ms-auto
  so labels/badges align correctly under dir="rtl"

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-02 09:59:07 +03:30
parent 3fc7bf2b97
commit 1aacf8bd5d
3 changed files with 16 additions and 22 deletions
+5 -20
View File
@@ -2,39 +2,24 @@
import { useTransition } from "react"; import { useTransition } from "react";
import { useLocale, useTranslations } from "next-intl"; import { useLocale, useTranslations } from "next-intl";
import { usePathname, useRouter } from "next/navigation";
import { Globe } from "lucide-react"; import { Globe } from "lucide-react";
import { routing, type Locale } from "@/i18n/routing"; import { usePathname, useRouter } from "@/i18n/navigation";
import { type Locale } from "@/i18n/routing";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
export function LanguageSwitcher({ className }: { className?: string }) { export function LanguageSwitcher({ className }: { className?: string }) {
const locale = useLocale() as Locale; const locale = useLocale() as Locale;
const t = useTranslations("langSwitcher"); const t = useTranslations("langSwitcher");
const router = useRouter(); const router = useRouter();
const pathname = usePathname(); const pathname = usePathname(); // locale-agnostic (no /en prefix)
const [isPending, startTransition] = useTransition(); const [isPending, startTransition] = useTransition();
const toggleLocale = () => { const toggleLocale = () => {
const nextLocale: Locale = locale === "fa" ? "en" : "fa"; const nextLocale: Locale = locale === "fa" ? "en" : "fa";
// Strip existing locale prefix from path, then prepend new one if needed
let newPath = pathname;
for (const loc of routing.locales) {
if (newPath.startsWith(`/${loc}/`)) {
newPath = newPath.slice(loc.length + 1); // remove /en
break;
} else if (newPath === `/${loc}`) {
newPath = "/";
break;
}
}
const prefix = nextLocale === routing.defaultLocale ? "" : `/${nextLocale}`;
const finalPath = prefix + (newPath.startsWith("/") ? newPath : `/${newPath}`);
startTransition(() => { startTransition(() => {
router.push(finalPath); // next-intl adds/removes the locale prefix per the routing config.
router.replace(pathname, { locale: nextLocale });
}); });
}; };
@@ -92,9 +92,9 @@ export function VideoTemplatesCategorySidebar({
)} )}
aria-hidden aria-hidden
/> />
<span className="min-w-0 flex-1 truncate text-left">{t(category.labelKey)}</span> <span className="min-w-0 flex-1 truncate text-start">{t(category.labelKey)}</span>
{category.count !== undefined ? ( {category.count !== undefined ? (
<span className="ml-auto rounded bg-gray-100 px-1.5 py-0.5 text-[11px] text-gray-500"> <span className="ms-auto rounded bg-gray-100 px-1.5 py-0.5 text-[11px] text-gray-500">
{category.count} {category.count}
</span> </span>
) : null} ) : null}
+9
View File
@@ -0,0 +1,9 @@
import { createNavigation } from "next-intl/navigation";
import { routing } from "./routing";
// Locale-aware navigation wrappers. `usePathname` returns the path WITHOUT the locale
// prefix, and `useRouter().replace(path, { locale })` switches locale correctly for the
// `as-needed` prefix strategy — which manual string-munging of next/navigation does not.
export const { Link, redirect, usePathname, useRouter, getPathname } =
createNavigation(routing);