import json import os import datetime import pandas as pd import streamlit as st from typing import List, Dict, Optional import bcrypt from deep_translator import GoogleTranslator from gtts import gTTS import speech_recognition as sr from geopy.geocoders import Nominatim import tempfile # --- NEW: UI Text Translations --- UI_TRANSLATIONS = { "English": { "app_title": "Farming Wisdom Archive", "app_tagline": "Preserving Traditional Indian Farming Knowledge for Future Generations", "about_platform_header": "About This Platform", "about_platform_text": "This archive is a community-driven effort to document and share the rich, time-tested farming practices of India. By contributing, you help create a valuable open-source corpus for agricultural research and AI.", "login_tab": "Login", "register_tab": "Register", "login_subheader": "Login to Your Account", "username_label": "Username", "password_label": "Password", "login_button": "Login", "login_success": "Login successful!", "invalid_credentials": "Invalid username or password", "enter_credentials": "Please enter both username and password", "register_subheader": "Join Our Community", "full_name_label": "Full Name", "email_label": "Email", "choose_username_label": "Choose Username", "choose_password_label": "Choose Password", "confirm_password_label": "Confirm Password", "register_button": "Register", "registration_success": "Registration successful! Please login.", "registration_failed": "Username or Email may already exist.", "passwords_no_match": "Passwords do not match", "fill_all_fields": "Please fill in all fields", "welcome_message": "Welcome", "entries_submitted": "Entries Submitted", "logout_button": "Logout", "navigation_header": "Navigation", "home_page": "ЁЯПа Home", "submit_page": "тЬНя╕П Submit Wisdom", "browse_page": "ЁЯУЦ Browse Knowledge", "map_page": "ЁЯЧ║я╕П Knowledge Map", "search_page": "ЁЯФН Search", "translate_page": "ЁЯМР Translation Hub", "export_page": "ЁЯУК Export Data", "profile_page": "ЁЯСд Profile", "home_header": "ЁЯПа Home Dashboard", "stats_header": "ЁЯУК Archive Statistics", "total_entries_metric": "Total Farming Entries", "languages_metric": "Languages Represented", "categories_metric": "Farming Categories", "getting_started_header": "ЁЯЪА Getting Started", "share_knowledge_card_header": "тЬНя╕П Share Knowledge", "share_knowledge_card_text": "Contribute your traditional farming knowledge to the archive.", "share_now_button": "Share Now", "explore_entries_card_header": "ЁЯУЦ Explore Entries", "explore_entries_card_text": "Explore wisdom shared by farmers from all across India.", "explore_now_button": "Explore Now", "discover_map_card_header": "ЁЯЧ║я╕П Discover on Map", "discover_map_card_text": "Find location-specific farming practices on our interactive map.", "view_map_button": "View Map", "submit_header": "тЬНя╕П Submit New Farming Wisdom", "submit_tagline": "Follow the steps below to contribute. Fields marked with `*` are required.", "step1_header": "Step 1: Use Helper Tools (Optional)", "pin_location_header": "**Pin a Location on the Map**", "pin_location_caption": "Search for a location to automatically get its coordinates, which will update the map in the form below.", "find_location_placeholder": "Search for a place (e.g., Hyderabad, Telangana)", "find_location_button": "ЁЯУН Find Location", "location_found_success": "Found and pinned: {location}", "location_not_found_error": "Could not find coordinates for '{location}'", "record_voice_header": "**Record Description with Voice**", "record_voice_caption": "Select a language and press record. The transcribed text will appear in the 'Description' field below.", "record_audio_button": "ЁЯОд Record Audio", "speech_recorded_success": "Text successfully recorded!", "speech_record_error": "Could not record speech.", "step2_header": "Step 2: Complete and Submit the Form", "part1_header": "**Part 1: Essential Information**", "part1_caption": "This is the core of your submission. Describe the farming wisdom clearly.", "title_label": "Title*", "title_placeholder": "e.g., Natural Pest Repellent using Neem Leaves", "description_label": "Description*", "description_placeholder": "Describe the wisdom, practice, or story in detail...", "category_label": "Category*", "language_entry_label": "Language of this Entry*", "part2_header": "**Part 2: Geographic Context**", "part2_caption": "Specify where this knowledge comes from. Use the helper tool above to search, or click the map to fine-tune.", "location_name_label": "Location Name (e.g., Village, District, State)", "latitude_label": "Latitude (Set by Map)", "longitude_label": "Longitude (Set by Map)", "part3_header": "**Part 3: Supporting Media (Optional)**", "part3_caption": "Upload an image or a pre-recorded audio file that relates to this wisdom.", "image_upload_label": "Upload an Image", "audio_upload_label": "Upload an Audio Recording", "submit_contribution_button": "тЬЕ Submit My Contribution", "submit_success": "Farming wisdom submitted successfully! Thank you.", "submit_failed": "Failed to save entry. Please try again.", "submit_validation_error": "Please fill in the required fields: Title and Description.", "browse_header": "ЁЯУЦ Browse Farming Knowledge", "browse_tagline": "Explore wisdom shared by the community. Use the filters to narrow your search.", "filter_lang_label": "Filter by Language", "filter_cat_label": "Filter by Category", "sort_by_label": "Sort by", "all_option": "All", "newest_first_option": "Newest First", "oldest_first_option": "Oldest First", "title_az_option": "Title A-Z", "showing_entries": "**Showing {count} of {total} entries**", "category_display": "**Category:** {category}", "language_display": "**Language:** {language}", "location_display": "**Location:** {location}", "submitted_display": "**Submitted:** {date}", "view_details_expander": "View Details and Media", "description_display": "**Description:**", "listen_button": "ЁЯФК Listen to Description", "image_caption": "Attached Image", "coords_display": "**Coords:** `{lat}, {lon}`", "map_header": "ЁЯЧ║я╕П Farming Wisdom Map", "map_tagline": "Explore traditional farming knowledge geographically. Click on a marker to see details.", "map_popup_category": "Category:", "map_popup_language": "Language:", "map_info": "Showing {count} entries with location data on the map.", "map_warning": "No entries with location data found. Submit entries with coordinates to see them on the map!", "search_header": "ЁЯФН Search Knowledge", "search_tagline": "Use the search bar and filters to find specific farming techniques and wisdom.", "search_placeholder": "ЁЯФО Search for farming practices...", "advanced_filters_expander": "Advanced Filters", "has_media_checkbox": "Has Media (Image/Audio)", "has_location_checkbox": "Has Location Data", "search_results_header": "--- \n ### Found {count} results for '{query}'", "translation_hub_header": "ЁЯМР Translation Hub", "translation_hub_tagline": "Translate farming knowledge between different Indian languages.", "original_text_header": "Original Text", "source_language_label": "Source Language", "text_to_translate_label": "Text to translate", "text_to_translate_placeholder": "Enter text here...", "translated_text_header": "Translated Text", "target_language_label": "Translate to", "translate_button": "ЁЯМР Translate", "translation_output_label": "Translation", "export_header": "ЁЯУК Export Data", "export_tagline": "Export collected farming wisdom for research and analysis.", "export_config_header": "Export Configuration", "format_label": "Select Format", "generate_export_button": "Generate Export File", "export_no_match_warning": "No entries match the selected filters.", "export_download_csv_button": "ЁЯУе Download CSV", "export_download_jsonl_button": "ЁЯУе Download JSONL", "export_ready_success": "Export file ready for {count} entries.", "profile_header": "ЁЯСд User Profile: {name}", "my_info_tab": "тД╣я╕П My Info", "my_contributions_tab": "ЁЯУЬ My Contributions", "settings_tab": "тЪЩя╕П Settings", "profile_info_header": "Profile Information", "full_name_display": "**Full Name:**\n\n`{name}`", "username_display": "**Username:**\n\n`{username}`", "email_display": "**Email:**\n\n`{email}`", "member_since_display": "**Member Since:**\n\n`{date}`", "total_entries_metric_profile": "Total Entries Submitted", "my_recent_contributions_header": "My Recent Contributions", "contributions_count_message": "You have contributed **{count}** farming knowledge entries. Here are the latest 5:", "no_contributions_message": "You haven't submitted any entries yet. Go to the 'Submit Wisdom' page to share your knowledge!", "account_prefs_header": "Account Preferences", "preferred_language_label": "Preferred Language (for display)", "email_notifications_checkbox": "Enable email notifications for new entries", "update_profile_button": "Update Profile", "profile_update_success": "Profile settings updated successfully! (Note: Feature is illustrative)", "app_version_footer": "ЁЯМ╛ **Farming Wisdom Archive v1.1**", "language_select_label": "Select Language" }, "Hindi": { "app_title": "рдХреГрд╖рд┐ рдЬреНрдЮрд╛рди рдкреБрд░рд╛рд▓реЗрдЦ", "app_tagline": "рдЖрдиреЗ рд╡рд╛рд▓реА рдкреАрдврд╝рд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рдкрд╛рд░рдВрдкрд░рд┐рдХ рднрд╛рд░рддреАрдп рдХреГрд╖рд┐ рдЬреНрдЮрд╛рди рдХрд╛ рд╕рдВрд░рдХреНрд╖рдг", "about_platform_header": "рдЗрд╕ рдкреНрд▓реЗрдЯрдлрд╝реЙрд░реНрдо рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ", "about_platform_text": "рдпрд╣ рдкреБрд░рд╛рд▓реЗрдЦ рднрд╛рд░рдд рдХреА рд╕рдореГрджреНрдз, рд╕рдордп-рдкрд░реАрдХреНрд╖рд┐рдд рдХреГрд╖рд┐ рдкрджреНрдзрддрд┐рдпреЛрдВ рдХрд╛ рджрд╕реНрддрд╛рд╡реЗрдЬреАрдХрд░рдг рдФрд░ рд╕рд╛рдЭрд╛ рдХрд░рдиреЗ рдХрд╛ рдПрдХ рд╕рд╛рдореБрджрд╛рдпрд┐рдХ рдкреНрд░рдпрд╛рд╕ рд╣реИред рдпреЛрдЧрджрд╛рди рдХрд░рдХреЗ, рдЖрдк рдХреГрд╖рд┐ рдЕрдиреБрд╕рдВрдзрд╛рди рдФрд░ рдПрдЖрдИ рдХреЗ рд▓рд┐рдП рдПрдХ рдореВрд▓реНрдпрд╡рд╛рди рдУрдкрди-рд╕реЛрд░реНрд╕ рдХреЙрд░реНрдкрд╕ рдмрдирд╛рдиреЗ рдореЗрдВ рдорджрдж рдХрд░рддреЗ рд╣реИрдВред", "login_tab": "рд▓реЙрдЧ рдЗрди рдХрд░реЗрдВ", "register_tab": "рдкрдВрдЬреАрдХрд░рдг рдХрд░реЗрдВ", "login_subheader": "рдЕрдкрдиреЗ рдЦрд╛рддреЗ рдореЗрдВ рд▓реЙрдЧ рдЗрди рдХрд░реЗрдВ", "username_label": "рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдирд╛рдо", "password_label": "рдкрд╛рд╕рд╡рд░реНрдб", "login_button": "рд▓реЙрдЧ рдЗрди рдХрд░реЗрдВ", "login_success": "рд▓реЙрдЧрд┐рди рд╕рдлрд▓!", "invalid_credentials": "рдЕрдорд╛рдиреНрдп рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдирд╛рдо рдпрд╛ рдкрд╛рд╕рд╡рд░реНрдб", "enter_credentials": "рдХреГрдкрдпрд╛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдирд╛рдо рдФрд░ рдкрд╛рд╕рд╡рд░реНрдб рджреЛрдиреЛрдВ рджрд░реНрдЬ рдХрд░реЗрдВ", "register_subheader": "рд╣рдорд╛рд░реЗ рд╕рдореБрджрд╛рдп рдореЗрдВ рд╢рд╛рдорд┐рд▓ рд╣реЛрдВ", "full_name_label": "рдкреВрд░рд╛ рдирд╛рдо", "email_label": "рдИрдореЗрд▓", "choose_username_label": "рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдирд╛рдо рдЪреБрдиреЗрдВ", "choose_password_label": "рдкрд╛рд╕рд╡рд░реНрдб рдЪреБрдиреЗрдВ", "confirm_password_label": "рдкрд╛рд╕рд╡рд░реНрдб рдХреА рдкреБрд╖реНрдЯрд┐ рдХрд░реЗрдВ", "register_button": "рдкрдВрдЬреАрдХрд░рдг рдХрд░реЗрдВ", "registration_success": "рдкрдВрдЬреАрдХрд░рдг рд╕рдлрд▓! рдХреГрдкрдпрд╛ рд▓реЙрдЧ рдЗрди рдХрд░реЗрдВред", "registration_failed": "рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдирд╛рдо рдпрд╛ рдИрдореЗрд▓ рдкрд╣рд▓реЗ рд╕реЗ рдореМрдЬреВрдж рд╣реЛ рд╕рдХрддрд╛ рд╣реИред", "passwords_no_match": "рдкрд╛рд╕рд╡рд░реНрдб рдореЗрд▓ рдирд╣реАрдВ рдЦрд╛рддреЗ", "fill_all_fields": "рдХреГрдкрдпрд╛ рд╕рднреА рдлрд╝реАрд▓реНрдб рднрд░реЗрдВ", "welcome_message": "рд╕реНрд╡рд╛рдЧрдд рд╣реИ", "entries_submitted": "рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐рдпрд╛рдБ рдкреНрд░рд╕реНрддреБрдд рдХреА рдЧрдИрдВ", "logout_button": "рд▓реЙрдЧ рдЖрдЙрдЯ", "navigation_header": "рдиреЗрд╡рд┐рдЧреЗрд╢рди", "home_page": "ЁЯПа рд╣реЛрдо", "submit_page": "тЬНя╕П рдЬреНрдЮрд╛рди рдкреНрд░рд╕реНрддреБрдд рдХрд░реЗрдВ", "browse_page": "ЁЯУЦ рдЬреНрдЮрд╛рди рдмреНрд░рд╛рдЙрдЬрд╝ рдХрд░реЗрдВ", "map_page": "ЁЯЧ║я╕П рдЬреНрдЮрд╛рди рдорд╛рдирдЪрд┐рддреНрд░", "search_page": "ЁЯФН рдЦреЛрдЬреЗрдВ", "translate_page": "ЁЯМР рдЕрдиреБрд╡рд╛рдж рд╣рдм", "export_page": "ЁЯУК рдбреЗрдЯрд╛ рдирд┐рд░реНрдпрд╛рдд рдХрд░реЗрдВ", "profile_page": "ЁЯСд рдкреНрд░реЛрдлрд╝рд╛рдЗрд▓", "home_header": "ЁЯПа рд╣реЛрдо рдбреИрд╢рдмреЛрд░реНрдб", "stats_header": "ЁЯУК рдкреБрд░рд╛рд▓реЗрдЦ рд╕рд╛рдВрдЦреНрдпрд┐рдХреА", "total_entries_metric": "рдХреБрд▓ рдХреГрд╖рд┐ рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐рдпрд╛рдБ", "languages_metric": "рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рд╡рд╛рд▓реА рднрд╛рд╖рд╛рдПрдБ", "categories_metric": "рдХреГрд╖рд┐ рд╢реНрд░реЗрдгрд┐рдпрд╛рдБ", "getting_started_header": "ЁЯЪА рд╢реБрд░реБрдЖрдд рдХреИрд╕реЗ рдХрд░реЗрдВ", "share_knowledge_card_header": "тЬНя╕П рдЬреНрдЮрд╛рди рд╕рд╛рдЭрд╛ рдХрд░реЗрдВ", "share_knowledge_card_text": "рдкреБрд░рд╛рд▓реЗрдЦ рдореЗрдВ рдЕрдкрдиреЗ рдкрд╛рд░рдВрдкрд░рд┐рдХ рдХреГрд╖рд┐ рдЬреНрдЮрд╛рди рдХрд╛ рдпреЛрдЧрджрд╛рди рдХрд░реЗрдВред", "share_now_button": "рдЕрднреА рд╕рд╛рдЭрд╛ рдХрд░реЗрдВ", "explore_entries_card_header": "ЁЯУЦ рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐рдпрд╛рдБ рджреЗрдЦреЗрдВ", "explore_entries_card_text": "рдкреВрд░реЗ рднрд╛рд░рдд рдХреЗ рдХрд┐рд╕рд╛рдиреЛрдВ рджреНрд╡рд╛рд░рд╛ рд╕рд╛рдЭрд╛ рдХрд┐рдП рдЧрдП рдЬреНрдЮрд╛рди рдХрд╛ рдЕрдиреНрд╡реЗрд╖рдг рдХрд░реЗрдВред", "explore_now_button": "рдЕрднреА рдЕрдиреНрд╡реЗрд╖рдг рдХрд░реЗрдВ", "discover_map_card_header": "ЁЯЧ║я╕П рдорд╛рдирдЪрд┐рддреНрд░ рдкрд░ рдЦреЛрдЬреЗрдВ", "discover_map_card_text": "рд╣рдорд╛рд░реЗ рдЗрдВрдЯрд░реЗрдХреНрдЯрд┐рд╡ рдорд╛рдирдЪрд┐рддреНрд░ рдкрд░ рд╕реНрдерд╛рди-рд╡рд┐рд╢рд┐рд╖реНрдЯ рдХреГрд╖рд┐ рдкрджреНрдзрддрд┐рдпреЛрдВ рдХрд╛ рдкрддрд╛ рд▓рдЧрд╛рдПрдВред", "view_map_button": "рдорд╛рдирдЪрд┐рддреНрд░ рджреЗрдЦреЗрдВ", "submit_header": "тЬНя╕П рдирдИ рдХреГрд╖рд┐ рд╡рд┐рд╢реЗрд╖рдЬреНрдЮрддрд╛ рдкреНрд░рд╕реНрддреБрдд рдХрд░реЗрдВ", "submit_tagline": "рдпреЛрдЧрджрд╛рди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдиреАрдЪреЗ рджрд┐рдП рдЧрдП рдЪрд░рдгреЛрдВ рдХрд╛ рдкрд╛рд▓рди рдХрд░реЗрдВред `*` рд╕реЗ рдЪрд┐рд╣реНрдирд┐рдд рдлрд╝реАрд▓реНрдб рдЖрд╡рд╢реНрдпрдХ рд╣реИрдВред", "step1_header": "рдЪрд░рдг 1: рд╕рд╣рд╛рдпрдХ рдЙрдкрдХрд░рдгреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ (рд╡реИрдХрд▓реНрдкрд┐рдХ)", "pin_location_header": "**рдорд╛рдирдЪрд┐рддреНрд░ рдкрд░ рдПрдХ рд╕реНрдерд╛рди рдкрд┐рди рдХрд░реЗрдВ**", "pin_location_caption": "рдХрд┐рд╕реА рд╕реНрдерд╛рди рдХреЗ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЦреЛрдЬреЗрдВ, рдЬреЛ рдиреАрдЪреЗ рджрд┐рдП рдЧрдП рдлреЙрд░реНрдо рдореЗрдВ рдорд╛рдирдЪрд┐рддреНрд░ рдХреЛ рдЕрдкрдбреЗрдЯ рдХрд░реЗрдЧрд╛ред", "find_location_placeholder": "рдПрдХ рдЬрдЧрд╣ рдЦреЛрдЬреЗрдВ (рдЬреИрд╕реЗ, рд╣реИрджрд░рд╛рдмрд╛рдж, рддреЗрд▓рдВрдЧрд╛рдирд╛)", "find_location_button": "ЁЯУН рд╕реНрдерд╛рди рдЦреЛрдЬреЗрдВ", "location_found_success": "рдорд┐рд▓ рдЧрдпрд╛ рдФрд░ рдкрд┐рди рдХрд┐рдпрд╛ рдЧрдпрд╛: {location}", "location_not_found_error": "'{location}' рдХреЗ рд▓рд┐рдП рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдирд╣реАрдВ рдорд┐рд▓ рд╕рдХреЗ", "record_voice_header": "**рдЖрд╡рд╛рдЬ рдХреЗ рд╕рд╛рде рд╡рд┐рд╡рд░рдг рд░рд┐рдХреЙрд░реНрдб рдХрд░реЗрдВ**", "record_voice_caption": "рдПрдХ рднрд╛рд╖рд╛ рдЪреБрдиреЗрдВ рдФрд░ рд░рд┐рдХреЙрд░реНрдб рджрдмрд╛рдПрдВред рд▓рд┐рдЦрд┐рдд рдкрд╛рда рдиреАрдЪреЗ 'рд╡рд┐рд╡рд░рдг' рдлрд╝реАрд▓реНрдб рдореЗрдВ рджрд┐рдЦрд╛рдИ рджреЗрдЧрд╛ред", "record_audio_button": "ЁЯОд рдСрдбрд┐рдпреЛ рд░рд┐рдХреЙрд░реНрдб рдХрд░реЗрдВ", "speech_recorded_success": "рдкрд╛рда рд╕рдлрд▓рддрд╛рдкреВрд░реНрд╡рдХ рд░рд┐рдХреЙрд░реНрдб рдХрд┐рдпрд╛ рдЧрдпрд╛!", "speech_record_error": "рднрд╛рд╖рдг рд░рд┐рдХреЙрд░реНрдб рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрд╛ред", "step2_header": "рдЪрд░рдг 2: рдлрд╝реЙрд░реНрдо рдХреЛ рдкреВрд░рд╛ рдХрд░реЗрдВ рдФрд░ рд╕рдмрдорд┐рдЯ рдХрд░реЗрдВ", "part1_header": "**рднрд╛рдЧ 1: рдЖрд╡рд╢реНрдпрдХ рдЬрд╛рдирдХрд╛рд░реА**", "part1_caption": "рдпрд╣ рдЖрдкрдХреЗ рд╕рдмрдорд┐рд╢рди рдХрд╛ рдореВрд▓ рд╣реИред рдХреГрд╖рд┐ рдЬреНрдЮрд╛рди рдХрд╛ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рд╡рд░реНрдгрди рдХрд░реЗрдВред", "title_label": "рд╢реАрд░реНрд╖рдХ*", "title_placeholder": "рдЙрджрд╛., рдиреАрдо рдХреЗ рдкрддреНрддреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдкреНрд░рд╛рдХреГрддрд┐рдХ рдХреАрдЯ рд╡рд┐рдХрд░реНрд╖рдХ", "description_label": "рд╡рд┐рд╡рд░рдг*", "description_placeholder": "рдЬреНрдЮрд╛рди, рдЕрднреНрдпрд╛рд╕, рдпрд╛ рдХрд╣рд╛рдиреА рдХрд╛ рд╡рд┐рд╕реНрддрд╛рд░ рд╕реЗ рд╡рд░реНрдгрди рдХрд░реЗрдВ...", "category_label": "рд╢реНрд░реЗрдгреА*", "language_entry_label": "рдЗрд╕ рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐ рдХреА рднрд╛рд╖рд╛*", "part2_header": "**рднрд╛рдЧ 2: рднреМрдЧреЛрд▓рд┐рдХ рд╕рдВрджрд░реНрдн**", "part2_caption": "рдмрддрд╛рдПрдВ рдХрд┐ рдпрд╣ рдЬреНрдЮрд╛рди рдХрд╣рд╛рдВ рд╕реЗ рдЖрддрд╛ рд╣реИред рдЦреЛрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рдКрдкрд░ рджрд┐рдП рдЧрдП рд╕рд╣рд╛рдпрдХ рдЙрдкрдХрд░рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ, рдпрд╛ рдкрд┐рди рдХреА рд╕реНрдерд┐рддрд┐ рдХреЛ рдареАрдХ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдорд╛рдирдЪрд┐рддреНрд░ рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░реЗрдВред", "location_name_label": "рд╕реНрдерд╛рди рдХрд╛ рдирд╛рдо (рдЙрджрд╛., рдЧрд╛рдБрд╡, рдЬрд┐рд▓рд╛, рд░рд╛рдЬреНрдп)", "latitude_label": "рдЕрдХреНрд╖рд╛рдВрд╢ (рдорд╛рдирдЪрд┐рддреНрд░ рджреНрд╡рд╛рд░рд╛ рдирд┐рд░реНрдзрд╛рд░рд┐рдд)", "longitude_label": "рджреЗрд╢рд╛рдВрддрд░ (рдорд╛рдирдЪрд┐рддреНрд░ рджреНрд╡рд╛рд░рд╛ рдирд┐рд░реНрдзрд╛рд░рд┐рдд)", "part3_header": "**рднрд╛рдЧ 3: рд╕рд╣рд╛рдпрдХ рдореАрдбрд┐рдпрд╛ (рд╡реИрдХрд▓реНрдкрд┐рдХ)**", "part3_caption": "рдПрдХ рдЫрд╡рд┐ рдпрд╛ рдкрд╣рд▓реЗ рд╕реЗ рд░рд┐рдХреЙрд░реНрдб рдХреА рдЧрдИ рдСрдбрд┐рдпреЛ рдлрд╝рд╛рдЗрд▓ рдЕрдкрд▓реЛрдб рдХрд░реЗрдВ рдЬреЛ рдЗрд╕ рдЬреНрдЮрд╛рди рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд рд╣реЛред", "image_upload_label": "рдПрдХ рдЫрд╡рд┐ рдЕрдкрд▓реЛрдб рдХрд░реЗрдВ", "audio_upload_label": "рдПрдХ рдСрдбрд┐рдпреЛ рд░рд┐рдХреЙрд░реНрдбрд┐рдВрдЧ рдЕрдкрд▓реЛрдб рдХрд░реЗрдВ", "submit_contribution_button": "тЬЕ рдореЗрд░рд╛ рдпреЛрдЧрджрд╛рди рдЬрдорд╛ рдХрд░реЗрдВ", "submit_success": "рдХреГрд╖рд┐ рдЬреНрдЮрд╛рди рд╕рдлрд▓рддрд╛рдкреВрд░реНрд╡рдХ рдкреНрд░рд╕реНрддреБрдд рдХрд┐рдпрд╛ рдЧрдпрд╛! рдзрдиреНрдпрд╡рд╛рджред", "submit_failed": "рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐ рд╕рд╣реЗрдЬрдиреЗ рдореЗрдВ рд╡рд┐рдлрд▓ред рдХреГрдкрдпрд╛ рдкреБрди: рдкреНрд░рдпрд╛рд╕ рдХрд░реЗрдВред", "submit_validation_error": "рдХреГрдкрдпрд╛ рдЖрд╡рд╢реНрдпрдХ рдлрд╝реАрд▓реНрдб рднрд░реЗрдВ: рд╢реАрд░реНрд╖рдХ рдФрд░ рд╡рд┐рд╡рд░рдгред", "browse_header": "ЁЯУЦ рдХреГрд╖рд┐ рдЬреНрдЮрд╛рди рдмреНрд░рд╛рдЙрдЬрд╝ рдХрд░реЗрдВ", "browse_tagline": "рд╕рдореБрджрд╛рдп рджреНрд╡рд╛рд░рд╛ рд╕рд╛рдЭрд╛ рдХрд┐рдП рдЧрдП рдЬреНрдЮрд╛рди рдХрд╛ рдЕрдиреНрд╡реЗрд╖рдг рдХрд░реЗрдВред рдЕрдкрдиреА рдЦреЛрдЬ рдХреЛ рд╕реАрдорд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред", "filter_lang_label": "рднрд╛рд╖рд╛ рдХреЗ рдЕрдиреБрд╕рд╛рд░ рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд░реЗрдВ", "filter_cat_label": "рд╢реНрд░реЗрдгреА рдХреЗ рдЕрдиреБрд╕рд╛рд░ рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд░реЗрдВ", "sort_by_label": "рдЗрд╕рдХреЗ рдЕрдиреБрд╕рд╛рд░ рдХреНрд░рдордмрджреНрдз рдХрд░реЗрдВ", "all_option": "рд╕рднреА", "newest_first_option": "рдирд╡реАрдирддрдо рдкрд╣рд▓реЗ", "oldest_first_option": "рд╕рдмрд╕реЗ рдкреБрд░рд╛рдирд╛ рдкрд╣рд▓реЗ", "title_az_option": "рд╢реАрд░реНрд╖рдХ A-Z", "showing_entries": "**{total} рдореЗрдВ рд╕реЗ {count} рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐рдпрд╛рдБ рджрд┐рдЦрд╛ рд░рд╣рд╛ рд╣реИ**", "category_display": "**рд╢реНрд░реЗрдгреА:** {category}", "language_display": "**рднрд╛рд╖рд╛:** {language}", "location_display": "**рд╕реНрдерд╛рди:** {location}", "submitted_display": "**рдкреНрд░рд╕реНрддреБрдд:** {date}", "view_details_expander": "рд╡рд┐рд╡рд░рдг рдФрд░ рдореАрдбрд┐рдпрд╛ рджреЗрдЦреЗрдВ", "description_display": "**рд╡рд┐рд╡рд░рдг:**", "listen_button": "ЁЯФК рд╡рд┐рд╡рд░рдг рд╕реБрдиреЗрдВ", "image_caption": "рд╕рдВрд▓рдЧреНрди рдЫрд╡рд┐", "coords_display": "**рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ:** `{lat}, {lon}`", "map_header": "ЁЯЧ║я╕П рдХреГрд╖рд┐ рдЬреНрдЮрд╛рди рдорд╛рдирдЪрд┐рддреНрд░", "map_tagline": "рднреМрдЧреЛрд▓рд┐рдХ рд░реВрдк рд╕реЗ рдкрд╛рд░рдВрдкрд░рд┐рдХ рдХреГрд╖рд┐ рдЬреНрдЮрд╛рди рдХрд╛ рдЕрдиреНрд╡реЗрд╖рдг рдХрд░реЗрдВред рд╡рд┐рд╡рд░рдг рджреЗрдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдорд╛рд░реНрдХрд░ рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░реЗрдВред", "map_popup_category": "рд╢реНрд░реЗрдгреА:", "map_popup_language": "рднрд╛рд╖рд╛:", "map_info": "рдорд╛рдирдЪрд┐рддреНрд░ рдкрд░ рд╕реНрдерд╛рди рдбреЗрдЯрд╛ рдХреЗ рд╕рд╛рде {count} рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐рдпрд╛рдБ рджрд┐рдЦрд╛ рд░рд╣рд╛ рд╣реИред", "map_warning": "рд╕реНрдерд╛рди рдбреЗрдЯрд╛ рдХреЗ рд╕рд╛рде рдХреЛрдИ рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐ рдирд╣реАрдВ рдорд┐рд▓реАред рдЙрдиреНрд╣реЗрдВ рдорд╛рдирдЪрд┐рддреНрд░ рдкрд░ рджреЗрдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдХреЗ рд╕рд╛рде рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐рдпрд╛рдБ рдЬрдорд╛ рдХрд░реЗрдВ!", "search_header": "ЁЯФН рдЬреНрдЮрд╛рди рдЦреЛрдЬреЗрдВ", "search_tagline": "рд╡рд┐рд╢рд┐рд╖реНрдЯ рдХреГрд╖рд┐ рддрдХрдиреАрдХреЛрдВ рдФрд░ рдЬреНрдЮрд╛рди рдХреЛ рдЦреЛрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рдЦреЛрдЬ рдмрд╛рд░ рдФрд░ рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред", "search_placeholder": "ЁЯФО рдХреГрд╖рд┐ рдкрджреНрдзрддрд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рдЦреЛрдЬреЗрдВ...", "advanced_filters_expander": "рдЙрдиреНрдирдд рдлрд╝рд┐рд▓реНрдЯрд░", "has_media_checkbox": "рдореАрдбрд┐рдпрд╛ рд╣реИ (рдЫрд╡рд┐/рдСрдбрд┐рдпреЛ)", "has_location_checkbox": "рд╕реНрдерд╛рди рдбреЗрдЯрд╛ рд╣реИ", "search_results_header": "--- \n ### '{query}' рдХреЗ рд▓рд┐рдП {count} рдкрд░рд┐рдгрд╛рдо рдорд┐рд▓реЗ", "translation_hub_header": "ЁЯМР рдЕрдиреБрд╡рд╛рдж рд╣рдм", "translation_hub_tagline": "рд╡рд┐рднрд┐рдиреНрди рднрд╛рд░рддреАрдп рднрд╛рд╖рд╛рдУрдВ рдХреЗ рдмреАрдЪ рдХреГрд╖рд┐ рдЬреНрдЮрд╛рди рдХрд╛ рдЕрдиреБрд╡рд╛рдж рдХрд░реЗрдВред", "original_text_header": "рдореВрд▓ рдкрд╛рда", "source_language_label": "рд╕реНрд░реЛрдд рднрд╛рд╖рд╛", "text_to_translate_label": "рдЕрдиреБрд╡рд╛рдж рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд╛рда", "text_to_translate_placeholder": "рдпрд╣рд╛рдВ рдкрд╛рда рджрд░реНрдЬ рдХрд░реЗрдВ...", "translated_text_header": "рдЕрдиреБрд╡рд╛рджрд┐рдд рдкрд╛рда", "target_language_label": "рдЗрд╕рдореЗрдВ рдЕрдиреБрд╡рд╛рдж рдХрд░реЗрдВ", "translate_button": "ЁЯМР рдЕрдиреБрд╡рд╛рдж рдХрд░реЗрдВ", "translation_output_label": "рдЕрдиреБрд╡рд╛рдж", "export_header": "ЁЯУК рдбреЗрдЯрд╛ рдирд┐рд░реНрдпрд╛рдд рдХрд░реЗрдВ", "export_tagline": "рдЕрдиреБрд╕рдВрдзрд╛рди рдФрд░ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХреЗ рд▓рд┐рдП рдПрдХрддреНрд░рд┐рдд рдХреГрд╖рд┐ рдЬреНрдЮрд╛рди рдХрд╛ рдирд┐рд░реНрдпрд╛рдд рдХрд░реЗрдВред", "export_config_header": "рдирд┐рд░реНрдпрд╛рдд рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди", "format_label": "рдкреНрд░рд╛рд░реВрдк рдЪреБрдиреЗрдВ", "generate_export_button": "рдирд┐рд░реНрдпрд╛рдд рдлрд╝рд╛рдЗрд▓ рдЙрддреНрдкрдиреНрди рдХрд░реЗрдВ", "export_no_match_warning": "рдЪрдпрдирд┐рдд рдлрд╝рд┐рд▓реНрдЯрд░ рд╕реЗ рдХреЛрдИ рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐ рдореЗрд▓ рдирд╣реАрдВ рдЦрд╛рддреАред", "export_download_csv_button": "ЁЯУе CSV рдбрд╛рдЙрдирд▓реЛрдб рдХрд░реЗрдВ", "export_download_jsonl_button": "ЁЯУе JSONL рдбрд╛рдЙрдирд▓реЛрдб рдХрд░реЗрдВ", "export_ready_success": "{count} рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рдирд┐рд░реНрдпрд╛рдд рдлрд╝рд╛рдЗрд▓ рддреИрдпрд╛рд░ рд╣реИред", "profile_header": "ЁЯСд рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдкреНрд░реЛрдлрд╝рд╛рдЗрд▓: {name}", "my_info_tab": "тД╣я╕П рдореЗрд░реА рдЬрд╛рдирдХрд╛рд░реА", "my_contributions_tab": "ЁЯУЬ рдореЗрд░реЗ рдпреЛрдЧрджрд╛рди", "settings_tab": "тЪЩя╕П рд╕реЗрдЯрд┐рдВрдЧреНрд╕", "profile_info_header": "рдкреНрд░реЛрдлрд╝рд╛рдЗрд▓ рдЬрд╛рдирдХрд╛рд░реА", "full_name_display": "**рдкреВрд░рд╛ рдирд╛рдо:**\n\n`{name}`", "username_display": "**рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдирд╛рдо:**\n\n`{username}`", "email_display": "**рдИрдореЗрд▓:**\n\n`{email}`", "member_since_display": "**рд╕рджрд╕реНрдп рдЬрдм рд╕реЗ:**\n\n`{date}`", "total_entries_metric_profile": "рдХреБрд▓ рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐рдпрд╛рдБ рдкреНрд░рд╕реНрддреБрдд рдХреА рдЧрдИрдВ", "my_recent_contributions_header": "рдореЗрд░реЗ рд╣рд╛рд▓ рдХреЗ рдпреЛрдЧрджрд╛рди", "contributions_count_message": "рдЖрдкрдиреЗ **{count}** рдХреГрд╖рд┐ рдЬреНрдЮрд╛рди рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐рдпреЛрдВ рдХрд╛ рдпреЛрдЧрджрд╛рди рджрд┐рдпрд╛ рд╣реИред рдпрд╣рд╛рдБ рдирд╡реАрдирддрдо 5 рд╣реИрдВ:", "no_contributions_message": "рдЖрдкрдиреЗ рдЕрднреА рддрдХ рдХреЛрдИ рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐ рдЬрдорд╛ рдирд╣реАрдВ рдХреА рд╣реИред рдЕрдкрдирд╛ рдЬреНрдЮрд╛рди рд╕рд╛рдЭрд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП 'рдЬреНрдЮрд╛рди рдЬрдорд╛ рдХрд░реЗрдВ' рдкреГрд╖реНрда рдкрд░ рдЬрд╛рдПрдВ!", "account_prefs_header": "рдЦрд╛рддрд╛ рдкреНрд░рд╛рдердорд┐рдХрддрд╛рдПрдВ", "preferred_language_label": "рдкрд╕рдВрджреАрджрд╛ рднрд╛рд╖рд╛ (рдкреНрд░рджрд░реНрд╢рди рдХреЗ рд▓рд┐рдП)", "email_notifications_checkbox": "рдирдП рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рдИрдореЗрд▓ рд╕реВрдЪрдирд╛рдПрдВ рд╕рдХреНрд╖рдо рдХрд░реЗрдВ", "update_profile_button": "рдкреНрд░реЛрдлрд╝рд╛рдЗрд▓ рдЕрдкрдбреЗрдЯ рдХрд░реЗрдВ", "profile_update_success": "рдкреНрд░реЛрдлрд╝рд╛рдЗрд▓ рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рд╕рдлрд▓рддрд╛рдкреВрд░реНрд╡рдХ рдЕрдкрдбреЗрдЯ рдХреА рдЧрдИрдВ! (рдиреЛрдЯ: рд╕реБрд╡рд┐рдзрд╛ рдЙрджрд╛рд╣рд░рдгрд╛рддреНрдордХ рд╣реИ)", "app_version_footer": "ЁЯМ╛ **рдХреГрд╖рд┐ рдЬреНрдЮрд╛рди рдкреБрд░рд╛рд▓реЗрдЦ v1.1**", "language_select_label": "рднрд╛рд╖рд╛ рдЪреБрдиреЗрдВ" } } # --- NEW: UI Text Retriever Function --- def get_ui_text(key, lang="English"): """ Retrieves UI text from the translations dictionary. Defaults to English if a key is not found in the target language. """ return UI_TRANSLATIONS.get(lang, UI_TRANSLATIONS["English"]).get(key, UI_TRANSLATIONS["English"].get(key, key)) # --- Data Storage Functions --- def load_entries() -> List[Dict]: """Load entries from JSON file.""" try: os.makedirs("data_entries", exist_ok=True) if os.path.exists("data_entries/entries.json"): with open("data_entries/entries.json", "r", encoding="utf-8") as f: return json.load(f) return [] except Exception as e: st.error(f"Error loading entries: {str(e)}") return [] def save_entry(entry: Dict) -> bool: """Save a single entry by appending to the JSON file.""" try: entries = load_entries() entries.append(entry) os.makedirs("data_entries", exist_ok=True) with open("data_entries/entries.json", "w", encoding="utf-8") as f: json.dump(entries, f, indent=4, ensure_ascii=False) return True except Exception as e: st.error(f"Error saving entry: {str(e)}") return False # --- Category and Language Definitions --- def get_categories() -> List[str]: """Get list of available categories.""" return [ "Seed Selection & Storage", "Soil Management", "Crop Rotation", "Natural Fertilizers", "Pest Control", "Water Management", "Harvest Techniques", "Seasonal Farming", "Traditional Tools", "Weather Prediction", "Post-Harvest Processing", "Other" ] def get_languages() -> List[str]: """Get list of supported languages for data entry.""" return [ "Hindi", "English", "Bengali", "Telugu", "Marathi", "Tamil", "Gujarati", "Urdu", "Kannada", "Malayalam", "Oriya", "Other" ] # --- Text-to-Speech & Speech-to-Text --- def text_to_speech(text: str, language: str = "English") -> None: """Convert text to speech and play it in Streamlit.""" try: lang_map = { "English": "en", "Hindi": "hi", "Bengali": "bn", "Telugu": "te", "Marathi": "mr", "Tamil": "ta", "Gujarati": "gu", "Urdu": "ur", "Kannada": "kn", "Malayalam": "ml" } lang_code = lang_map.get(language, "en") tts = gTTS(text=text, lang=lang_code, slow=False) with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as fp: tts.save(fp.name) st.audio(fp.name) os.remove(fp.name) # Clean up the temp file except Exception as e: st.error(f"Error in text-to-speech: {str(e)}") def speech_to_text(language: str = "English") -> Optional[str]: """Convert speech from microphone to text.""" lang_map = { "English": "en-IN", "Hindi": "hi-IN", "Bengali": "bn-IN", "Telugu": "te-IN", "Marathi": "mr-IN", "Tamil": "ta-IN", "Gujarati": "gu-IN", "Urdu": "ur-IN", "Kannada": "kn-IN", "Malayalam": "ml-IN" } lang_code = lang_map.get(language, "en-IN") r = sr.Recognizer() try: with sr.Microphone() as source: st.info("Listening... Adjusting for ambient noise.") r.adjust_for_ambient_noise(source, duration=1) audio = r.listen(source) st.info("Recognizing...") text = r.recognize_google(audio, language=lang_code) return text except sr.UnknownValueError: st.error("Could not understand audio.") return None except sr.RequestError as e: st.error(f"Could not request results from speech recognition service; {e}") return None except Exception as e: st.error(f"Mic error: {e}. Ensure microphone is enabled and permissions are granted.") return None # --- Geocoding & Search --- def geocode_location(location_name: str) -> Optional[tuple]: """Get coordinates for a location name.""" try: geolocator = Nominatim(user_agent="farming_wisdom_archive") location = geolocator.geocode(location_name) return (location.latitude, location.longitude) if location else None except Exception as e: st.error(f"Geocoding error: {e}") return None def search_entries(entries, query, language=None, category=None, has_media=False, has_location=False): """Search entries based on query and filters.""" query_lower = query.lower() results = [ e for e in entries if query_lower in e.get('title', '').lower() or query_lower in e.get('description', '').lower() ] if language and language != 'All': results = [e for e in results if e.get('language') == language] if category and category != 'All': results = [e for e in results if e.get('category') == category] if has_media: results = [e for e in results if e.get('image_path') or e.get('audio_path')] if has_location: results = [e for e in results if e.get('latitude') and e.get('longitude')] return results # --- Data Export --- def export_to_jsonl(entries, include_media, include_coords): lines = [] for entry in entries: data = entry.copy() if not include_media: data.pop('image_path', None); data.pop('audio_path', None) if not include_coords: data.pop('latitude', None); data.pop('longitude', None) lines.append(json.dumps(data)) return "\n".join(lines) def export_to_csv(entries, include_media, include_coords): df = pd.DataFrame(entries) if not include_media: df = df.drop(columns=['image_path', 'audio_path'], errors='ignore') if not include_coords: df = df.drop(columns=['latitude', 'longitude'], errors='ignore') return df.to_csv(index=False).encode('utf-8') # --- User Authentication --- def load_user_data() -> Dict: """Load user data from users.json.""" os.makedirs("data_entries", exist_ok=True) users_file = "data_entries/users.json" if not os.path.exists(users_file): return {"users": {}} try: with open(users_file, "r", encoding="utf-8") as f: return json.load(f) except (json.JSONDecodeError, FileNotFoundError): return {"users": {}} def save_user_data(user_data: Dict) -> bool: """Save user data to users.json.""" try: with open("data_entries/users.json", "w", encoding="utf-8") as f: json.dump(user_data, f, indent=4) return True except Exception as e: st.error(f"Error saving user data: {e}") return False def hash_password(password: str) -> bytes: return bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()) def verify_password(password: str, hashed: bytes) -> bool: return bcrypt.checkpw(password.encode('utf-8'), hashed) def register_user(username, email, password, full_name): user_data = load_user_data() if username in user_data['users']: return False for user in user_data['users'].values(): if user['email'] == email: return False hashed_password = hash_password(password).decode('utf-8') user_data['users'][username] = { "email": email, "password": hashed_password, "full_name": full_name, "registration_date": datetime.datetime.now(datetime.timezone.utc).isoformat(), "entries_submitted": 0 } return save_user_data(user_data) def authenticate_user(username, password): user_data = load_user_data() user_info = user_data['users'].get(username) if not user_info: return False return verify_password(password, user_info['password'].encode('utf-8')) def get_user_info(username): return load_user_data()['users'].get(username, {}) def update_user_entry_count(username): user_data = load_user_data() if username in user_data['users']: user_data['users'][username]['entries_submitted'] += 1 save_user_data(user_data) # --- Translation and Language Detection --- def translate_text(text: str, target_lang: str, source_lang: str = "auto") -> str: """Translate text using deep_translator.""" lang_map = { "Hindi": "hi", "English": "en", "Bengali": "bn", "Telugu": "te", "Marathi": "mr", "Tamil": "ta", "Gujarati": "gu", "Urdu": "ur", "Kannada": "kn", "Malayalam": "ml", "Oriya": "or" } target_code = lang_map.get(target_lang, "en") source_code = lang_map.get(source_lang, "auto") if source_lang != "Auto-detect" else "auto" try: return GoogleTranslator(source=source_code, target=target_code).translate(text) except Exception as e: return f"Translation Error: {e}" def detect_language(text: str) -> str: """Detect the language of the text.""" try: code_map_rev = { "hi": "Hindi", "en": "English", "bn": "Bengali", "te": "Telugu", "mr": "Marathi", "ta": "Tamil", "gu": "Gujarati", "ur": "Urdu", "kn": "Kannada", "ml": "Malayalam", "or": "Oriya" } detected_code = GoogleTranslator(source="auto", target="en").detect(text) return code_map_rev.get(detected_code[0], f"Unknown ({detected_code[0]})") except Exception: return "Detection Error"