mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-17 15:32:01 +00:00
Fixed various language translations
This commit is contained in:
parent
e0077244c6
commit
ded57baeab
6 changed files with 737 additions and 541 deletions
|
|
@ -18,6 +18,7 @@ import Animated, {
|
|||
interpolate,
|
||||
Extrapolate,
|
||||
} from 'react-native-reanimated';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useTheme } from '../../contexts/ThemeContext';
|
||||
import { isMDBListEnabled } from '../../screens/MDBListSettingsScreen';
|
||||
import { getAgeRatingColor } from '../../utils/ageRatingColors';
|
||||
|
|
@ -53,6 +54,7 @@ const MetadataDetails: React.FC<MetadataDetailsProps> = ({
|
|||
contentId,
|
||||
loadingMetadata = false,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { currentTheme } = useTheme();
|
||||
const [isFullDescriptionOpen, setIsFullDescriptionOpen] = useState(false);
|
||||
const [isMDBEnabled, setIsMDBEnabled] = useState(false);
|
||||
|
|
@ -407,7 +409,7 @@ const MetadataDetails: React.FC<MetadataDetailsProps> = ({
|
|||
fontSize: isTV ? 16 : isLargeTablet ? 15 : isTablet ? 14 : 14
|
||||
}
|
||||
]}>
|
||||
{isFullDescriptionOpen ? 'Show Less' : 'Show More'}
|
||||
{isFullDescriptionOpen ? t('common.show_less') : t('common.show_more')}
|
||||
</Text>
|
||||
<MaterialIcons
|
||||
name={isFullDescriptionOpen ? "keyboard-arrow-up" : "keyboard-arrow-down"}
|
||||
|
|
@ -533,4 +535,4 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
});
|
||||
|
||||
export default React.memo(MetadataDetails);
|
||||
export default React.memo(MetadataDetails);
|
||||
|
|
@ -469,13 +469,13 @@
|
|||
"profanity": "Profanity",
|
||||
"alcohol": "Alcohol/Drugs",
|
||||
"frightening": "Frightening"
|
||||
},
|
||||
},
|
||||
"severity": {
|
||||
"severe": "Severe",
|
||||
"moderate": "Moderate",
|
||||
"mild": "Mild",
|
||||
"none": "None"
|
||||
}
|
||||
}
|
||||
},
|
||||
"addons": {
|
||||
"title": "Addons",
|
||||
|
|
@ -565,6 +565,43 @@
|
|||
"conflict_msg": "You cannot connect to Simkl while Trakt is connected. Please disconnect Trakt first.",
|
||||
"disclaimer": "Nuvio is not affiliated with Simkl."
|
||||
},
|
||||
"mal_settings": {
|
||||
"title": "MyAnimeList",
|
||||
"connect_title": "Connect MyAnimeList",
|
||||
"connect_desc": "Sync your watch history and manage your anime list.",
|
||||
"sign_in": "Sign In with MyAnimeList",
|
||||
"sign_out": "Disconnect",
|
||||
"sign_out_confirm": "Are you sure you want to disconnect your MyAnimeList account?",
|
||||
"auth_success_title": "Successfully Connected",
|
||||
"auth_success_msg": "Your MyAnimeList account has been connected successfully.",
|
||||
"auth_error_title": "Authentication Error",
|
||||
"auth_error_msg": "Failed to complete authentication with MyAnimeList.",
|
||||
"auth_error_generic": "An error occurred during authentication.",
|
||||
"sync_complete_title": "Sync Complete",
|
||||
"sync_error_title": "Sync Failed",
|
||||
"sync_success_msg": "MyAnimeList data refreshed successfully.",
|
||||
"sync_error_msg": "Failed to refresh MyAnimeList data. Please try again.",
|
||||
"sync_now_button": "Sync Now",
|
||||
"sync_settings_title": "Sync Settings",
|
||||
"profile_id": "ID: {{id}}",
|
||||
"stats_total": "Total",
|
||||
"stats_days": "Days",
|
||||
"stats_mean": "Mean",
|
||||
"status_watching": "Watching",
|
||||
"status_completed": "Completed",
|
||||
"status_on_hold": "On Hold",
|
||||
"status_dropped": "Dropped",
|
||||
"sync_enabled_label": "Enable MyAnimeList Sync",
|
||||
"sync_enabled_desc": "Global switch to enable or disable all MyAnimeList features.",
|
||||
"auto_update_label": "Auto Episode Update",
|
||||
"auto_update_desc": "Automatically update your progress on MyAnimeList when you finish watching an episode (>=90% completion).",
|
||||
"auto_add_label": "Auto Add Anime",
|
||||
"auto_add_desc": "If an anime is not in your MyAnimeList list, it will be added automatically when you start watching.",
|
||||
"auto_library_sync_label": "Auto-Sync to Library",
|
||||
"auto_library_sync_desc": "Automatically add items from your MyAnimeList \"Watching\" list to your Nuvio Library.",
|
||||
"include_nsfw_label": "Include NSFW Content",
|
||||
"include_nsfw_desc": "Allow NSFW entries to be returned when fetching your MyAnimeList list."
|
||||
},
|
||||
"tmdb_settings": {
|
||||
"title": "TMDb Settings",
|
||||
"metadata_enrichment": "Metadata Enrichment",
|
||||
|
|
@ -916,6 +953,46 @@
|
|||
"success_removed": "API key removed successfully",
|
||||
"error_remove": "Failed to remove API key"
|
||||
},
|
||||
"ai_chat": {
|
||||
"title": "AI Chat",
|
||||
"loading_context": "Loading AI context...",
|
||||
"welcome_title": "Ask me anything about",
|
||||
"welcome_description": "I have detailed knowledge about this content and can answer questions about plot, characters, themes, and more.",
|
||||
"try_asking": "Try asking:",
|
||||
"placeholder": "Ask about this content...",
|
||||
"errors": {
|
||||
"load_content_details": "Failed to load content details for AI chat",
|
||||
"generic": "Sorry, I encountered an error. Please try again.",
|
||||
"not_configured": "Please configure your OpenRouter API key in Settings > AI Assistant.",
|
||||
"invalid_api_key": "OpenRouter rejected your API key. Please verify the key in Settings > AI Assistant.",
|
||||
"quota": "OpenRouter quota/credits were rejected for this request. Please check your OpenRouter usage and limits.",
|
||||
"model_unavailable": "The selected OpenRouter model is unavailable. Retry with `openrouter/free` or choose another custom model in Settings > AI Assistant.",
|
||||
"connection": "Failed to connect to AI service. Please check your internet connection, API key, and OpenRouter model availability."
|
||||
},
|
||||
"starters": {
|
||||
"series": {
|
||||
"overall": "What is {{title}} about overall?",
|
||||
"arcs": "Summarize key arcs across all seasons",
|
||||
"rated": "Which episodes are the highest rated and why?",
|
||||
"pivotal": "List pivotal episodes for character development",
|
||||
"themes": "How did themes evolve from Season 1 onward?"
|
||||
},
|
||||
"episode": {
|
||||
"overview": "What happened in this episode of {{showTitle}}?",
|
||||
"plot_points": "Explain the main plot points of \"{{episodeTitle}}\"",
|
||||
"character_development": "What character development occurred in this episode?",
|
||||
"hidden_details": "Are there any hidden details or easter eggs I might have missed?",
|
||||
"story_arc": "How does this episode connect to the overall story arc?"
|
||||
},
|
||||
"movie": {
|
||||
"overview": "What is {{title}} about?",
|
||||
"themes": "Explain the themes in this movie",
|
||||
"ending": "What's the significance of the ending?",
|
||||
"characters": "Tell me about the main characters and their development",
|
||||
"production_facts": "Are there any interesting production facts about this film?"
|
||||
}
|
||||
}
|
||||
},
|
||||
"catalog_settings": {
|
||||
"title": "Catalogs",
|
||||
"layout_phone": "LAYOUT CATALOGSCREEN (PHONE)",
|
||||
|
|
@ -1504,4 +1581,4 @@
|
|||
"no_logs_captured": "No logs captured."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -550,6 +550,43 @@
|
|||
"conflict_msg": "Non puoi connettere Simkl mentre Trakt è connesso. Disconnetti prima Trakt.",
|
||||
"disclaimer": "Nuvio non è affiliato con Simkl."
|
||||
},
|
||||
"mal_settings": {
|
||||
"title": "MyAnimeList",
|
||||
"connect_title": "Connetti MyAnimeList",
|
||||
"connect_desc": "Sincronizza la tua cronologia di visione e gestisci la tua lista anime.",
|
||||
"sign_in": "Accedi con MyAnimeList",
|
||||
"sign_out": "Disconnetti",
|
||||
"sign_out_confirm": "Sei sicuro di voler disconnettere il tuo account MyAnimeList?",
|
||||
"auth_success_title": "Connessione riuscita",
|
||||
"auth_success_msg": "Il tuo account MyAnimeList e stato collegato correttamente.",
|
||||
"auth_error_title": "Errore di autenticazione",
|
||||
"auth_error_msg": "Impossibile completare l'autenticazione con MyAnimeList.",
|
||||
"auth_error_generic": "Si \u00e8 verificato un errore durante l'autenticazione.",
|
||||
"sync_complete_title": "Sincronizzazione completata",
|
||||
"sync_error_title": "Sincronizzazione fallita",
|
||||
"sync_success_msg": "I dati MyAnimeList sono stati aggiornati correttamente.",
|
||||
"sync_error_msg": "Impossibile aggiornare i dati MyAnimeList. Riprova.",
|
||||
"sync_now_button": "Sincronizza ora",
|
||||
"sync_settings_title": "Impostazioni sincronizzazione",
|
||||
"profile_id": "ID: {{id}}",
|
||||
"stats_total": "Totale",
|
||||
"stats_days": "Giorni",
|
||||
"stats_mean": "Media",
|
||||
"status_watching": "In visione",
|
||||
"status_completed": "Completati",
|
||||
"status_on_hold": "In pausa",
|
||||
"status_dropped": "Interrotti",
|
||||
"sync_enabled_label": "Abilita sincronizzazione MyAnimeList",
|
||||
"sync_enabled_desc": "Interruttore globale per abilitare o disabilitare tutte le funzioni MyAnimeList.",
|
||||
"auto_update_label": "Aggiornamento automatico episodi",
|
||||
"auto_update_desc": "Aggiorna automaticamente i tuoi progressi su MyAnimeList quando completi un episodio (>=90% completamento).",
|
||||
"auto_add_label": "Aggiunta automatica anime",
|
||||
"auto_add_desc": "Se un anime non e presente nella tua lista MyAnimeList, verra aggiunto automaticamente quando inizi a guardarlo.",
|
||||
"auto_library_sync_label": "Sincronizzazione automatica con la Libreria",
|
||||
"auto_library_sync_desc": "Aggiunge automaticamente alla Libreria Nuvio gli elementi presenti nella tua lista MyAnimeList \"Watching\".",
|
||||
"include_nsfw_label": "Includi contenuti NSFW",
|
||||
"include_nsfw_desc": "Consenti il recupero di contenuti NSFW quando viene caricata la tua lista MyAnimeList."
|
||||
},
|
||||
"tmdb_settings": {
|
||||
"title": "Impostazioni TMDb",
|
||||
"metadata_enrichment": "Arricchimento metadati",
|
||||
|
|
@ -660,6 +697,7 @@
|
|||
"integrations": "Integrazioni",
|
||||
"playback": "Riproduzione",
|
||||
"backup_restore": "Backup e Ripristino",
|
||||
"backup_restore_desc": "Crea e ripristina backup dell'app",
|
||||
"updates": "Aggiornamenti",
|
||||
"about": "Informazioni",
|
||||
"developer": "Sviluppatore",
|
||||
|
|
@ -690,7 +728,8 @@
|
|||
"media": "MEDIA",
|
||||
"notifications": "NOTIFICHE",
|
||||
"testing": "TEST",
|
||||
"danger_zone": "ZONA PERICOLOSA"
|
||||
"danger_zone": "ZONA PERICOLOSA",
|
||||
"introdb_contribution": "Contributi IntroDB"
|
||||
},
|
||||
"items": {
|
||||
"legal": "Note Legali & Disclaimer",
|
||||
|
|
@ -751,7 +790,9 @@
|
|||
"reset_campaigns": "Ripristina Campagne",
|
||||
"reset_campaigns_desc": "Cancella le impressioni delle campagne",
|
||||
"clear_all_data": "Cancella tutti i dati",
|
||||
"clear_all_data_desc": "Ripristina tutte le impostazioni e i dati memorizzati"
|
||||
"clear_all_data_desc": "Ripristina tutte le impostazioni e i dati memorizzati",
|
||||
"enable_intro_submission": "Abilita l'aggiunta di dati sulle intro",
|
||||
"enable_intro_submission_desc": "Contribuisci alla community IntroDB"
|
||||
},
|
||||
"options": {
|
||||
"horizontal": "Orizzontale",
|
||||
|
|
@ -794,7 +835,7 @@
|
|||
"description": "Aggiorna dal cloud (pull) o usa le impostazioni di questo dispositivo come sorgente principale (push).",
|
||||
"pull_btn": "Pull dal Cloud",
|
||||
"push_btn": "Push dal Dispositivo",
|
||||
"manage_account": "Gestiscci Account",
|
||||
"manage_account": "Gestisci Account",
|
||||
"sign_out": "Disconnetti",
|
||||
"sign_in_up": "Accedi / Registrati"
|
||||
},
|
||||
|
|
@ -900,6 +941,46 @@
|
|||
"success_removed": "Chiave API rimossa con successo",
|
||||
"error_remove": "Impossibile rimuovere la chiave API"
|
||||
},
|
||||
"ai_chat": {
|
||||
"title": "Chat IA",
|
||||
"loading_context": "Caricamento contesto IA...",
|
||||
"welcome_title": "Chiedimi qualsiasi cosa su",
|
||||
"welcome_description": "Ho una conoscenza dettagliata di questo contenuto e posso rispondere a domande su trama, personaggi, temi e altro.",
|
||||
"try_asking": "Prova a chiedere:",
|
||||
"placeholder": "Chiedi qualcosa su questo contenuto...",
|
||||
"errors": {
|
||||
"load_content_details": "Impossibile caricare i dettagli del contenuto per la chat IA",
|
||||
"generic": "Mi dispiace, si \u00e8 verificato un errore. Riprova.",
|
||||
"not_configured": "Configura la tua chiave API OpenRouter in Impostazioni > Assistente IA.",
|
||||
"invalid_api_key": "OpenRouter ha rifiutato la tua chiave API. Verificala in Impostazioni > Assistente IA.",
|
||||
"quota": "OpenRouter ha rifiutato la richiesta per limiti o crediti. Controlla utilizzo e limiti del tuo account.",
|
||||
"model_unavailable": "Il modello OpenRouter selezionato non \u00e8 disponibile. Riprova con `openrouter/free` oppure scegli un altro modello personalizzato in Impostazioni > Assistente IA.",
|
||||
"connection": "Impossibile connettersi al servizio IA. Controlla connessione internet, chiave API e disponibilit\u00e0 del modello OpenRouter."
|
||||
},
|
||||
"starters": {
|
||||
"series": {
|
||||
"overall": "Di cosa parla {{title}} nel complesso?",
|
||||
"arcs": "Riassumi gli archi narrativi principali di tutte le stagioni",
|
||||
"rated": "Quali episodi hanno la valutazione pi\u00f9 alta e perch\u00e9?",
|
||||
"pivotal": "Elenca gli episodi chiave per lo sviluppo dei personaggi",
|
||||
"themes": "Come si evolvono i temi dalla Stagione 1 in poi?"
|
||||
},
|
||||
"episode": {
|
||||
"overview": "Cosa succede in questo episodio di {{showTitle}}?",
|
||||
"plot_points": "Spiegami i punti principali della trama di \"{{episodeTitle}}\"",
|
||||
"character_development": "Quale sviluppo dei personaggi avviene in questo episodio?",
|
||||
"hidden_details": "Ci sono dettagli nascosti o easter egg che potrei essermi perso?",
|
||||
"story_arc": "Come si collega questo episodio all'arco narrativo generale?"
|
||||
},
|
||||
"movie": {
|
||||
"overview": "Di cosa parla {{title}}?",
|
||||
"themes": "Spiegami i temi di questo film",
|
||||
"ending": "Qual \u00e8 il significato del finale?",
|
||||
"characters": "Parlami dei personaggi principali e della loro evoluzione",
|
||||
"production_facts": "Ci sono curiosit\u00e0 interessanti sulla produzione di questo film?"
|
||||
}
|
||||
}
|
||||
},
|
||||
"catalog_settings": {
|
||||
"title": "Cataloghi",
|
||||
"layout_phone": "LAYOUT SCHERMATA CATALOGO (TELEFONO)",
|
||||
|
|
@ -1144,7 +1225,7 @@
|
|||
"sync_desc": "Sincronizza automaticamente le notifiche per tutte le serie nella tua libreria e nella watchlist/collezione di Trakt.",
|
||||
"section_advanced": "Avanzate",
|
||||
"reset_button": "Ripristina tutte le notifiche",
|
||||
"test_button": "Test Notifica (5 sec)",
|
||||
"test_button": "Test Invio Notifica (5 sec)",
|
||||
"test_notification_in": "Notifica tra {{seconds}}s...",
|
||||
"test_notification_text": "La notifica apparirà tra {{seconds}} secondi",
|
||||
"alert_reset_title": "Ripristina Notifiche",
|
||||
|
|
@ -1488,4 +1569,4 @@
|
|||
"no_logs_captured": "Nessun log catturato."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import { useTheme } from '../contexts/ThemeContext';
|
|||
import FastImage from '@d11/react-native-fast-image';
|
||||
import { BlurView as ExpoBlurView } from 'expo-blur';
|
||||
import { LinearGradient } from 'expo-linear-gradient';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
// Lazy-safe community blur import (avoid bundling issues on web)
|
||||
let AndroidBlurView: any = null;
|
||||
if (Platform.OS === 'android') {
|
||||
|
|
@ -46,7 +47,7 @@ if (Platform.OS === 'ios') {
|
|||
}
|
||||
}
|
||||
import { useSafeAreaInsets, SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { aiService, ChatMessage, ContentContext, createMovieContext, createEpisodeContext, createSeriesContext, generateConversationStarters } from '../services/aiService';
|
||||
import { aiService, ChatMessage, ContentContext, createMovieContext, createEpisodeContext, createSeriesContext } from '../services/aiService';
|
||||
import { tmdbService } from '../services/tmdbService';
|
||||
import Markdown from 'react-native-markdown-display';
|
||||
import Animated, {
|
||||
|
|
@ -428,13 +429,17 @@ const SuggestionChip: React.FC<SuggestionChipProps> = React.memo(({ text, onPres
|
|||
}, (prev, next) => prev.text === next.text && prev.onPress === next.onPress && prev.index === next.index);
|
||||
|
||||
const AIChatScreen: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// CustomAlert state
|
||||
const [alertVisible, setAlertVisible] = useState(false);
|
||||
const [alertTitle, setAlertTitle] = useState('');
|
||||
const [alertMessage, setAlertMessage] = useState('');
|
||||
const [alertActions, setAlertActions] = useState<Array<{ label: string; onPress: () => void; style?: object }>>([
|
||||
{ label: 'OK', onPress: () => setAlertVisible(false) },
|
||||
]);
|
||||
const [alertActions, setAlertActions] = useState<Array<{ label: string; onPress: () => void; style?: object }>>([]);
|
||||
|
||||
const defaultAlertActions = useMemo(() => ([
|
||||
{ label: t('common.ok'), onPress: () => setAlertVisible(false) },
|
||||
]), [t]);
|
||||
|
||||
const openAlert = (
|
||||
title: string,
|
||||
|
|
@ -452,7 +457,7 @@ const AIChatScreen: React.FC = () => {
|
|||
}))
|
||||
);
|
||||
} else {
|
||||
setAlertActions([{ label: 'OK', onPress: () => setAlertVisible(false) }]);
|
||||
setAlertActions(defaultAlertActions);
|
||||
}
|
||||
setAlertVisible(true);
|
||||
};
|
||||
|
|
@ -536,11 +541,42 @@ const AIChatScreen: React.FC = () => {
|
|||
|
||||
useEffect(() => {
|
||||
if (context && messages.length === 0) {
|
||||
// Generate conversation starters
|
||||
const starters = generateConversationStarters(context);
|
||||
const starters = (() => {
|
||||
if ('episodesBySeason' in (context as any)) {
|
||||
const series = context as any;
|
||||
return [
|
||||
t('ai_chat.starters.series.overall', { title: series.title }),
|
||||
t('ai_chat.starters.series.arcs'),
|
||||
t('ai_chat.starters.series.rated'),
|
||||
t('ai_chat.starters.series.pivotal'),
|
||||
t('ai_chat.starters.series.themes'),
|
||||
];
|
||||
}
|
||||
|
||||
if ('showTitle' in (context as any)) {
|
||||
const episode = context as any;
|
||||
return [
|
||||
t('ai_chat.starters.episode.overview', { showTitle: episode.showTitle }),
|
||||
t('ai_chat.starters.episode.plot_points', { episodeTitle: episode.episodeTitle }),
|
||||
t('ai_chat.starters.episode.character_development'),
|
||||
t('ai_chat.starters.episode.hidden_details'),
|
||||
t('ai_chat.starters.episode.story_arc'),
|
||||
];
|
||||
}
|
||||
|
||||
const movie = context as any;
|
||||
return [
|
||||
t('ai_chat.starters.movie.overview', { title: movie.title }),
|
||||
t('ai_chat.starters.movie.themes'),
|
||||
t('ai_chat.starters.movie.ending'),
|
||||
t('ai_chat.starters.movie.characters'),
|
||||
t('ai_chat.starters.movie.production_facts'),
|
||||
];
|
||||
})();
|
||||
|
||||
setSuggestions(starters);
|
||||
}
|
||||
}, [context, messages.length]);
|
||||
}, [context, messages.length, t]);
|
||||
|
||||
const loadContext = async () => {
|
||||
try {
|
||||
|
|
@ -597,7 +633,7 @@ const AIChatScreen: React.FC = () => {
|
|||
}
|
||||
} catch (error) {
|
||||
if (__DEV__) console.error('Error loading context:', error);
|
||||
openAlert('Error', 'Failed to load content details for AI chat');
|
||||
openAlert(t('common.error'), t('ai_chat.errors.load_content_details'));
|
||||
} finally {
|
||||
setIsLoadingContext(false);
|
||||
{/* CustomAlert at root */ }
|
||||
|
|
@ -692,18 +728,18 @@ const AIChatScreen: React.FC = () => {
|
|||
} catch (error) {
|
||||
if (__DEV__) console.error('Error sending message:', error);
|
||||
|
||||
let errorMessage = 'Sorry, I encountered an error. Please try again.';
|
||||
let errorMessage = t('ai_chat.errors.generic');
|
||||
if (error instanceof Error) {
|
||||
if (error.message.includes('not configured')) {
|
||||
errorMessage = 'Please configure your OpenRouter API key in Settings > AI Assistant.';
|
||||
errorMessage = t('ai_chat.errors.not_configured');
|
||||
} else if (/401|unauthorized|invalid api key|authentication/i.test(error.message)) {
|
||||
errorMessage = 'OpenRouter rejected your API key. Please verify the key in Settings > AI Assistant.';
|
||||
errorMessage = t('ai_chat.errors.invalid_api_key');
|
||||
} else if (/insufficient|credit|quota|429/i.test(error.message)) {
|
||||
errorMessage = 'OpenRouter quota/credits were rejected for this request. Please check your OpenRouter usage and limits.';
|
||||
errorMessage = t('ai_chat.errors.quota');
|
||||
} else if (/model|provider|endpoint|unsupported|unavailable|not found/i.test(error.message)) {
|
||||
errorMessage = 'The selected OpenRouter model is unavailable. Retry with `openrouter/free` or choose another custom model in Settings > AI Assistant.';
|
||||
errorMessage = t('ai_chat.errors.model_unavailable');
|
||||
} else if (error.message.includes('API request failed')) {
|
||||
errorMessage = 'Failed to connect to AI service. Please check your internet connection, API key, and OpenRouter model availability.';
|
||||
errorMessage = t('ai_chat.errors.connection');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -718,7 +754,7 @@ const AIChatScreen: React.FC = () => {
|
|||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [context, messages, isLoading]);
|
||||
}, [context, messages, isLoading, t]);
|
||||
|
||||
const handleSendPress = useCallback(() => {
|
||||
sendMessage(inputText);
|
||||
|
|
@ -767,7 +803,7 @@ const AIChatScreen: React.FC = () => {
|
|||
<StatusBar barStyle="light-content" />
|
||||
<ActivityIndicator size="large" color={currentTheme.colors.primary} />
|
||||
<Text style={[styles.loadingText, { color: currentTheme.colors.mediumEmphasis }]}>
|
||||
Loading AI context...
|
||||
{t('ai_chat.loading_context')}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
|
|
@ -820,7 +856,7 @@ const AIChatScreen: React.FC = () => {
|
|||
|
||||
<View style={styles.headerInfo}>
|
||||
<Text style={[styles.headerTitle, { color: currentTheme.colors.highEmphasis }]}>
|
||||
AI Chat
|
||||
{t('ai_chat.title')}
|
||||
</Text>
|
||||
<Text style={[styles.headerSubtitle, { color: currentTheme.colors.mediumEmphasis }]}>
|
||||
{getDisplayTitle()}
|
||||
|
|
@ -867,18 +903,18 @@ const AIChatScreen: React.FC = () => {
|
|||
<MaterialIcons name="auto-awesome" size={34} color="white" />
|
||||
</LinearGradient>
|
||||
<Text style={[styles.welcomeTitle, { color: currentTheme.colors.highEmphasis }]}>
|
||||
Ask me anything about
|
||||
{t('ai_chat.welcome_title')}
|
||||
</Text>
|
||||
<Text style={[styles.welcomeSubtitle, { color: currentTheme.colors.primary }]}>
|
||||
{getDisplayTitle()}
|
||||
</Text>
|
||||
<Text style={[styles.welcomeDescription, { color: currentTheme.colors.mediumEmphasis }]}>
|
||||
I have detailed knowledge about this content and can answer questions about plot, characters, themes, and more.
|
||||
{t('ai_chat.welcome_description')}
|
||||
</Text>
|
||||
|
||||
<View style={styles.suggestionsContainer}>
|
||||
<Text style={[styles.suggestionsTitle, { color: currentTheme.colors.mediumEmphasis }]}>
|
||||
Try asking:
|
||||
{t('ai_chat.try_asking')}
|
||||
</Text>
|
||||
<View style={styles.suggestionsGrid}>
|
||||
{suggestions.map((suggestion, index) => (
|
||||
|
|
@ -942,7 +978,7 @@ const AIChatScreen: React.FC = () => {
|
|||
]}
|
||||
value={inputText}
|
||||
onChangeText={setInputText}
|
||||
placeholder="Ask about this content..."
|
||||
placeholder={t('ai_chat.placeholder')}
|
||||
placeholderTextColor={currentTheme.colors.mediumEmphasis}
|
||||
multiline
|
||||
maxLength={500}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -420,8 +420,8 @@ const SettingsScreen: React.FC = () => {
|
|||
)}
|
||||
{isItemVisible('mal') && (
|
||||
<SettingItem
|
||||
title="MyAnimeList"
|
||||
description="Sync with MyAnimeList"
|
||||
title={t('mal_settings.title')}
|
||||
description={t('mal_settings.connect_title')}
|
||||
customIcon={<Image source={require('../../assets/rating-icons/mal-icon.png')} style={{ width: isTablet ? 24 : 20, height: isTablet ? 24 : 20, borderRadius: 4 }} resizeMode="contain" />}
|
||||
renderControl={() => <ChevronRight />}
|
||||
onPress={() => navigation.navigate('MalSettings')}
|
||||
|
|
@ -717,8 +717,8 @@ const SettingsScreen: React.FC = () => {
|
|||
<SettingsCard title={t('settings.account').toUpperCase()}>
|
||||
{showCloudSyncItem && (
|
||||
<SettingItem
|
||||
title="Nuvio Sync"
|
||||
description="Sync data across your Nuvio devices"
|
||||
title={t('settings.cloud_sync.title')}
|
||||
description={t('settings.cloud_sync.description')}
|
||||
customIcon={
|
||||
<FastImage
|
||||
source={require('../../assets/nuvio-sync-icon-og.png')}
|
||||
|
|
@ -829,7 +829,7 @@ const SettingsScreen: React.FC = () => {
|
|||
{(settingsConfig?.categories?.['backup']?.visible !== false) && (
|
||||
<SettingItem
|
||||
title={t('settings.backup_restore')}
|
||||
description="Create and restore app backups"
|
||||
description={t('settings.backup_restore_desc')}
|
||||
icon="archive"
|
||||
renderControl={() => <ChevronRight />}
|
||||
onPress={() => navigation.navigate('Backup')}
|
||||
|
|
|
|||
Loading…
Reference in a new issue