// Winky i18n — Persian (default) + English. // Usage: t('key') resolves current language. Falls back to Persian. const I18N = { fa: { appName: "Winky", welcome: "به Winky خوش اومدی 💞", welcomeDesc: "به Winky خوش اومدی. قبل از شروع، لطفاً تایید کن:", chooseLang: "زبان رو انتخاب کن", chooseLangSub: "زبان خود را انتخاب کنید", langFa: "🇮🇷 فارسی", langEn: "🇬🇧 English", age: "سن", agePlaceholder: "سن", terms: "من بالای ۱۸ سال هستم و قوانین و حریم خصوصی Winky رو می‌پذیرم.", createAccount: "ساخت حساب کاربری ✨", nameLabel: "نام نمایشی", namePlaceholder: "مثلاً سارا", province: "استان", cityLabel: "شهر", cityPlaceholder: "تهران", next: "بعدی", wizLooking: "دنبال چی می‌گردی؟", wizLookingDesc: "بیشتر از یه مورد رو می‌تونی انتخاب کنی و هر وقت خواستی عوضش کنی.", openToEveryone: "با همه آشنا میشم.", shownToNote: "فقط به کسایی نشون داده میشی که دنبال جنسیت تو باشن.", genderLabel: "جنسیت من", chooseGender: "جنسیت رو انتخاب کن", lookingLabel: "دنبال", back: "قبلی", female: "زن", male: "مرد", other: "دیگر", wizIntent: "دنبال چی می‌گردی؟", wizIntentDesc: "سفرِ دوست‌یابیِ خودته — ۱ یا ۲ موردی که بهت می‌خوره انتخاب کن.", intentNote: "این روی پروفایلت نشون داده میشه تا بقیه راحت‌تر پیدات کنن.", errIntent: "حداقل یک مورد رو انتخاب کن.", intent_long_term: "رابطه‌ی بلندمدت", intent_life_partner: "همسر زندگی", intent_casual: "قرارهای دوستانه و خودمونی", intent_intimacy: "صمیمیت، بدون تعهد", intent_marriage: "ازدواج", intent_non_mono: "غیرتک‌همسریِ اخلاقی", wizLife: "سبک زندگی و عادت‌هات", wizLifeDesc: "درباره‌ی عادت‌هات هرچقدر راحتی بگو — به بقیه کمک می‌کنه بهتر با تو کنار بیان.", life_drinking: "نوشیدن", life_drinking_yes: "بله، مینوشم", life_drinking_sometimes: "گاهی مینوشم", life_drinking_rarely: "کم پیش میاد", life_drinking_no: "نمی‌نوشم", life_drinking_sober: "ترک کرده‌ام", life_smoking: "سیگار", life_smoking_sometimes: "گاهی میکشم", life_smoking_no: "نمیکشم", life_smoking_yes: "میکشم", life_smoking_quitting: "دارم ترک میکنم", life_workout: "ورزش", life_workout_daily: "روزانه", life_workout_weekly: "هفتگی", life_workout_rarely: "کم", life_workout_no: "نمیرم", wizInterests: "علایق", wizInterestsDesc: "غذایی یا ورزشی؟ علایقت رو اضافه کن تا با آدم‌های هم‌ذوقت بیشتر آشنا بشی.", selected: "انتخاب شده", saving: "در حال ذخیره...", errName: "نام", errBirth: "تاریخ تولد", errGender: "جنسیت", errPhoto: "حداقل یک عکس آپلود کن", errVoice: "ویس", errLoc: "لوکیشنت رو ثبت کن", locPleaseAllow: "دکمه «اجازه میدم» رو بزن تا لوکیشنت ثبت بشه", errFinish: "لطفاً موارد زیر را وارد کن:", errSave: "ثبت‌نام انجام نشد — اینترنت را چک کن و دوباره تلاش کن.", int_cat_popular: "محبوب‌ترین", int_cat_arts: "هنر", int_cat_film: "فیلم", int_cat_food: "غذا", int_cat_gaming: "بازی", int_cat_music: "موسیقی", int_cat_sports: "ورزش", int_cat_travel: "سفر", int_cat_nature: "طبیعت", int_cat_books: "کتاب", int_cat_pets: "حیوانات", int_cat_wellness: "سلامتی", life_pets: "حیوانات خانگی", life_pets_dog: "سگ", life_pets_cat: "گربه", life_pets_bird: "پرنده", life_pets_fish: "ماهی", life_pets_reptile: "خزنده", life_pets_hamster: "همستر", life_pets_horse: "اسب", life_pets_free: "بدون حیوان", life_pets_want: "می‌خوام داشته باشم", life_pets_allergic: "حساسیت دارم", both: "هر دو", bioTitle: "بیو", bioDesc: "یه توضیح کوتاه بنویس که بقیه بدونن کی هستی.", bioPlaceholder: "پروفایل نمونه...", photosTitle: "عکس‌هات", photosDesc: "تا ۶ عکس آپلود کن (اولی عکس اصلیه). روی عکسی بزن تا اصلی بشه.", main: "اصلی ✅", pick: "انتخاب", voiceTitle: "ویس اینترو 🎙", voiceDesc: "۳۰ ثانیه صدات رو ضبط کن — بقیه قبل از دیدن چهره‌ت، صدات رو می‌شنون. (اختیاری)", record: "🎙 ضبط ویس", done: "آماده‌ای؟ 🎉", doneDesc: "پروفیلت ساخته شد. حالا برو کشف کن و با آدم‌های هم‌خونت آشنا شو.", enter: "ورود به Winky 💞", errAge: "برای استفاده باید بالای ۱۸ سال باشی.", errTerms: "لطفاً قوانین رو تایید کن.", errName: "نام رو وارد کن.", errGender: "جنسیت رو انتخاب کن.", errLooking: "حداقل یک گزینه رو انتخاب کن.", errAgeValid: "سن معتبر وارد کن.", wizNameGender: "اسم و جنسیت", wizBirth: "تاریخ تولد", wizBirthDesc: "روز، ماه و سال تولدت رو با بالا و پایین کردن انتخاب کن", wizBio: "بیو", wizBioDesc: "یه توضیح کوتاه بنویس که بقیه بدونن کی هستی.", wizPhotos: "عکس‌هات", wizPhotosDesc: "تا ۶ عکس آپلود کن (اولی عکس اصلیه). روی عکسی بزن تا اصلی بشه.", uploading: "در حال آپلود...", uploaded: "آپلود شد ✓", errUpload: "خطا در آپلود", errSignup: "خطا در ثبت‌نام، دوباره تلاش کن", ageConfirmTitle: "شما {age} سال دارید", ageConfirmDesc: "مطمئن شو سنّت درسته چون بعداً قابل تغییر نیست.", ageConfirmBtn: "تایید سن من", ageCancelBtn: "انصراف", tapToClose: "برای بستن بزن", cropTitle: "عکست رو کراپ کن", cropApply: "تایید", cropCancel: "انصراف", cropWarn: "اول عکست رو بزرگ‌نمایی کن تا کادر رو کامل پر کنه (حاشیه سیاه نمونه).", skip: "رد کردن", tapToRecord: "برای ضبط بزن", recording: "در حال ضبط...", voiceRecorded: "ضبط شد ✓", voiceUploaded: "ویس آپلود شد ✓", voiceReady: "گوش کن — راضی بودی تایید بزن", reRecord: "ضبط مجدد", confirmVoice: "تایید و ارسال", processing: "در حال آپلود...", micNeeded: "دسترسی میکروفون لازمه 🎙", wizVoice: "ویس اینترو 🎙", wizVoiceDesc: "۳۰ ثانیه صدات رو ضبط کن — بقیه قبل از دیدن چهره‌ت، صدات رو می‌شنون. (اختیاری)", wizDone: "آماده‌ای؟ 🎉", wizDoneDesc: "پروفیلت ساخته شد. حالا برو کشف کن و با آدم‌های هم‌خونت آشنا شو.", wizLoc: "اهل اینجایی؟", wizLocDesc: "لوکیشنت رو تنظیم کن تا ببینی چه کسانی نزدیکته یا فراتر. بدون این نمی‌تونی با کسی مچ بشی.", locAllow: "اجازه میدم 📍", locWorking: "در حال دریافت...", locSet: "✓ لوکیشن ثبت شد", locDenied: "دسترسی داده نشد — از دکمه پایین استفاده کن 📍", locNoGeo: "مرورگر لوکیشن رو پشتیبانی نمی‌کند — از دکمه پایین استفاده کن 📍", locTgFallback: "ارسال لوکیشن از طریق تلگرام 📍", locHow: "لوکیشن چطور استفاده میشه؟", locHowText: "لوکیشن فقط برای نشون دادن آدمای نزدیکته استفاده میشه. ما مختصات دقیق رو ذخیره می‌کنیم ولی هرگز با کسی به اشتراک نمی‌گذاریم.", resetLikes: "🔄 ریست لایک‌ها", testLogin: "🧪 ورود تست (بدون تلگرام)", emptyFeed: "فعلاً کسی توی فید نیست. بعداً برگرد 🔄", matchTitle: "💞 مچ شدید!", matchOk: "باشه ✅", noMatches: "هنوز مچی نداری. برو کشف کن! 🔍", tabDiscover: "کشف", tabQuiz: "کوئیز", tabProfile: "پروفایل", tabMatches: "مچ‌ها", saveProfile: "ذخیره پروفایل", voiceLabel: "ویس اینترو (۳۰ ثانیه)", record: "🎙 ضبط ویس", saveQuiz: "ذخیره کوئیز", photoLabel: "عکس پروفایل (لینک یا آپلود)", photoPlaceholder: "file_id یا آدرس عکس", compat: "هم‌خونی", sameCity: "هم‌شهری هستید", compatDefault: "شهر متفاوته ولی علایق مهمه", like: "لایک", pass: "رد", }, en: { appName: "Winky", welcome: "Welcome to Winky 💞", welcomeDesc: "Welcome to Winky. Before we start, please confirm:", chooseLang: "Choose your language", chooseLangSub: "Select your language", langFa: "🇮🇷 فارسی", langEn: "🇬🇧 English", age: "Age", agePlaceholder: "Age", terms: "I am over 18 and accept the Winky Terms and Privacy Policy.", createAccount: "Create account ✨", nameLabel: "Display name", namePlaceholder: "e.g. Sara", province: "Province", cityLabel: "City", cityPlaceholder: "Tehran", next: "Next", back: "Back", genderLabel: "My gender", chooseGender: "Choose your gender", lookingLabel: "Looking for", female: "Female", male: "Male", other: "Other", wizIntent: "And what are you hoping to find?", wizIntentDesc: "It's your dating journey, so choose 1 or 2 options that feel right for you.", intentNote: "This will show on your profile to help everyone find what they're looking for.", errIntent: "Please select at least one option.", intent_long_term: "A long-term relationship", intent_life_partner: "A life partner", intent_casual: "Fun, casual dates", intent_intimacy: "Intimacy, without commitment", intent_marriage: "Marriage", intent_non_mono: "Ethical non-monogamy", wizLife: "Let's talk about your lifestyle and habits", wizLifeDesc: "Share as much about your habits as you're comfortable with.", life_drinking: "Drinking", life_drinking_yes: "Yes, I drink", life_drinking_sometimes: "I drink sometimes", life_drinking_rarely: "I rarely drink", life_drinking_no: "No, I don't drink", life_drinking_sober: "I'm sober", life_smoking: "Smoking", life_smoking_sometimes: "I smoke sometimes", life_smoking_no: "No, I don't smoke", life_smoking_yes: "Yes, I smoke", life_smoking_quitting: "I'm trying to quit", life_workout: "Workout", life_workout_daily: "Daily", life_workout_weekly: "Weekly", life_workout_rarely: "Rarely", life_workout_no: "No", wizInterests: "What are you into?", wizInterestsDesc: "Proud foodie or big on bouldering? Add interests to your profile to help you match with people who love them too.", selected: "selected", saving: "Saving...", errName: "name", errBirth: "birthdate", errGender: "gender", errPhoto: "Upload at least one photo", errVoice: "voice", errLoc: "Please set your location", locPleaseAllow: "Tap \"Allow\" to register your location", errFinish: "Please fill in:", errSave: "Sign-up failed — check your connection and try again.", int_cat_popular: "POPULAR", int_cat_arts: "ARTS", int_cat_film: "FILM", int_cat_food: "FOOD", int_cat_gaming: "GAMING", int_cat_music: "MUSIC", int_cat_sports: "SPORTS", int_cat_travel: "TRAVEL", int_cat_nature: "NATURE", int_cat_books: "BOOKS", int_cat_pets: "PETS", int_cat_wellness: "WELLNESS", life_pets: "Pets", life_pets_dog: "Dog", life_pets_cat: "Cat", life_pets_bird: "Bird", life_pets_fish: "Fish", life_pets_reptile: "Reptile", life_pets_hamster: "Hamster", life_pets_horse: "Horse", life_pets_free: "Pet-free", life_pets_want: "Want a pet", life_pets_allergic: "Allergic to pets", both: "Both", bioTitle: "Bio", bioDesc: "Write a short intro so others know who you are.", bioPlaceholder: "Sample bio...", photosTitle: "Your photos", photosDesc: "Upload up to 6 photos (first is your main). Tap a photo to make it main.", main: "Main ✅", pick: "Select", voiceTitle: "Voice intro 🎙", voiceDesc: "Record 30s of your voice — others hear it before seeing your face. (Optional)", record: "🎙 Record voice", done: "Ready? 🎉", doneDesc: "Your profile is ready. Go discover and meet compatible people.", enter: "Enter Winky 💞", errAge: "You must be over 18 to use this app.", errTerms: "Please accept the terms.", errName: "Please enter a name.", errGender: "Please select your gender.", errLooking: "Please select at least one option.", errAgeValid: "Please enter a valid age.", wizNameGender: "Name & Gender", wizBirth: "Date of Birth", wizBirthDesc: "Scroll up/down to pick your birth day, month and year", wizLooking: "Who would you like to meet?", wizLookingDesc: "You can choose more than one answer and change it any time.", openToEveryone: "I'm open to dating everyone.", shownToNote: "You'll only be shown to people looking to date your gender.", wizBio: "Bio", wizBioDesc: "Write a short intro so others know who you are.", wizPhotos: "Your photos", wizPhotosDesc: "Upload up to 6 photos (first is your main). Tap a photo to make it main.", uploading: "Uploading...", uploaded: "Uploaded ✓", errUpload: "Upload failed", errSignup: "Signup failed, please try again", ageConfirmTitle: "You're {age}", ageConfirmDesc: "Make sure this is your correct age as you can't change this later.", ageConfirmBtn: "Confirm My Age", ageCancelBtn: "Cancel", tapToClose: "Tap to close", cropTitle: "Crop your photo", cropApply: "Apply", cropCancel: "Cancel", cropWarn: "Zoom in so the photo fills the frame (no black bars) before applying.", skip: "Skip", tapToRecord: "Tap to record", recording: "Recording...", voiceRecorded: "Recorded ✓", voiceUploaded: "Voice uploaded ✓", voiceReady: "Listen — tap confirm if you're happy", reRecord: "Re-record", confirmVoice: "Confirm & send", processing: "Uploading...", micNeeded: "Microphone access needed 🎙", wizVoice: "Voice intro 🎙", wizVoiceDesc: "Record 30s of your voice — others hear it before seeing your face. (Optional)", wizDone: "Ready? 🎉", wizDoneDesc: "Your profile is ready. Go discover and meet compatible people.", wizLoc: "So, are you from around here?", wizLocDesc: "Set your location to see who's in your neighborhood or beyond. You won't be able to match with people otherwise.", locAllow: "Allow 📍", locWorking: "Getting location...", locSet: "✓ Location set", locDenied: "Permission denied — use the button below 📍", locNoGeo: "Browser doesn't support location — use the button below 📍", locTgFallback: "Share location via Telegram 📍", locHow: "How is my location used?", locHowText: "Your location is only used to show nearby people. We store your coordinates but never share them with anyone.", resetLikes: "🔄 Reset likes", testLogin: "🧪 Test login (no Telegram)", emptyFeed: "No one in the feed yet. Come back later 🔄", matchTitle: "💞 It's a match!", matchOk: "OK ✅", noMatches: "No matches yet. Go discover! 🔍", tabDiscover: "Discover", tabQuiz: "Quiz", tabProfile: "Profile", tabMatches: "Matches", saveProfile: "Save profile", voiceLabel: "Voice intro (30s)", record: "🎙 Record voice", saveQuiz: "Save quiz", photoLabel: "Profile photo (link or upload)", photoPlaceholder: "file_id or image URL", compat: "Match", sameCity: "You're from the same city", compatDefault: "Different cities but interests matter", like: "Like", pass: "Pass", }, }; let CUR_LANG = "fa"; function t(key, vars) { const dict = I18N[CUR_LANG] || I18N.fa; let s = dict[key] !== undefined ? dict[key] : (I18N.fa[key] !== undefined ? I18N.fa[key] : key); if (vars) for (const k in vars) s = s.replace(new RegExp("\\{" + k + "\\}", "g"), vars[k]); return s; } function setLang(lang) { CUR_LANG = lang; document.documentElement.lang = lang; document.documentElement.dir = lang === "fa" ? "rtl" : "ltr"; try { localStorage.setItem("winky_lang", lang); } catch (e) {} applyI18n(); } // Translate every element carrying data-i18n="key" using the current language. function applyI18n(root) { const base = root || document; base.querySelectorAll("[data-i18n]").forEach(el => { const key = el.getAttribute("data-i18n"); const val = t(key); if (el.tagName === "INPUT" || el.tagName === "TEXTAREA") { if (!el.value && el.hasAttribute("data-i18n-ph")) el.placeholder = t(el.getAttribute("data-i18n-ph")); else if (!el.value) el.placeholder = val; return; } if (el.hasAttribute("data-i18n-ph")) el.placeholder = t(el.getAttribute("data-i18n-ph")); el.textContent = val; }); // aria-label translations base.querySelectorAll("[data-i18n-aria]").forEach(el => { el.setAttribute("aria-label", t(el.getAttribute("data-i18n-aria"))); }); // also handle placeholder-only attributes base.querySelectorAll("[data-i18n-ph]").forEach(el => { if (!el.hasAttribute("data-i18n")) el.placeholder = t(el.getAttribute("data-i18n-ph")); }); } // Compute age in years from an ISO birthdate string (YYYY-MM-DD), with the // exact birthday taken into account. Returns 0 if the date is missing/invalid. function calcAge(birthdate) { if (!birthdate || birthdate.length < 8) return 0; const [y, m, d] = birthdate.split("-").map(Number); if (!y || !m || !d) return 0; const now = new Date(); let age = now.getFullYear() - y; const hadBirthday = now.getMonth() + 1 > m || (now.getMonth() + 1 === m && now.getDate() >= d); if (!hadBirthday) age--; return age < 0 ? 0 : age; }