language-selector.tsx 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. "use client";
  2. import { useAction } from "next-safe-action/hooks";
  3. import { Languages } from "lucide-react";
  4. import { useChangeLocale, languages, useI18n } from "locales/client";
  5. import { updateUserAction } from "@/entities/user/model/update-user.action";
  6. const languageFlags: Record<string, string> = {
  7. en: "🇬🇧",
  8. fr: "🇫🇷",
  9. es: "🇪🇸",
  10. "zh-CN": "🇨🇳",
  11. ru: "🇷🇺",
  12. };
  13. export function LanguageSelector() {
  14. const action = useAction(updateUserAction);
  15. const changeLocale = useChangeLocale();
  16. const t = useI18n();
  17. const handleLanguageChange = async (newLocale: string) => {
  18. // update cookie to prevent auto-detection conflicts
  19. document.cookie = `detected-locale=${newLocale}; max-age=${60 * 60 * 24 * 365}; path=/; samesite=lax`;
  20. // change locale immediately for better UX
  21. changeLocale(newLocale as "en" | "fr" | "es" | "zh-CN" | "ru");
  22. // save to database (fire and forget)
  23. action.execute({ locale: newLocale });
  24. };
  25. const getLanguageName = (language: string) => {
  26. switch (language) {
  27. case "en":
  28. return "English";
  29. case "fr":
  30. return "Français";
  31. case "es":
  32. return "Español";
  33. case "zh-CN":
  34. return "中文";
  35. case "ru":
  36. return "Русский";
  37. default:
  38. return language;
  39. }
  40. };
  41. return (
  42. <div className="dropdown dropdown-end">
  43. <div className="tooltip tooltip-bottom" data-tip={t("commons.change_language")}>
  44. <div
  45. className="btn btn-ghost btn-circle h-8 w-8 p-0 text-xl flex items-center justify-center hover:bg-slate-200 border-none dark:hover:bg-gray-800 rounded-full"
  46. role="button"
  47. tabIndex={0}
  48. >
  49. <Languages className="w-5 h-5 text-blue-500 dark:text-blue-400" />
  50. </div>
  51. </div>
  52. <ul
  53. className="mt-3 z-[1] p-2 shadow menu menu-sm dropdown-content bg-base-100 dark:bg-black dark:text-gray-200 rounded-box border border-slate-200 dark:border-gray-800"
  54. tabIndex={0}
  55. >
  56. {languages.map((language) => (
  57. <li className="" key={language}>
  58. <button
  59. className="flex items-center gap-2 text-base hover:bg-slate-200 dark:hover:bg-gray-800 rounded-lg px-3 py-2 transition-colors whitespace-nowrap min-w-fit"
  60. onClick={() => handleLanguageChange(language)}
  61. >
  62. <span className="text-lg">{languageFlags[language]}</span>
  63. <span className="text-base whitespace-nowrap">{getLanguageName(language)}</span>
  64. </button>
  65. </li>
  66. ))}
  67. </ul>
  68. </div>
  69. );
  70. }