Files
HokmPlay/src/lib/i18n.tsx
T
soroush.asadi 5776036d78 Add friend chat; replace betting wording (شرط) with entry coins
- Friend-to-friend chat outside the game (ChatScreen) with mock replies,
  per-friend history, unread tracking; chat button on each friend row
- OnlineService + mock + online-store extended with chat (list/get/send/markRead)
- Reframe gambling term: "شرط"/"Stake" -> "سکه ورودی"/"Entry coins";
  free entry labeled رایگان/Free

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 10:21:44 +03:30

450 lines
14 KiB
TypeScript

"use client";
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from "react";
export type Locale = "fa" | "en";
type Dict = Record<string, string>;
const fa: Dict = {
"app.title": "حکم",
"app.subtitle": "بازی کارت اصیل ایرانی",
"app.tagline": "تجربه‌ای لوکس از بازی حکم، با حریف‌های هوشمند",
"home.play": "شروع بازی",
"home.continue": "ادامه بازی",
"home.vsAI": "بازی با کامپیوتر",
"home.target": "امتیاز برد",
"home.targetHint": "تعداد دست برای برنده شدن",
"home.yourName": "نام شما",
"home.start": "بزن بریم",
"home.howTo": "آموزش بازی",
"home.lang": "English",
"seat.you": "شما",
"team.us": "ما",
"team.them": "حریف",
"team.0": "تیم ما",
"team.1": "تیم حریف",
"hakem.title": "تعیین حاکم",
"hakem.desc": "ورق می‌چینیم تا اولین آس بیاید",
"hakem.is": "حاکم: {name}",
"trump.title": "حکم را انتخاب کنید",
"trump.desc": "شما حاکم هستید — خال حکم را تعیین کنید",
"trump.waiting": "{name} در حال انتخاب حکم است…",
"trump.label": "حکم",
"turn.you": "نوبت شماست",
"turn.other": "نوبت {name}",
"trick.wins": "{name} دست را برد",
"round.over": "پایان دست",
"round.kot": "کُت! ",
"round.won": "{team} برنده شد",
"round.score": "امتیاز: {us} - {them}",
"round.next": "دست بعد…",
"match.over": "پایان بازی",
"match.youWin": "شما بردید! 🏆",
"match.youLose": "این بار باختید",
"match.again": "بازی دوباره",
"match.menu": "منوی اصلی",
"score.title": "امتیاز",
"score.tricks": "دست‌ها",
"hud.menu": "منو",
"hud.quit": "خروج",
"menu.vsComputer": "بازی با کامپیوتر",
"menu.vsComputerDesc": "تمرین با حریف‌های هوشمند",
"menu.online": "بازی آنلاین",
"menu.onlineDesc": "با دوستان یا بازیکن‌های واقعی",
"menu.profile": "پروفایل",
"menu.friends": "دوستان",
"menu.leaderboard": "جدول امتیازات",
"menu.shop": "فروشگاه",
"menu.signIn": "ورود / ثبت‌نام",
"menu.guest": "مهمان",
"menu.signOut": "خروج از حساب",
"common.back": "بازگشت",
"common.coins": "سکه",
"common.level": "سطح",
"common.rating": "امتیاز",
"common.save": "ذخیره",
"common.cancel": "انصراف",
"common.confirm": "تأیید",
"common.soon": "به‌زودی",
"common.copy": "کپی",
"common.copied": "کپی شد",
"common.free": "رایگان",
"chat.title": "گفتگو",
"chat.placeholder": "پیام بنویسید…",
"chat.send": "ارسال",
"chat.empty": "گفتگو را شروع کنید",
"friends.message": "پیام",
"profile.title": "پروفایل",
"profile.stats": "آمار",
"profile.games": "بازی‌ها",
"profile.wins": "بردها",
"profile.winrate": "درصد برد",
"profile.kots": "کُت‌ها",
"profile.streak": "بهترین نوار",
"profile.achievements": "دستاوردها",
"profile.editName": "ویرایش نام",
"profile.chooseAvatar": "انتخاب آواتار",
"friends.title": "دوستان",
"friends.add": "افزودن",
"friends.addPlaceholder": "نام کاربری یا شماره",
"friends.requests": "درخواست‌ها",
"friends.online": "آنلاین",
"friends.offline": "آفلاین",
"friends.inGame": "در حال بازی",
"friends.invite": "دعوت",
"friends.accept": "قبول",
"friends.decline": "رد",
"friends.remove": "حذف",
"friends.empty": "هنوز دوستی ندارید",
"lobby.title": "بازی آنلاین",
"lobby.createRoom": "ساخت اتاق خصوصی",
"lobby.createDesc": "هم‌تیمی و حریف‌ها را خودتان انتخاب کنید",
"lobby.random": "بازی رتبه‌ای",
"lobby.randomDesc": "حریف تصادفی و کسب امتیاز و سکه",
"room.title": "اتاق بازی",
"room.code": "کد اتاق",
"room.partner": "هم‌تیمی",
"room.opponents": "حریف‌ها",
"room.choosePartner": "انتخاب هم‌تیمی",
"room.invite": "دعوت دوست",
"room.addBot": "ربات",
"room.empty": "خالی",
"room.waiting": "در انتظار…",
"room.start": "شروع بازی",
"room.stake": "سکه ورودی",
"room.leave": "ترک اتاق",
"room.pickFriend": "یک دوست را انتخاب کنید",
"mm.title": "جستجوی بازیکن",
"mm.searching": "در حال یافتن حریف…",
"mm.found": "بازیکنان پیدا شدند!",
"mm.ready": "آماده شروع",
"mm.cancel": "لغو",
"mm.start": "ورود به بازی",
"lead.title": "جدول امتیازات",
"lead.rank": "رتبه",
"shop.title": "فروشگاه",
"shop.buy": "خرید",
"shop.owned": "موجود",
"shop.avatars": "آواتارها",
"shop.themes": "تم‌ها",
"shop.notEnough": "سکه کافی نیست",
"auth.title": "ورود به حکم",
"auth.subtitle": "برای بازی آنلاین وارد شوید",
"auth.phone": "موبایل",
"auth.email": "ایمیل",
"auth.phoneLabel": "شماره موبایل",
"auth.phonePlaceholder": "۰۹۱۲۳۴۵۶۷۸۹",
"auth.sendCode": "ارسال کد",
"auth.codeLabel": "کد تأیید",
"auth.codePlaceholder": "کد ۴ رقمی",
"auth.verify": "تأیید و ورود",
"auth.devCode": "کد آزمایشی: {code}",
"auth.emailLabel": "ایمیل",
"auth.passLabel": "رمز عبور",
"auth.nameLabel": "نام نمایشی",
"auth.signIn": "ورود",
"auth.signUp": "ثبت‌نام",
"auth.google": "ورود با گوگل",
"auth.toggleSignup": "حساب ندارید؟ ثبت‌نام کنید",
"auth.toggleSignin": "حساب دارید؟ وارد شوید",
"auth.invalidCode": "کد نادرست است",
"reward.title": "پاداش بازی",
"reward.rating": "امتیاز رتبه‌ای",
"reward.coins": "سکه",
"reward.xp": "تجربه",
"reward.levelUp": "ارتقای سطح!",
"reward.promoted": "ارتقای لیگ!",
"reward.demoted": "سقوط لیگ",
"reward.newAchievement": "دستاورد جدید",
"reward.continue": "ادامه",
"reward.win": "بردید! 🏆",
"reward.lose": "باختید",
"daily.title": "پاداش روزانه",
"daily.day": "روز {n}",
"daily.claim": "دریافت",
"daily.claimed": "دریافت شد",
"daily.come": "فردا برگردید",
"rank.label": "لیگ",
};
const en: Dict = {
"app.title": "Hokm",
"app.subtitle": "The classic Persian card game",
"app.tagline": "A luxury Hokm experience with smart opponents",
"home.play": "Play",
"home.continue": "Continue",
"home.vsAI": "Play vs Computer",
"home.target": "Target score",
"home.targetHint": "Rounds needed to win",
"home.yourName": "Your name",
"home.start": "Let's go",
"home.howTo": "How to play",
"home.lang": "فارسی",
"seat.you": "You",
"team.us": "Us",
"team.them": "Them",
"team.0": "Our team",
"team.1": "Their team",
"hakem.title": "Choosing the Hakem",
"hakem.desc": "Dealing face-up until the first Ace",
"hakem.is": "Hakem: {name}",
"trump.title": "Choose the trump",
"trump.desc": "You are the Hakem — pick the trump suit",
"trump.waiting": "{name} is choosing trump…",
"trump.label": "Trump",
"turn.you": "Your turn",
"turn.other": "{name}'s turn",
"trick.wins": "{name} wins the trick",
"round.over": "Round over",
"round.kot": "Kot! ",
"round.won": "{team} wins",
"round.score": "Score: {us} - {them}",
"round.next": "Next round…",
"match.over": "Game over",
"match.youWin": "You win! 🏆",
"match.youLose": "You lost this time",
"match.again": "Play again",
"match.menu": "Main menu",
"score.title": "Score",
"score.tricks": "Tricks",
"hud.menu": "Menu",
"hud.quit": "Quit",
"menu.vsComputer": "Play vs Computer",
"menu.vsComputerDesc": "Practice against smart bots",
"menu.online": "Play Online",
"menu.onlineDesc": "With friends or real players",
"menu.profile": "Profile",
"menu.friends": "Friends",
"menu.leaderboard": "Leaderboard",
"menu.shop": "Shop",
"menu.signIn": "Sign in / Sign up",
"menu.guest": "Guest",
"menu.signOut": "Sign out",
"common.back": "Back",
"common.coins": "Coins",
"common.level": "Level",
"common.rating": "Rating",
"common.save": "Save",
"common.cancel": "Cancel",
"common.confirm": "Confirm",
"common.soon": "Coming soon",
"common.copy": "Copy",
"common.copied": "Copied",
"common.free": "Free",
"chat.title": "Chat",
"chat.placeholder": "Type a message…",
"chat.send": "Send",
"chat.empty": "Start the conversation",
"friends.message": "Message",
"profile.title": "Profile",
"profile.stats": "Stats",
"profile.games": "Games",
"profile.wins": "Wins",
"profile.winrate": "Win rate",
"profile.kots": "Kots",
"profile.streak": "Best streak",
"profile.achievements": "Achievements",
"profile.editName": "Edit name",
"profile.chooseAvatar": "Choose avatar",
"friends.title": "Friends",
"friends.add": "Add",
"friends.addPlaceholder": "Username or phone",
"friends.requests": "Requests",
"friends.online": "Online",
"friends.offline": "Offline",
"friends.inGame": "In game",
"friends.invite": "Invite",
"friends.accept": "Accept",
"friends.decline": "Decline",
"friends.remove": "Remove",
"friends.empty": "No friends yet",
"lobby.title": "Play Online",
"lobby.createRoom": "Create private room",
"lobby.createDesc": "Choose your partner and opponents",
"lobby.random": "Ranked match",
"lobby.randomDesc": "Random opponents, earn rating & coins",
"room.title": "Game Room",
"room.code": "Room code",
"room.partner": "Partner",
"room.opponents": "Opponents",
"room.choosePartner": "Choose partner",
"room.invite": "Invite friend",
"room.addBot": "Bot",
"room.empty": "Empty",
"room.waiting": "Waiting…",
"room.start": "Start game",
"room.stake": "Entry coins",
"room.leave": "Leave room",
"room.pickFriend": "Pick a friend",
"mm.title": "Finding players",
"mm.searching": "Searching for opponents…",
"mm.found": "Players found!",
"mm.ready": "Ready to start",
"mm.cancel": "Cancel",
"mm.start": "Enter game",
"lead.title": "Leaderboard",
"lead.rank": "Rank",
"shop.title": "Shop",
"shop.buy": "Buy",
"shop.owned": "Owned",
"shop.avatars": "Avatars",
"shop.themes": "Themes",
"shop.notEnough": "Not enough coins",
"auth.title": "Sign in to Hokm",
"auth.subtitle": "Sign in to play online",
"auth.phone": "Phone",
"auth.email": "Email",
"auth.phoneLabel": "Mobile number",
"auth.phonePlaceholder": "0912 345 6789",
"auth.sendCode": "Send code",
"auth.codeLabel": "Verification code",
"auth.codePlaceholder": "4-digit code",
"auth.verify": "Verify & sign in",
"auth.devCode": "Dev code: {code}",
"auth.emailLabel": "Email",
"auth.passLabel": "Password",
"auth.nameLabel": "Display name",
"auth.signIn": "Sign in",
"auth.signUp": "Sign up",
"auth.google": "Continue with Google",
"auth.toggleSignup": "No account? Sign up",
"auth.toggleSignin": "Have an account? Sign in",
"auth.invalidCode": "Invalid code",
"reward.title": "Match rewards",
"reward.rating": "Rating",
"reward.coins": "Coins",
"reward.xp": "XP",
"reward.levelUp": "Level up!",
"reward.promoted": "Promoted!",
"reward.demoted": "Demoted",
"reward.newAchievement": "New achievement",
"reward.continue": "Continue",
"reward.win": "You won! 🏆",
"reward.lose": "You lost",
"daily.title": "Daily reward",
"daily.day": "Day {n}",
"daily.claim": "Claim",
"daily.claimed": "Claimed",
"daily.come": "Come back tomorrow",
"rank.label": "League",
};
const DICTS: Record<Locale, Dict> = { fa, en };
interface I18nValue {
locale: Locale;
dir: "rtl" | "ltr";
t: (key: string, vars?: Record<string, string | number>) => string;
setLocale: (l: Locale) => void;
toggle: () => void;
}
const I18nContext = createContext<I18nValue | null>(null);
export function I18nProvider({ children }: { children: React.ReactNode }) {
const [locale, setLocaleState] = useState<Locale>("fa");
useEffect(() => {
const saved = localStorage.getItem("hokm.locale") as Locale | null;
if (saved === "fa" || saved === "en") setLocaleState(saved);
}, []);
const setLocale = useCallback((l: Locale) => {
setLocaleState(l);
localStorage.setItem("hokm.locale", l);
}, []);
const dir: "rtl" | "ltr" = locale === "fa" ? "rtl" : "ltr";
useEffect(() => {
document.documentElement.lang = locale;
document.documentElement.dir = dir;
}, [locale, dir]);
const t = useCallback(
(key: string, vars?: Record<string, string | number>) => {
let str = DICTS[locale][key] ?? DICTS.en[key] ?? key;
if (vars) {
for (const [k, v] of Object.entries(vars)) {
str = str.replace(new RegExp(`\\{${k}\\}`, "g"), String(v));
}
}
return str;
},
[locale]
);
const value = useMemo<I18nValue>(
() => ({
locale,
dir,
t,
setLocale,
toggle: () => setLocale(locale === "fa" ? "en" : "fa"),
}),
[locale, dir, t, setLocale]
);
return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>;
}
export function useI18n(): I18nValue {
const ctx = useContext(I18nContext);
if (!ctx) throw new Error("useI18n must be used within I18nProvider");
return ctx;
}