Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -10,9 +10,304 @@ from travel import (
|
|
| 10 |
run_task
|
| 11 |
)
|
| 12 |
|
| 13 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
st.set_page_config(
|
| 15 |
-
page_title="
|
| 16 |
page_icon="✈️",
|
| 17 |
layout="wide",
|
| 18 |
initial_sidebar_state="expanded"
|
|
@@ -233,19 +528,19 @@ st.markdown("""
|
|
| 233 |
# Helper function to download HTML file
|
| 234 |
def get_download_link(text_content, filename):
|
| 235 |
b64 = base64.b64encode(text_content.encode()).decode()
|
| 236 |
-
href = f'<a class="download-link" href="data:text/plain;base64,{b64}" download="{filename}"><i>📥</i>
|
| 237 |
return href
|
| 238 |
|
| 239 |
# Updated helper function to display modern progress with a single UI element
|
| 240 |
def display_modern_progress(current_step, total_steps=6):
|
| 241 |
if 'progress_steps' not in st.session_state:
|
| 242 |
st.session_state.progress_steps = {
|
| 243 |
-
0: {'status': 'pending', 'name':
|
| 244 |
-
1: {'status': 'pending', 'name':
|
| 245 |
-
2: {'status': 'pending', 'name':
|
| 246 |
-
3: {'status': 'pending', 'name':
|
| 247 |
-
4: {'status': 'pending', 'name':
|
| 248 |
-
5: {'status': 'pending', 'name':
|
| 249 |
}
|
| 250 |
|
| 251 |
# Update the current step status
|
|
@@ -416,13 +711,13 @@ if 'form_submitted' not in st.session_state:
|
|
| 416 |
st.session_state.form_submitted = False
|
| 417 |
|
| 418 |
# Modern animated header
|
| 419 |
-
st.markdown('''
|
| 420 |
<div class="animate-in" style="text-align: center;">
|
| 421 |
<div style="margin-bottom: 20px;">
|
| 422 |
<img src="https://img.icons8.com/fluency/96/travel-card.png" width="90"
|
| 423 |
style="filter: drop-shadow(0 4px 8px rgba(0,0,0,0.1));">
|
| 424 |
</div>
|
| 425 |
-
<h1 class="main-header">
|
| 426 |
<p style="font-size: 1.2rem; color: #6c757d; margin-bottom: 25px;">
|
| 427 |
✨ Create your personalized AI-powered travel itinerary in minutes! ✨
|
| 428 |
</p>
|
|
@@ -447,7 +742,7 @@ with st.sidebar:
|
|
| 447 |
|
| 448 |
# About section with modern container
|
| 449 |
st.markdown('<div class="modern-card">', unsafe_allow_html=True)
|
| 450 |
-
st.markdown("### 🌟
|
| 451 |
st.info(
|
| 452 |
"This AI-powered tool creates a personalized travel itinerary based on your preferences. "
|
| 453 |
"Fill in the form and let our specialized travel agents plan your perfect trip!"
|
|
@@ -456,7 +751,7 @@ with st.sidebar:
|
|
| 456 |
|
| 457 |
# How it works with steps and icons
|
| 458 |
st.markdown('<div class="modern-card">', unsafe_allow_html=True)
|
| 459 |
-
st.markdown("### 🔍
|
| 460 |
st.markdown("""
|
| 461 |
<ol style="padding-left: 25px;">
|
| 462 |
<li><b>🖊️ Enter</b> your travel details</li>
|
|
@@ -487,7 +782,7 @@ with st.sidebar:
|
|
| 487 |
if not st.session_state.generation_complete:
|
| 488 |
# Sleek form with minimal design
|
| 489 |
st.markdown('<div class="modern-card animate-in">', unsafe_allow_html=True)
|
| 490 |
-
st.markdown("<h3 style='font-weight: 600; color: var(--primary-dark); display: flex; align-items: center; gap: 10px;'><span style='font-size: 20px;'>✈️</span>
|
| 491 |
|
| 492 |
# Minimalist description
|
| 493 |
st.markdown("""
|
|
@@ -500,14 +795,13 @@ if not st.session_state.generation_complete:
|
|
| 500 |
|
| 501 |
with col1:
|
| 502 |
st.markdown('<p style="font-weight: 500; color: var(--primary); font-size: 14px; margin-bottom: 12px;">Trip Details</p>', unsafe_allow_html=True)
|
| 503 |
-
origin = st.text_input("
|
| 504 |
-
destination = st.text_input("
|
| 505 |
|
| 506 |
# Minimalist date picker
|
| 507 |
st.markdown('<p style="margin-bottom: 5px; font-size: 14px;">Travel Dates</p>', unsafe_allow_html=True)
|
| 508 |
start_date = st.date_input("Start Date", min_value=datetime.now(), label_visibility="collapsed")
|
| 509 |
-
|
| 510 |
-
duration = st.slider("Duration (days)", min_value=1, max_value=30, value=7)
|
| 511 |
end_date = start_date + timedelta(days=duration-1)
|
| 512 |
st.markdown(f'<p style="font-size: 13px; color: var(--text-muted); margin-top: 5px;">{start_date.strftime("%b %d")} - {end_date.strftime("%b %d, %Y")}</p>', unsafe_allow_html=True)
|
| 513 |
|
|
@@ -531,7 +825,7 @@ if not st.session_state.generation_complete:
|
|
| 531 |
placeholder="Dietary restrictions, accessibility needs...")
|
| 532 |
|
| 533 |
# Submit button with enhanced styling
|
| 534 |
-
submit_button = st.form_submit_button("
|
| 535 |
|
| 536 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 537 |
|
|
@@ -633,19 +927,19 @@ Special Requirements: {user_input['special_requirements']}
|
|
| 633 |
|
| 634 |
# Show request details in the details tab
|
| 635 |
with details_tab:
|
| 636 |
-
st.markdown("####
|
| 637 |
-
st.markdown(f"**
|
| 638 |
-
st.markdown(f"**
|
| 639 |
-
st.markdown(f"**
|
| 640 |
-
st.markdown(f"**
|
| 641 |
-
st.markdown(f"**
|
| 642 |
if user_input['preferences']:
|
| 643 |
st.markdown(f"**Interests:** {user_input['preferences']}")
|
| 644 |
if user_input['special_requirements']:
|
| 645 |
st.markdown(f"**Special Requirements:** {user_input['special_requirements']}")
|
| 646 |
|
| 647 |
with progress_tab:
|
| 648 |
-
|
| 649 |
if 'progress_placeholder' not in st.session_state:
|
| 650 |
st.session_state.progress_placeholder = st.empty()
|
| 651 |
with st.session_state.progress_placeholder.container():
|
|
@@ -660,7 +954,7 @@ Special Requirements: {user_input['special_requirements']}
|
|
| 660 |
output_container = st.container()
|
| 661 |
with output_container:
|
| 662 |
st.markdown('<div class="modern-card">', unsafe_allow_html=True)
|
| 663 |
-
st.markdown("### 🌟
|
| 664 |
st.info("Our AI agents will show their work here as they create your itinerary")
|
| 665 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 666 |
|
|
@@ -890,7 +1184,7 @@ if st.session_state.generation_complete:
|
|
| 890 |
st.markdown('<div class="modern-card">', unsafe_allow_html=True)
|
| 891 |
|
| 892 |
# Modern tabs for different views
|
| 893 |
-
itinerary_tab, details_tab, download_tab = st.tabs(["🗒️
|
| 894 |
|
| 895 |
with itinerary_tab:
|
| 896 |
# Preview the itinerary as text
|
|
@@ -923,13 +1217,11 @@ if st.session_state.generation_complete:
|
|
| 923 |
st.markdown("### 🍽️ Dining Recommendations")
|
| 924 |
st.markdown(st.session_state.results["dining_info"])
|
| 925 |
|
| 926 |
-
|
| 927 |
-
|
| 928 |
with download_tab:
|
| 929 |
col1, col2 = st.columns([2, 1])
|
| 930 |
|
| 931 |
with col1:
|
| 932 |
-
st.markdown("###
|
| 933 |
st.markdown("Download your personalized travel plan to access it offline or share with your travel companions.")
|
| 934 |
|
| 935 |
# Display stylized download button
|
|
@@ -948,18 +1240,18 @@ if st.session_state.generation_complete:
|
|
| 948 |
st.markdown("</div>", unsafe_allow_html=True)
|
| 949 |
|
| 950 |
# Share options
|
| 951 |
-
st.markdown("###
|
| 952 |
st.markdown("*Coming soon: Email your itinerary or share via social media.*")
|
| 953 |
|
| 954 |
with col2:
|
| 955 |
# QR code placeholder for future implementation
|
| 956 |
-
st.markdown("###
|
| 957 |
st.markdown("*Coming soon: QR code for easy access on your phone*")
|
| 958 |
|
| 959 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 960 |
|
| 961 |
# Add a reset button to create a new itinerary
|
| 962 |
-
if st.button("
|
| 963 |
# Reset the session state
|
| 964 |
st.session_state.generated_itinerary = None
|
| 965 |
st.session_state.generation_complete = False
|
|
@@ -969,9 +1261,9 @@ if st.session_state.generation_complete:
|
|
| 969 |
st.experimental_rerun()
|
| 970 |
|
| 971 |
# Footer for the app
|
| 972 |
-
st.markdown("""
|
| 973 |
<div style="margin-top: 50px; text-align: center; padding: 20px; color: #6c757d; font-size: 0.8rem;">
|
| 974 |
-
<p>
|
| 975 |
</div>
|
| 976 |
""", unsafe_allow_html=True)
|
| 977 |
|
|
@@ -1007,4 +1299,4 @@ st.markdown("""
|
|
| 1007 |
<p>Built with ❤️ for You</p>
|
| 1008 |
</div>
|
| 1009 |
</div>
|
| 1010 |
-
""", unsafe_allow_html=True)
|
|
|
|
| 10 |
run_task
|
| 11 |
)
|
| 12 |
|
| 13 |
+
# ------------------------------------------
|
| 14 |
+
# 다국어 지원을 위한 번역 사전 및 헬퍼 함수
|
| 15 |
+
# ------------------------------------------
|
| 16 |
+
translations = {
|
| 17 |
+
"en": {
|
| 18 |
+
"page_title": "Globetrotter AI: Your AI Agent for Travelling",
|
| 19 |
+
"header": "Globetrotter AI: Your AI Agent for Travelling",
|
| 20 |
+
"create_itinerary": "Create Your Itinerary",
|
| 21 |
+
"trip_details": "Trip Details",
|
| 22 |
+
"origin": "Origin",
|
| 23 |
+
"destination": "Destination",
|
| 24 |
+
"travel_dates": "Travel Dates",
|
| 25 |
+
"duration": "Duration (days)",
|
| 26 |
+
"preferences": "Preferences",
|
| 27 |
+
"additional_preferences": "Additional Preferences",
|
| 28 |
+
"interests": "Interests",
|
| 29 |
+
"special_requirements": "Special Requirements",
|
| 30 |
+
"submit": "🚀 Create My Personal Travel Itinerary",
|
| 31 |
+
"request_details": "Your Travel Request",
|
| 32 |
+
"from": "From",
|
| 33 |
+
"when": "When",
|
| 34 |
+
"budget": "Budget",
|
| 35 |
+
"travel_style": "Travel Style",
|
| 36 |
+
"live_agent_outputs": "Live Agent Outputs",
|
| 37 |
+
"full_itinerary": "Full Itinerary",
|
| 38 |
+
"details": "Details",
|
| 39 |
+
"download_share": "Download & Share",
|
| 40 |
+
"save_itinerary": "Save Your Itinerary",
|
| 41 |
+
"plan_another_trip": "🔄 Plan Another Trip",
|
| 42 |
+
"about": "About",
|
| 43 |
+
"how_it_works": "How it works",
|
| 44 |
+
"travel_agents": "Travel Agents",
|
| 45 |
+
"share_itinerary": "Share Your Itinerary",
|
| 46 |
+
"save_for_mobile": "Save for Mobile",
|
| 47 |
+
"built_with": "Built with ❤️ for you"
|
| 48 |
+
},
|
| 49 |
+
"ko": {
|
| 50 |
+
"page_title": "Globetrotter AI: 당신의 여행을 위한 AI 에이전트",
|
| 51 |
+
"header": "Globetrotter AI: 당신의 여행을 위한 AI 에이전트",
|
| 52 |
+
"create_itinerary": "여행 일정 생성",
|
| 53 |
+
"trip_details": "여행 세부 정보",
|
| 54 |
+
"origin": "출발지",
|
| 55 |
+
"destination": "목적지",
|
| 56 |
+
"travel_dates": "여행 날짜",
|
| 57 |
+
"duration": "기간 (일수)",
|
| 58 |
+
"preferences": "선호사항",
|
| 59 |
+
"additional_preferences": "추가 선호사항",
|
| 60 |
+
"interests": "관심사",
|
| 61 |
+
"special_requirements": "특별 요구사항",
|
| 62 |
+
"submit": "🚀 나만의 여행 일정 생성",
|
| 63 |
+
"request_details": "여행 요청 정보",
|
| 64 |
+
"from": "출발지",
|
| 65 |
+
"when": "여행 기간",
|
| 66 |
+
"budget": "예산",
|
| 67 |
+
"travel_style": "여행 스타일",
|
| 68 |
+
"live_agent_outputs": "실시간 에이전트 결과",
|
| 69 |
+
"full_itinerary": "전체 일정",
|
| 70 |
+
"details": "세부사항",
|
| 71 |
+
"download_share": "다운로드 및 공유",
|
| 72 |
+
"save_itinerary": "일정 저장",
|
| 73 |
+
"plan_another_trip": "🔄 다른 여행 계획",
|
| 74 |
+
"about": "소개",
|
| 75 |
+
"how_it_works": "작동 방식",
|
| 76 |
+
"travel_agents": "여행 에이전트",
|
| 77 |
+
"share_itinerary": "일정 공유",
|
| 78 |
+
"save_for_mobile": "모바일 저장",
|
| 79 |
+
"built_with": "당신을 위해 ❤️ 만들어졌습니다"
|
| 80 |
+
},
|
| 81 |
+
"ja": {
|
| 82 |
+
"page_title": "Globetrotter AI: あなたの旅行のためのAIエージェント",
|
| 83 |
+
"header": "Globetrotter AI: あなたの旅行のためのAIエージェント",
|
| 84 |
+
"create_itinerary": "旅行プラン作成",
|
| 85 |
+
"trip_details": "旅行詳細",
|
| 86 |
+
"origin": "出発地",
|
| 87 |
+
"destination": "目的地",
|
| 88 |
+
"travel_dates": "旅行日程",
|
| 89 |
+
"duration": "期間(日数)",
|
| 90 |
+
"preferences": "好み",
|
| 91 |
+
"additional_preferences": "追加の好み",
|
| 92 |
+
"interests": "興味",
|
| 93 |
+
"special_requirements": "特別な要件",
|
| 94 |
+
"submit": "🚀 私のための旅行プラン作成",
|
| 95 |
+
"request_details": "旅行リクエスト",
|
| 96 |
+
"from": "出発地",
|
| 97 |
+
"when": "旅行期間",
|
| 98 |
+
"budget": "予算",
|
| 99 |
+
"travel_style": "旅行スタイル",
|
| 100 |
+
"live_agent_outputs": "リアルタイムエージェント出力",
|
| 101 |
+
"full_itinerary": "全行程",
|
| 102 |
+
"details": "詳細",
|
| 103 |
+
"download_share": "ダウンロードと共有",
|
| 104 |
+
"save_itinerary": "旅行プランを保存",
|
| 105 |
+
"plan_another_trip": "🔄 他の旅行を計画",
|
| 106 |
+
"about": "概要",
|
| 107 |
+
"how_it_works": "使い方",
|
| 108 |
+
"travel_agents": "旅行エージェント",
|
| 109 |
+
"share_itinerary": "旅行プランを共有",
|
| 110 |
+
"save_for_mobile": "モバイル保存",
|
| 111 |
+
"built_with": "愛を込めて作られました"
|
| 112 |
+
},
|
| 113 |
+
"zh": {
|
| 114 |
+
"page_title": "Globetrotter AI:您的旅行 AI 代理",
|
| 115 |
+
"header": "Globetrotter AI:您的旅行 AI 代理",
|
| 116 |
+
"create_itinerary": "创建您的行程",
|
| 117 |
+
"trip_details": "旅行详情",
|
| 118 |
+
"origin": "出发地",
|
| 119 |
+
"destination": "目的地",
|
| 120 |
+
"travel_dates": "旅行日期",
|
| 121 |
+
"duration": "天数",
|
| 122 |
+
"preferences": "偏好",
|
| 123 |
+
"additional_preferences": "其他偏好",
|
| 124 |
+
"interests": "兴趣",
|
| 125 |
+
"special_requirements": "特殊需求",
|
| 126 |
+
"submit": "🚀 创建我的个性化行程",
|
| 127 |
+
"request_details": "您的旅行请求",
|
| 128 |
+
"from": "出发地",
|
| 129 |
+
"when": "旅行时间",
|
| 130 |
+
"budget": "预算",
|
| 131 |
+
"travel_style": "旅行风格",
|
| 132 |
+
"live_agent_outputs": "实时代理输出",
|
| 133 |
+
"full_itinerary": "完整行程",
|
| 134 |
+
"details": "详情",
|
| 135 |
+
"download_share": "下载与分享",
|
| 136 |
+
"save_itinerary": "保存行程",
|
| 137 |
+
"plan_another_trip": "🔄 计划另一趟旅行",
|
| 138 |
+
"about": "关于",
|
| 139 |
+
"how_it_works": "工作原理",
|
| 140 |
+
"travel_agents": "旅行代理",
|
| 141 |
+
"share_itinerary": "分享行程",
|
| 142 |
+
"save_for_mobile": "保存到手机",
|
| 143 |
+
"built_with": "用❤️为您制作"
|
| 144 |
+
},
|
| 145 |
+
"es": {
|
| 146 |
+
"page_title": "Globetrotter AI: Tu Agente de IA para Viajar",
|
| 147 |
+
"header": "Globetrotter AI: Tu Agente de IA para Viajar",
|
| 148 |
+
"create_itinerary": "Crea Tu Itinerario",
|
| 149 |
+
"trip_details": "Detalles del Viaje",
|
| 150 |
+
"origin": "Origen",
|
| 151 |
+
"destination": "Destino",
|
| 152 |
+
"travel_dates": "Fechas del Viaje",
|
| 153 |
+
"duration": "Duración (días)",
|
| 154 |
+
"preferences": "Preferencias",
|
| 155 |
+
"additional_preferences": "Preferencias Adicionales",
|
| 156 |
+
"interests": "Intereses",
|
| 157 |
+
"special_requirements": "Requisitos Especiales",
|
| 158 |
+
"submit": "🚀 Crea Mi Itinerario Personalizado",
|
| 159 |
+
"request_details": "Tu Solicitud de Viaje",
|
| 160 |
+
"from": "Desde",
|
| 161 |
+
"when": "Cuándo",
|
| 162 |
+
"budget": "Presupuesto",
|
| 163 |
+
"travel_style": "Estilo de Viaje",
|
| 164 |
+
"live_agent_outputs": "Salidas en Vivo del Agente",
|
| 165 |
+
"full_itinerary": "Itinerario Completo",
|
| 166 |
+
"details": "Detalles",
|
| 167 |
+
"download_share": "Descargar y Compartir",
|
| 168 |
+
"save_itinerary": "Guardar Itinerario",
|
| 169 |
+
"plan_another_trip": "🔄 Planear Otro Viaje",
|
| 170 |
+
"about": "Acerca de",
|
| 171 |
+
"how_it_works": "Cómo Funciona",
|
| 172 |
+
"travel_agents": "Agentes de Viaje",
|
| 173 |
+
"share_itinerary": "Compartir Itinerario",
|
| 174 |
+
"save_for_mobile": "Guardar para Móvil",
|
| 175 |
+
"built_with": "Hecho con ❤️ para ti"
|
| 176 |
+
},
|
| 177 |
+
"fr": {
|
| 178 |
+
"page_title": "Globetrotter AI : Votre Agent IA pour Voyager",
|
| 179 |
+
"header": "Globetrotter AI : Votre Agent IA pour Voyager",
|
| 180 |
+
"create_itinerary": "Créez Votre Itinéraire",
|
| 181 |
+
"trip_details": "Détails du Voyage",
|
| 182 |
+
"origin": "Origine",
|
| 183 |
+
"destination": "Destination",
|
| 184 |
+
"travel_dates": "Dates du Voyage",
|
| 185 |
+
"duration": "Durée (jours)",
|
| 186 |
+
"preferences": "Préférences",
|
| 187 |
+
"additional_preferences": "Préférences Supplémentaires",
|
| 188 |
+
"interests": "Centres d'intérêt",
|
| 189 |
+
"special_requirements": "Exigences Spéciales",
|
| 190 |
+
"submit": "🚀 Créez Mon Itinéraire Personnalisé",
|
| 191 |
+
"request_details": "Votre Demande de Voyage",
|
| 192 |
+
"from": "De",
|
| 193 |
+
"when": "Quand",
|
| 194 |
+
"budget": "Budget",
|
| 195 |
+
"travel_style": "Style de Voyage",
|
| 196 |
+
"live_agent_outputs": "Résultats en Direct de l'Agent",
|
| 197 |
+
"full_itinerary": "Itinéraire Complet",
|
| 198 |
+
"details": "Détails",
|
| 199 |
+
"download_share": "Télécharger et Partager",
|
| 200 |
+
"save_itinerary": "Enregistrer l'Itinéraire",
|
| 201 |
+
"plan_another_trip": "🔄 Planifier un Autre Voyage",
|
| 202 |
+
"about": "À Propos",
|
| 203 |
+
"how_it_works": "Fonctionnement",
|
| 204 |
+
"travel_agents": "Agents de Voyage",
|
| 205 |
+
"share_itinerary": "Partager l'Itinéraire",
|
| 206 |
+
"save_for_mobile": "Enregistrer pour Mobile",
|
| 207 |
+
"built_with": "Conçu avec ❤️ pour vous"
|
| 208 |
+
},
|
| 209 |
+
"de": {
|
| 210 |
+
"page_title": "Globetrotter AI: Ihr KI-Reiseassistent",
|
| 211 |
+
"header": "Globetrotter AI: Ihr KI-Reiseassistent",
|
| 212 |
+
"create_itinerary": "Erstellen Sie Ihre Reiseroute",
|
| 213 |
+
"trip_details": "Reisedetails",
|
| 214 |
+
"origin": "Abfahrtsort",
|
| 215 |
+
"destination": "Zielort",
|
| 216 |
+
"travel_dates": "Reisedaten",
|
| 217 |
+
"duration": "Dauer (Tage)",
|
| 218 |
+
"preferences": "Vorlieben",
|
| 219 |
+
"additional_preferences": "Zusätzliche Vorlieben",
|
| 220 |
+
"interests": "Interessen",
|
| 221 |
+
"special_requirements": "Besondere Anforderungen",
|
| 222 |
+
"submit": "🚀 Erstellen Sie meine personalisierte Reiseroute",
|
| 223 |
+
"request_details": "Ihre Reiseanfrage",
|
| 224 |
+
"from": "Von",
|
| 225 |
+
"when": "Wann",
|
| 226 |
+
"budget": "Budget",
|
| 227 |
+
"travel_style": "Reisestil",
|
| 228 |
+
"live_agent_outputs": "Live Agent Ausgaben",
|
| 229 |
+
"full_itinerary": "Komplette Reiseroute",
|
| 230 |
+
"details": "Details",
|
| 231 |
+
"download_share": "Herunterladen & Teilen",
|
| 232 |
+
"save_itinerary": "Reiseroute speichern",
|
| 233 |
+
"plan_another_trip": "🔄 Plane eine weitere Reise",
|
| 234 |
+
"about": "Über",
|
| 235 |
+
"how_it_works": "Wie es funktioniert",
|
| 236 |
+
"travel_agents": "Reiseassistenten",
|
| 237 |
+
"share_itinerary": "Reiseroute teilen",
|
| 238 |
+
"save_for_mobile": "Für Mobilgeräte speichern",
|
| 239 |
+
"built_with": "Mit ❤️ für Sie gebaut"
|
| 240 |
+
},
|
| 241 |
+
"ar": {
|
| 242 |
+
"page_title": "Globetrotter AI: وكيل السفر الذكي الخاص بك",
|
| 243 |
+
"header": "Globetrotter AI: وكيل السفر الذكي الخاص بك",
|
| 244 |
+
"create_itinerary": "إنشاء خط سير الرحلة",
|
| 245 |
+
"trip_details": "تفاصيل الرحلة",
|
| 246 |
+
"origin": "المغادرة من",
|
| 247 |
+
"destination": "الوجهة",
|
| 248 |
+
"travel_dates": "تواريخ السفر",
|
| 249 |
+
"duration": "المدة (بالأيام)",
|
| 250 |
+
"preferences": "التفضيلات",
|
| 251 |
+
"additional_preferences": "تفضيلات إضافية",
|
| 252 |
+
"interests": "الاهتمامات",
|
| 253 |
+
"special_requirements": "المتطلبات الخاصة",
|
| 254 |
+
"submit": "🚀 إنشاء خط سير الرحلة الشخصي",
|
| 255 |
+
"request_details": "طلب السفر الخاص بك",
|
| 256 |
+
"from": "من",
|
| 257 |
+
"when": "متى",
|
| 258 |
+
"budget": "الميزانية",
|
| 259 |
+
"travel_style": "أسلوب السفر",
|
| 260 |
+
"live_agent_outputs": "مخرجات الوكيل المباشرة",
|
| 261 |
+
"full_itinerary": "خط سير الرحلة الكامل",
|
| 262 |
+
"details": "التفاصيل",
|
| 263 |
+
"download_share": "تنزيل ومشاركة",
|
| 264 |
+
"save_itinerary": "حفظ خط سير الرحلة",
|
| 265 |
+
"plan_another_trip": "🔄 خطط لرحلة أخرى",
|
| 266 |
+
"about": "حول",
|
| 267 |
+
"how_it_works": "كيف يعمل",
|
| 268 |
+
"travel_agents": "وكلاء السفر",
|
| 269 |
+
"share_itinerary": "شارك خط سير الرحلة",
|
| 270 |
+
"save_for_mobile": "حفظ للهاتف المحمول",
|
| 271 |
+
"built_with": "مصنوع بحب من أجلك"
|
| 272 |
+
}
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
# 번역 헬퍼 함수: 선택된 언어에 따라 키 값을 반환합니다.
|
| 276 |
+
def t(key):
|
| 277 |
+
lang = st.session_state.get("selected_language", "en")
|
| 278 |
+
return translations[lang].get(key, key)
|
| 279 |
+
|
| 280 |
+
# ---------------------------
|
| 281 |
+
# 세션 초기화
|
| 282 |
+
# ---------------------------
|
| 283 |
+
if 'selected_language' not in st.session_state:
|
| 284 |
+
st.session_state.selected_language = "en" # 기본은 영어
|
| 285 |
+
|
| 286 |
+
# ------------------------------------------
|
| 287 |
+
# 사이드바에 언어 선택 위젯 추가
|
| 288 |
+
# ------------------------------------------
|
| 289 |
+
with st.sidebar:
|
| 290 |
+
language = st.selectbox(
|
| 291 |
+
"Language / 언어 / 言語 / 语言 / Idioma / Langue / Sprache / اللغة",
|
| 292 |
+
["English", "한국어", "日本語", "中文", "Español", "Français", "Deutsch", "العربية"]
|
| 293 |
+
)
|
| 294 |
+
lang_map = {
|
| 295 |
+
"English": "en",
|
| 296 |
+
"한국어": "ko",
|
| 297 |
+
"日本語": "ja",
|
| 298 |
+
"中文": "zh",
|
| 299 |
+
"Español": "es",
|
| 300 |
+
"Français": "fr",
|
| 301 |
+
"Deutsch": "de",
|
| 302 |
+
"العربية": "ar"
|
| 303 |
+
}
|
| 304 |
+
st.session_state.selected_language = lang_map.get(language, "en")
|
| 305 |
+
|
| 306 |
+
# ------------------------------------------
|
| 307 |
+
# 페이지 설정 (번역 적용)
|
| 308 |
+
# ------------------------------------------
|
| 309 |
st.set_page_config(
|
| 310 |
+
page_title=t("page_title"),
|
| 311 |
page_icon="✈️",
|
| 312 |
layout="wide",
|
| 313 |
initial_sidebar_state="expanded"
|
|
|
|
| 528 |
# Helper function to download HTML file
|
| 529 |
def get_download_link(text_content, filename):
|
| 530 |
b64 = base64.b64encode(text_content.encode()).decode()
|
| 531 |
+
href = f'<a class="download-link" href="data:text/plain;base64,{b64}" download="{filename}"><i>📥</i> {t("save_itinerary")}</a>'
|
| 532 |
return href
|
| 533 |
|
| 534 |
# Updated helper function to display modern progress with a single UI element
|
| 535 |
def display_modern_progress(current_step, total_steps=6):
|
| 536 |
if 'progress_steps' not in st.session_state:
|
| 537 |
st.session_state.progress_steps = {
|
| 538 |
+
0: {'status': 'pending', 'name': t("trip_details")},
|
| 539 |
+
1: {'status': 'pending', 'name': t("about")}, # Accommodation
|
| 540 |
+
2: {'status': 'pending', 'name': t("travel_style")}, # Transportation
|
| 541 |
+
3: {'status': 'pending', 'name': t("live_agent_outputs")}, # Activities
|
| 542 |
+
4: {'status': 'pending', 'name': t("download_share")}, # Dining
|
| 543 |
+
5: {'status': 'pending', 'name': t("full_itinerary")}
|
| 544 |
}
|
| 545 |
|
| 546 |
# Update the current step status
|
|
|
|
| 711 |
st.session_state.form_submitted = False
|
| 712 |
|
| 713 |
# Modern animated header
|
| 714 |
+
st.markdown(f'''
|
| 715 |
<div class="animate-in" style="text-align: center;">
|
| 716 |
<div style="margin-bottom: 20px;">
|
| 717 |
<img src="https://img.icons8.com/fluency/96/travel-card.png" width="90"
|
| 718 |
style="filter: drop-shadow(0 4px 8px rgba(0,0,0,0.1));">
|
| 719 |
</div>
|
| 720 |
+
<h1 class="main-header">{t("header")}</h1>
|
| 721 |
<p style="font-size: 1.2rem; color: #6c757d; margin-bottom: 25px;">
|
| 722 |
✨ Create your personalized AI-powered travel itinerary in minutes! ✨
|
| 723 |
</p>
|
|
|
|
| 742 |
|
| 743 |
# About section with modern container
|
| 744 |
st.markdown('<div class="modern-card">', unsafe_allow_html=True)
|
| 745 |
+
st.markdown(f"### 🌟 {t('about')}")
|
| 746 |
st.info(
|
| 747 |
"This AI-powered tool creates a personalized travel itinerary based on your preferences. "
|
| 748 |
"Fill in the form and let our specialized travel agents plan your perfect trip!"
|
|
|
|
| 751 |
|
| 752 |
# How it works with steps and icons
|
| 753 |
st.markdown('<div class="modern-card">', unsafe_allow_html=True)
|
| 754 |
+
st.markdown(f"### 🔍 {t('how_it_works')}")
|
| 755 |
st.markdown("""
|
| 756 |
<ol style="padding-left: 25px;">
|
| 757 |
<li><b>🖊️ Enter</b> your travel details</li>
|
|
|
|
| 782 |
if not st.session_state.generation_complete:
|
| 783 |
# Sleek form with minimal design
|
| 784 |
st.markdown('<div class="modern-card animate-in">', unsafe_allow_html=True)
|
| 785 |
+
st.markdown(f"<h3 style='font-weight: 600; color: var(--primary-dark); display: flex; align-items: center; gap: 10px;'><span style='font-size: 20px;'>✈️</span> {t('create_itinerary')}</h3>", unsafe_allow_html=True)
|
| 786 |
|
| 787 |
# Minimalist description
|
| 788 |
st.markdown("""
|
|
|
|
| 795 |
|
| 796 |
with col1:
|
| 797 |
st.markdown('<p style="font-weight: 500; color: var(--primary); font-size: 14px; margin-bottom: 12px;">Trip Details</p>', unsafe_allow_html=True)
|
| 798 |
+
origin = st.text_input(t("origin"), placeholder="e.g., New York, USA")
|
| 799 |
+
destination = st.text_input(t("destination"), placeholder="e.g., Paris, France")
|
| 800 |
|
| 801 |
# Minimalist date picker
|
| 802 |
st.markdown('<p style="margin-bottom: 5px; font-size: 14px;">Travel Dates</p>', unsafe_allow_html=True)
|
| 803 |
start_date = st.date_input("Start Date", min_value=datetime.now(), label_visibility="collapsed")
|
| 804 |
+
duration = st.slider(t("duration"), min_value=1, max_value=30, value=7)
|
|
|
|
| 805 |
end_date = start_date + timedelta(days=duration-1)
|
| 806 |
st.markdown(f'<p style="font-size: 13px; color: var(--text-muted); margin-top: 5px;">{start_date.strftime("%b %d")} - {end_date.strftime("%b %d, %Y")}</p>', unsafe_allow_html=True)
|
| 807 |
|
|
|
|
| 825 |
placeholder="Dietary restrictions, accessibility needs...")
|
| 826 |
|
| 827 |
# Submit button with enhanced styling
|
| 828 |
+
submit_button = st.form_submit_button(t("submit"))
|
| 829 |
|
| 830 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 831 |
|
|
|
|
| 927 |
|
| 928 |
# Show request details in the details tab
|
| 929 |
with details_tab:
|
| 930 |
+
st.markdown("#### " + t("request_details"))
|
| 931 |
+
st.markdown(f"**{t('destination')}:** {user_input['destination']}")
|
| 932 |
+
st.markdown(f"**{t('from')}:** {user_input['origin']}")
|
| 933 |
+
st.markdown(f"**{t('when')}:** {user_input['travel_dates']} ({user_input['duration']} days)")
|
| 934 |
+
st.markdown(f"**{t('budget')}:** {user_input['budget'].title()}")
|
| 935 |
+
st.markdown(f"**{t('travel_style')}:** {user_input['travel_style']}")
|
| 936 |
if user_input['preferences']:
|
| 937 |
st.markdown(f"**Interests:** {user_input['preferences']}")
|
| 938 |
if user_input['special_requirements']:
|
| 939 |
st.markdown(f"**Special Requirements:** {user_input['special_requirements']}")
|
| 940 |
|
| 941 |
with progress_tab:
|
| 942 |
+
# Create a persistent placeholder for progress display to avoid duplication
|
| 943 |
if 'progress_placeholder' not in st.session_state:
|
| 944 |
st.session_state.progress_placeholder = st.empty()
|
| 945 |
with st.session_state.progress_placeholder.container():
|
|
|
|
| 954 |
output_container = st.container()
|
| 955 |
with output_container:
|
| 956 |
st.markdown('<div class="modern-card">', unsafe_allow_html=True)
|
| 957 |
+
st.markdown("### 🌟 " + t("live_agent_outputs"))
|
| 958 |
st.info("Our AI agents will show their work here as they create your itinerary")
|
| 959 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 960 |
|
|
|
|
| 1184 |
st.markdown('<div class="modern-card">', unsafe_allow_html=True)
|
| 1185 |
|
| 1186 |
# Modern tabs for different views
|
| 1187 |
+
itinerary_tab, details_tab, download_tab = st.tabs([f"🗒️ {t('full_itinerary')}", f"💼 {t('details')}", f"💾 {t('download_share')}"])
|
| 1188 |
|
| 1189 |
with itinerary_tab:
|
| 1190 |
# Preview the itinerary as text
|
|
|
|
| 1217 |
st.markdown("### 🍽️ Dining Recommendations")
|
| 1218 |
st.markdown(st.session_state.results["dining_info"])
|
| 1219 |
|
|
|
|
|
|
|
| 1220 |
with download_tab:
|
| 1221 |
col1, col2 = st.columns([2, 1])
|
| 1222 |
|
| 1223 |
with col1:
|
| 1224 |
+
st.markdown("### " + t("save_itinerary"))
|
| 1225 |
st.markdown("Download your personalized travel plan to access it offline or share with your travel companions.")
|
| 1226 |
|
| 1227 |
# Display stylized download button
|
|
|
|
| 1240 |
st.markdown("</div>", unsafe_allow_html=True)
|
| 1241 |
|
| 1242 |
# Share options
|
| 1243 |
+
st.markdown("### " + t("share_itinerary"))
|
| 1244 |
st.markdown("*Coming soon: Email your itinerary or share via social media.*")
|
| 1245 |
|
| 1246 |
with col2:
|
| 1247 |
# QR code placeholder for future implementation
|
| 1248 |
+
st.markdown("### " + t("save_for_mobile"))
|
| 1249 |
st.markdown("*Coming soon: QR code for easy access on your phone*")
|
| 1250 |
|
| 1251 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 1252 |
|
| 1253 |
# Add a reset button to create a new itinerary
|
| 1254 |
+
if st.button(t("plan_another_trip"), key="reset_button"):
|
| 1255 |
# Reset the session state
|
| 1256 |
st.session_state.generated_itinerary = None
|
| 1257 |
st.session_state.generation_complete = False
|
|
|
|
| 1261 |
st.experimental_rerun()
|
| 1262 |
|
| 1263 |
# Footer for the app
|
| 1264 |
+
st.markdown(f"""
|
| 1265 |
<div style="margin-top: 50px; text-align: center; padding: 20px; color: #6c757d; font-size: 0.8rem;">
|
| 1266 |
+
<p>{t("built_with")}</p>
|
| 1267 |
</div>
|
| 1268 |
""", unsafe_allow_html=True)
|
| 1269 |
|
|
|
|
| 1299 |
<p>Built with ❤️ for You</p>
|
| 1300 |
</div>
|
| 1301 |
</div>
|
| 1302 |
+
""", unsafe_allow_html=True)
|