From a2a45918482afdd4ed6b63ccb186e096f73afebc Mon Sep 17 00:00:00 2001 From: Nurmuhammet Allanov Date: Thu, 21 Aug 2025 18:28:08 +0500 Subject: [PATCH] Add TranslatorModal and PhrasebookModal to ServicesScreen; update localization for new phrases --- app/(tabs)/services.tsx | 17 ++++ components/PhrasebookModal.tsx | 122 +++++++++++++++++++++++ components/TranslatorModal.tsx | 175 +++++++++++++++++++++++++++++++++ locales/tk.json | 5 +- 4 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 components/PhrasebookModal.tsx create mode 100644 components/TranslatorModal.tsx diff --git a/app/(tabs)/services.tsx b/app/(tabs)/services.tsx index 935e98b..487a248 100644 --- a/app/(tabs)/services.tsx +++ b/app/(tabs)/services.tsx @@ -7,12 +7,15 @@ import React, { useState } from 'react'; import CurrencyConverterModal from '@/components/CurrencyConverterModal'; import HotelBusinessCardModal from '@/components/HotelBusinessCardModal'; import LostKeyModal from '@/components/LostKeyModal'; +import TranslatorModal from '@/components/TranslatorModal'; +import PhrasebookModal from '@/components/PhrasebookModal'; export default function ServicesScreen() { const [currencyModalVisible, setCurrencyModalVisible] = useState(false); const [hotelModalVisible, setHotelModalVisible] = useState(false); const [lostKeyModalVisible, setLostKeyModalVisible] = useState(false); const [translatorModalVisible, setTranslatorModalVisible] = useState(false); + const [phrasebookModalVisible, setPhrasebookModalVisible] = useState(false); const services = [ { @@ -39,6 +42,12 @@ export default function ServicesScreen() { icon: , onPress: () => setTranslatorModalVisible(true), }, + { + title: i18n.t('Phrasebook'), + name: 'phrasebook', + icon: , + onPress: () => setPhrasebookModalVisible(true), + }, ]; return ( @@ -64,6 +73,14 @@ export default function ServicesScreen() { visible={lostKeyModalVisible} onClose={() => setLostKeyModalVisible(false)} /> + setTranslatorModalVisible(false)} + /> + setPhrasebookModalVisible(false)} + /> ); } diff --git a/components/PhrasebookModal.tsx b/components/PhrasebookModal.tsx new file mode 100644 index 0000000..dd8e351 --- /dev/null +++ b/components/PhrasebookModal.tsx @@ -0,0 +1,122 @@ +import React from 'react'; +import { Modal, View, Text, StyleSheet, TouchableOpacity, FlatList, SafeAreaView } from 'react-native'; +import { View as ThemedView } from './Themed'; +import { FontAwesome } from '@expo/vector-icons'; +import i18n from '@/i18n'; + +type PhrasebookModalProps = { + visible: boolean; + onClose: () => void; +}; + +const PHRASES = [ + { tk: 'Salam', ar: 'مرحبا (Marhaban)' }, + { tk: 'Hawa', ar: 'نعم (Na\'am)' }, + { tk: 'Ýok', ar: 'لا (La)' }, + { tk: 'Sag boluň', ar: 'شكرا (Shukran)' }, + { tk: 'Minnetdar', ar: 'شكرا جزيلا (Shukran Gazilan)' }, + { tk: 'Haýyş edýärin', ar: 'من فضلك (Min fadlik)' }, + { tk: 'Bagyşlaň', ar: 'آسف (Asif)' }, + { tk: 'Men size nähili kömek edip bilerin?', ar: 'كيف يمكنني مساعدتك؟ (Kayfa yumkinuni musa\'adatuk?)' }, + { tk: 'Bu näçe?', ar: 'بكم هذا؟ (Bikam hadha?)' }, + { tk: 'Hajathana nirede?', ar: 'أين الحمام؟ (Ayna al-hammam?)' }, + { tk: 'Men ýolumy ýitirdim', ar: 'لقد ضللت طريقي (Laqad dalalt tariqi)' }, + { tk: 'Lukman çagyryň', ar: 'اتصل بطبيب (Ittasil bi-tabib)' }, +]; + +const PhrasebookModal = ({ visible, onClose }: PhrasebookModalProps) => { + const renderItem = ({ item }: { item: { tk: string; ar: string } }) => ( + + {item.tk} + {item.ar} + + ); + + return ( + + + + + + + {i18n.t('Phrasebook')} + + index.toString()} + ItemSeparatorComponent={() => } + /> + + + + + ); +}; + +const styles = StyleSheet.create({ + centeredView: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: 'rgba(0,0,0,0.5)', + }, + modalView: { + margin: 20, + backgroundColor: 'white', + borderRadius: 20, + padding: 20, + paddingTop: 40, + alignItems: 'center', + shadowColor: '#000', + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 0.25, + shadowRadius: 4, + elevation: 5, + width: '90%', + height: '80%', + }, + closeButton: { + position: 'absolute', + top: 15, + right: 15, + zIndex: 1, + }, + modalTitle: { + fontSize: 20, + fontWeight: 'bold', + marginBottom: 15, + }, + listContainer: { + width: '100%', + flex: 1, + }, + phraseItem: { + paddingVertical: 15, + paddingHorizontal: 10, + }, + turkmenText: { + fontSize: 16, + marginBottom: 5, + }, + arabicText: { + fontSize: 18, + textAlign: 'right', + color: '#333', + }, + separator: { + height: 1, + backgroundColor: '#eee', + width: '100%', + }, +}); + +export default PhrasebookModal; diff --git a/components/TranslatorModal.tsx b/components/TranslatorModal.tsx new file mode 100644 index 0000000..ab9d98e --- /dev/null +++ b/components/TranslatorModal.tsx @@ -0,0 +1,175 @@ +import React, { useState } from 'react'; +import { Modal, View, Text, TextInput, Button, StyleSheet, TouchableOpacity, KeyboardAvoidingView, Platform, ScrollView } from 'react-native'; +import { View as ThemedView } from './Themed'; +import { FontAwesome } from '@expo/vector-icons'; +import i18n from '@/i18n'; + +type TranslatorModalProps = { + visible: boolean; + onClose: () => void; +}; + +const TranslatorModal = ({ visible, onClose }: TranslatorModalProps) => { + const [turkmenText, setTurkmenText] = useState(''); + const [arabicText, setArabicText] = useState(''); + + const handleTranslate = async () => { + if (turkmenText.trim() === '') { + setArabicText(''); + return; + } + + // --- Placeholder for Translation API call --- + // You would replace this with your actual translation logic. + // For example, calling a function that makes an API request to a translation service. + try { + // const translated = await translateText(turkmenText, 'tk', 'ar'); + // setArabicText(translated); + setArabicText(`(Translation for: ${turkmenText})`); // Mock response + } catch (error) { + console.error("Translation error:", error); + setArabicText("Error: Could not translate."); + } + // ----------------------------------------- + }; + + return ( + + + + + + + + + {i18n.t('Translator')} + + + Türkmençe + + + + + Arapça + + {arabicText} + + + + + {i18n.t('Translate')} + + + + + + + + ); +}; + +const styles = StyleSheet.create({ + keyboardAvoidingContainer: { + flex: 1, + width: '100%', + }, + centeredView: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: 'rgba(0,0,0,0.5)', + width: '100%', + }, + scrollViewContent: { + flexGrow: 1, + justifyContent: 'center', + alignItems: 'center', + width: '100%', + }, + modalView: { + margin: 20, + backgroundColor: 'white', + borderRadius: 20, + padding: 35, + alignItems: 'center', + shadowColor: '#000', + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 0.25, + shadowRadius: 4, + elevation: 5, + width: '100%', + minWidth: '90%', + }, + closeButton: { + position: 'absolute', + top: 10, + right: 10, + }, + modalTitle: { + fontSize: 20, + fontWeight: 'bold', + marginBottom: 20, + }, + inputContainer: { + marginBottom: 15, + minWidth: '90%', + }, + label: { + fontSize: 16, + marginBottom: 5, + color: '#333', + }, + textInput: { + borderWidth: 1, + borderColor: '#ddd', + borderRadius: 10, + padding: 10, + fontSize: 16, + minWidth: '90%', + }, + turkmenInput: { + height: 100, + textAlignVertical: 'top', + }, + arabicOutputContainer: { + height: 100, + borderWidth: 1, + borderColor: '#ddd', + borderRadius: 10, + padding: 10, + backgroundColor: '#f9f9f9', + justifyContent: 'center', + }, + arabicText: { + fontSize: 18, + textAlign: 'right', // For RTL text + }, + translateButton: { + marginTop: 15, + }, + translateButtonText: { + color: '#D4AF37', + fontSize: 18, + fontWeight: '500', + }, +}); + +export default TranslatorModal; diff --git a/locales/tk.json b/locales/tk.json index a2400cf..d59315f 100644 --- a/locales/tk.json +++ b/locales/tk.json @@ -42,5 +42,8 @@ "Money": "Pul", "Hotel": "Otel", "Lost room key": "Açar otagyň içinde galdy", - "Translator": "Perewod" + "Translator": "Perewod", + "Phrasebook": "Sözlük", + "Enter text in Turkmen": "Türkmençe sözleri gir", + "Translate": "Terjime et" }