"use client"; import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from "react"; export type Locale = "fa" | "en"; type Dict = Record; 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", "home.onlineCount": "{n} نفر آنلاین", "resume.title": "بازی در جریان", "resume.cta": "بازگشت به بازی", "resume.matchEnded": "بازی به پایان رسید", "resume.matchEndedBody": "نتیجه و جایزه را ببینید", "celebrate.purchased": "خرید با موفقیت انجام شد!", "achv.title": "دستاوردها", "achv.unlocked": "باز شده", "achv.coinsEarned": "سکه کسب‌شده", "achv.viewAll": "همه", "achv.unlocksSticker": "استیکر", "lobby.chooseLeague": "لیگ را انتخاب کنید", "lobby.lvl": "سطح", "profile.uploadLocked": "آپلود عکس از سطح ۲۵ فعال می‌شود", "forfeit.title": "تسلیم", "forfeit.ask": "از این بازی تسلیم می‌شوید؟", "forfeit.teammateAsks": "{name} می‌خواهد تسلیم شود. موافقید؟", "forfeit.rule": "با تسلیم، دو برابر سکهٔ ورودی را از دست می‌دهید و هیچ امتیاز تجربه‌ای نمی‌گیرید.", "forfeit.confirm": "تسلیم", "forfeit.keepPlaying": "ادامه می‌دهم", "match.players": "بازیکنان", "match.you": "شما", "match.bot": "ربات", "match.addFriend": "افزودن", "match.sent": "ارسال شد", "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}", "keys.title": "میان‌برهای صفحه‌کلید", "keys.play": "بازی کردن کارت", "keys.first": "اولین کارت مجاز", "keys.trump": "انتخاب حکم", "keys.mute": "قطع صدا", "keys.forfeit": "تسلیم", "keys.quit": "خروج", "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": "تمرین با حریف‌های هوشمند", "speed.label": "سریع", "speed.normal": "عادی", "speed.desc": "حالت سریع: نوبت‌های کوتاه‌تر و بازی برق‌آسا", "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": "رایگان", "common.yes": "بله", "common.no": "خیر", "buy.title": "خرید سکه", "buy.note": "پرداخت امن — درگاه پرداخت ایرانی به‌زودی اضافه می‌شود", "buy.toman": "تومان", "buy.bonus": "هدیه", "buy.redirecting": "صفحهٔ پرداخت در تب جدید باز شد. پس از پرداخت، سکه‌ها به‌صورت خودکار اضافه می‌شوند.", "buy.failed": "پرداخت در دسترس نیست. بعداً دوباره تلاش کنید.", "buy.popular": "محبوب", "buy.best": "بهترین", "buy.starter": "شروع", "lobby.entry": "ورودی", "lobby.free": "رایگان", "lobby.needCoins": "سکه کافی نیست — شارژ کنید", "friends.removeQ": "این دوست حذف شود؟", "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.sendRequest": "افزودن دوست", "profile.requestSent": "درخواست ارسال شد", "profile.alreadyFriend": "دوست شماست", "profile.memberSince": "عضو از", "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": "هنوز دوستی ندارید", "social.title": "اجتماعی", "social.tabFriends": "دوستان", "social.tabDiscover": "یافتن", "social.tabMessages": "پیام‌ها", "discover.searchPlaceholder": "جستجوی بازیکن با نام…", "discover.results": "نتایج جستجو", "discover.suggested": "بازیکنان پیشنهادی", "discover.noResults": "بازیکنی پیدا نشد", "discover.friend": "دوست", "messages.empty": "هنوز گفتگویی ندارید", "messages.you": "شما", "time.now": "همین حالا", "time.min": "{n} دقیقه پیش", "time.hour": "{n} ساعت پیش", "time.day": "{n} روز پیش", "common.retry": "تلاش دوباره", "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.fillHint": "اگر بازیکن آنلاینی پیدا نشود، ربات‌ها جایگزین می‌شوند", "intro.found": "بازیکنان آماده‌اند!", "intro.getReady": "بازی در حال شروع است…", "intro.go": "شروع!", "mm.cancel": "لغو", "mm.start": "ورود به بازی", "lead.title": "جدول امتیازات", "lead.rank": "رتبه", "shop.title": "فروشگاه", "shop.buy": "خرید", "shop.owned": "موجود", "shop.luxury": "ویژه", "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": "کد نادرست است", "auth.otherSoon": "سایر روش‌های ورود به‌زودی فعال می‌شوند", "reward.title": "پاداش بازی", "reward.rating": "امتیاز رتبه‌ای", "reward.coins": "سکه", "reward.xp": "تجربه", "reward.levelUp": "ارتقای سطح!", "reward.promoted": "ارتقای لیگ!", "reward.demoted": "سقوط لیگ", "reward.newAchievement": "دستاورد جدید", "reward.stickerUnlocked": "بستهٔ استیکر باز شد", "reward.continue": "ادامه", "reward.win": "بردید! 🏆", "reward.lose": "باختید", "daily.title": "پاداش روزانه", "daily.day": "روز {n}", "daily.claim": "دریافت", "daily.claimed": "دریافت شد", "daily.come": "فردا برگردید", "daily.special": "پاداش ویژه", "rank.label": "لیگ", "dc.waiting": "{name} قطع شد — منتظر بازگشت ({s})", "profile.titleLabel": "عنوان", "profile.cardStyleLabel": "طرح کارت", "profile.image": "تصویر پروفایل", "profile.upload": "آپلود تصویر", "profile.plan": "اشتراک", "plan.pro": "ویژه", "plan.free": "رایگان", "plan.upgrade": "ارتقا به ویژه", "plan.proDesc": "بازی بدون صف، در هر زمان", "plan.active": "اشتراک ویژه فعال است", "queue.title": "در صف بازی", "queue.busy": "سرور شلوغ است", "queue.position": "نفر {n} در صف", "queue.skip": "با اشتراک ویژه بدون صف وارد شوید", "queue.upgrade": "ورود سریع (ویژه)", "shop.cardstyles": "طرح کارت‌ها", "shop.reactions": "بسته شکلک‌ها", "shop.stickers": "بسته استیکرها", "shop.titles": "عناوین", "shop.titlesHint": "عنوان شما زیر نامتان در بازی و لیست‌ها نمایش داده می‌شود", "shop.xp": "امتیاز تجربه (XP)", "shop.xpHint": "افزایش سریع سطح — XP گران است", "shop.includes": "شامل", "shop.reqLevel": "سطح", "shop.reqRating": "امتیاز", "reward.newTitle": "عنوان جدید", "reactions.title": "شکلک", "stickers.title": "استیکر", "notif.title": "اعلان‌ها", "notif.empty": "اعلانی ندارید", "settings.audio": "تنظیمات صدا", "settings.sound": "افکت صدا", "settings.music": "موسیقی پس‌زمینه", "settings.musicStyle": "سبک موسیقی", "settings.trackSantoor": "سنتی (سنتور)", "settings.trackPlayful": "شاد", "profile.cardFront": "روی کارت", "profile.cardBack": "پشت کارت", "profile.social": "اجتماعی و ارتباط", "profile.gender": "جنسیت", "profile.genderNone": "نامشخص", "profile.socialLinks": "شبکه‌های اجتماعی", "profile.socialsVisibility": "نمایش شبکه‌ها به", "profile.visPublic": "همه", "profile.visFriends": "دوستان", "profile.visHidden": "هیچ‌کس", "profile.socialsHint": "می‌توانید نمایش پیج‌هایتان را عمومی، فقط برای دوستان یا غیرفعال کنید.", "profile.saveLinks": "ذخیره شبکه‌ها", "profile.saved": "ذخیره شد", "shop.cardfronts": "روی کارت‌ها", "shop.cardbacks": "پشت کارت‌ها", }; const en: Dict = { "app.title": "Barg-e Vasat", "app.subtitle": "Online Hokm", "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": "فارسی", "home.onlineCount": "{n} players online", "resume.title": "Game in progress", "resume.cta": "Return to game", "resume.matchEnded": "Match ended", "resume.matchEndedBody": "See the result and reward", "celebrate.purchased": "Purchase complete!", "achv.title": "Achievements", "achv.unlocked": "Unlocked", "achv.coinsEarned": "Coins earned", "achv.viewAll": "All", "achv.unlocksSticker": "Sticker", "lobby.chooseLeague": "Choose a league", "lobby.lvl": "Lvl", "profile.uploadLocked": "Photo upload unlocks at level 25", "forfeit.title": "Forfeit", "forfeit.ask": "Surrender this match?", "forfeit.teammateAsks": "{name} wants to forfeit. Agree?", "forfeit.rule": "Forfeiting costs double your entry coins and earns no XP.", "forfeit.confirm": "Forfeit", "forfeit.keepPlaying": "Keep playing", "match.players": "Players", "match.you": "You", "match.bot": "Bot", "match.addFriend": "Add", "match.sent": "Sent", "intro.found": "Players ready!", "intro.getReady": "The game is starting…", "intro.go": "GO!", "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", "keys.title": "Keyboard shortcuts", "keys.play": "Play a card", "keys.first": "First legal card", "keys.trump": "Choose trump", "keys.mute": "Mute", "keys.forfeit": "Forfeit", "keys.quit": "Quit", "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", "speed.label": "Speed", "speed.normal": "Normal", "speed.desc": "Blitz mode: short turns, lightning-fast match", "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", "common.yes": "Yes", "common.no": "No", "buy.title": "Buy Coins", "buy.note": "Secure payment — Iranian gateway coming soon", "buy.toman": "Toman", "buy.bonus": "bonus", "buy.redirecting": "Payment opened in a new tab. Your coins will be added automatically once you pay.", "buy.failed": "Payment unavailable. Please try again later.", "buy.popular": "Popular", "buy.best": "Best value", "buy.starter": "Starter", "lobby.entry": "Entry", "lobby.free": "Free", "lobby.needCoins": "Not enough coins — top up", "friends.removeQ": "Remove this friend?", "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.sendRequest": "Add friend", "profile.requestSent": "Request sent", "profile.alreadyFriend": "Your friend", "profile.memberSince": "Member since", "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", "social.title": "Social", "social.tabFriends": "Friends", "social.tabDiscover": "Discover", "social.tabMessages": "Messages", "discover.searchPlaceholder": "Search players by name…", "discover.results": "Search results", "discover.suggested": "Suggested players", "discover.noResults": "No players found", "discover.friend": "Friend", "messages.empty": "No conversations yet", "messages.you": "You", "time.now": "now", "time.min": "{n}m ago", "time.hour": "{n}h ago", "time.day": "{n}d ago", "common.retry": "Retry", "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.fillHint": "If no online players are found, bots will fill in", "mm.cancel": "Cancel", "mm.start": "Enter game", "lead.title": "Leaderboard", "lead.rank": "Rank", "shop.title": "Shop", "shop.buy": "Buy", "shop.owned": "Owned", "shop.luxury": "Luxury", "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", "auth.otherSoon": "Other sign-in methods coming soon", "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.stickerUnlocked": "Sticker pack unlocked", "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", "daily.special": "Special Reward", "rank.label": "League", "dc.waiting": "{name} disconnected — waiting ({s})", "profile.titleLabel": "Title", "profile.cardStyleLabel": "Card style", "profile.image": "Profile image", "profile.upload": "Upload image", "profile.plan": "Plan", "plan.pro": "Pro", "plan.free": "Free", "plan.upgrade": "Upgrade to Pro", "plan.proDesc": "Skip the queue, play anytime", "plan.active": "Pro plan active", "queue.title": "In queue", "queue.busy": "Server is busy", "queue.position": "{n} in line", "queue.skip": "Go Pro to skip the queue", "queue.upgrade": "Skip queue (Pro)", "shop.cardstyles": "Card styles", "shop.reactions": "Reaction packs", "shop.stickers": "Sticker packs", "shop.titles": "Titles", "shop.titlesHint": "Your title shows under your name in games & lists", "shop.xp": "XP packs", "shop.xpHint": "Level up faster — XP is expensive", "shop.includes": "Includes", "shop.reqLevel": "Level", "shop.reqRating": "Rating", "reward.newTitle": "New title", "reactions.title": "Emoji", "stickers.title": "Stickers", "notif.title": "Notifications", "notif.empty": "No notifications yet", "settings.audio": "Audio", "settings.sound": "Sound effects", "settings.music": "Background music", "settings.musicStyle": "Music style", "settings.trackSantoor": "Traditional (Santoor)", "settings.trackPlayful": "Playful", "profile.cardFront": "Card front", "profile.cardBack": "Card back", "profile.social": "Social & contact", "profile.gender": "Gender", "profile.genderNone": "Unspecified", "profile.socialLinks": "Social media", "profile.socialsVisibility": "Show socials to", "profile.visPublic": "Everyone", "profile.visFriends": "Friends", "profile.visHidden": "Nobody", "profile.socialsHint": "Choose who can see your social pages: everyone, only friends, or nobody.", "profile.saveLinks": "Save links", "profile.saved": "Saved", "shop.cardfronts": "Card fronts", "shop.cardbacks": "Card backs", }; const DICTS: Record = { fa, en }; interface I18nValue { locale: Locale; dir: "rtl" | "ltr"; t: (key: string, vars?: Record) => string; setLocale: (l: Locale) => void; toggle: () => void; } const I18nContext = createContext(null); export function I18nProvider({ children }: { children: React.ReactNode }) { const [locale, setLocaleState] = useState("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) => { 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( () => ({ locale, dir, t, setLocale, toggle: () => setLocale(locale === "fa" ? "en" : "fa"), }), [locale, dir, t, setLocale] ); return {children}; } export function useI18n(): I18nValue { const ctx = useContext(I18nContext); if (!ctx) throw new Error("useI18n must be used within I18nProvider"); return ctx; }