feat: complete i18n support, telegram webhook, and security improvements

This commit is contained in:
AyrisAI
2026-05-14 13:46:17 +03:00
parent 4c9a07e3ef
commit cc65a2bd72
23 changed files with 798 additions and 205 deletions

View File

@@ -0,0 +1,101 @@
"use client";
import { usePathname, useRouter } from "next/navigation";
import { useState, useRef, useEffect } from "react";
export default function LanguageSwitcher({ currentLang }: { currentLang: string }) {
const pathname = usePathname();
const router = useRouter();
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const languages = [
{ code: "tr", label: "Türkçe", flag: "🇹🇷" },
{ code: "en", label: "English", flag: "🇺🇸" },
];
const handleLangChange = (newLang: string) => {
if (newLang === currentLang) {
setIsOpen(false);
return;
}
const segments = pathname.split("/");
segments[1] = newLang;
const newPath = segments.join("/");
router.push(newPath);
setIsOpen(false);
};
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
const current = languages.find((l) => l.code === currentLang) || languages[0];
return (
<div className="lang-switcher" ref={dropdownRef} style={{ position: "relative", marginBottom: "12px" }}>
<button
className="btn btn-ghost"
onClick={() => setIsOpen(!isOpen)}
style={{ width: "100%", justifyContent: "space-between", padding: "8px 12px", border: "1px solid var(--border-color)" }}
>
<span style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<span>{current.flag}</span>
<span style={{ fontSize: "13px" }}>{current.label}</span>
</span>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ transform: isOpen ? "rotate(180deg)" : "none", transition: "transform 0.2s" }}>
<polyline points="6 9 12 15 18 9" />
</svg>
</button>
{isOpen && (
<div style={{
position: "absolute",
bottom: "100%",
left: 0,
right: 0,
marginBottom: "4px",
background: "var(--card-bg)",
border: "1px solid var(--border-color)",
borderRadius: "8px",
boxShadow: "0 4px 12px rgba(0,0,0,0.2)",
zIndex: 100,
overflow: "hidden"
}}>
{languages.map((lang) => (
<button
key={lang.code}
className={`lang-option ${lang.code === currentLang ? "active" : ""}`}
onClick={() => handleLangChange(lang.code)}
style={{
width: "100%",
padding: "10px 12px",
display: "flex",
alignItems: "center",
gap: "10px",
background: lang.code === currentLang ? "var(--bg-secondary)" : "transparent",
border: "none",
color: "var(--text-primary)",
cursor: "pointer",
textAlign: "left",
fontSize: "13px",
transition: "background 0.2s"
}}
>
<span>{lang.flag}</span>
<span>{lang.label}</span>
</button>
))}
</div>
)}
</div>
);
}