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)
|