updated settinsgcreen and it's sub-pages to support localization

This commit is contained in:
tapframe 2026-01-06 11:39:12 +05:30
parent 9c37ad8b94
commit afddf4bf2d
9 changed files with 360 additions and 132 deletions

View file

@ -37,6 +37,108 @@
"language": "Language",
"select_language": "Select Language",
"english": "English",
"portuguese": "Portuguese"
"portuguese": "Portuguese",
"account": "Account",
"content_discovery": "Content & Discovery",
"appearance": "Appearance",
"integrations": "Integrations",
"playback": "Playback",
"backup_restore": "Backup & Restore",
"updates": "Updates",
"about": "About",
"developer": "Developer",
"cache": "Cache",
"settings_title": "Settings",
"sign_in_sync": "Sign in to sync",
"add_catalogs_sources": "Addons, catalogs, and sources",
"player_trailers_downloads": "Player, trailers, downloads",
"mdblist_tmdb_ai": "MDBList, TMDB, AI",
"check_updates": "Check for updates",
"developer_tools": "Testing and debug options",
"clear_mdblist_cache": "Clear MDBList Cache",
"cache_management": "CACHE MANAGEMENT",
"downloads_counter": "downloads and counting",
"made_with_love": "Made with ❤️ by Tapframe and friends",
"sections": {
"information": "INFORMATION",
"theme": "THEME",
"layout": "LAYOUT",
"sources": "SOURCES",
"catalogs": "CATALOGS",
"discovery": "DISCOVERY",
"metadata": "METADATA",
"ai_assistant": "AI ASSISTANT",
"video_player": "VIDEO PLAYER",
"audio_subtitles": "AUDIO & SUBTITLES",
"media": "MEDIA",
"notifications": "NOTIFICATIONS",
"testing": "TESTING",
"danger_zone": "DANGER ZONE"
},
"items": {
"privacy_policy": "Privacy Policy",
"report_issue": "Report Issue",
"version": "Version",
"contributors": "Contributors",
"view_contributors": "View all contributors",
"theme": "Theme",
"episode_layout": "Episode Layout",
"streams_backdrop": "Streams Backdrop",
"streams_backdrop_desc": "Show blurred backdrop on mobile streams",
"addons": "Addons",
"installed": "installed",
"debrid_integration": "Debrid Integration",
"debrid_desc": "Connect Torbox for premium streams",
"plugins": "Plugins",
"plugins_desc": "Manage plugins and repositories",
"catalogs": "Catalogs",
"active": "active",
"home_screen": "Home Screen",
"home_screen_desc": "Layout and content",
"continue_watching": "Continue Watching",
"continue_watching_desc": "Cache and playback behavior",
"show_discover": "Show Discover Section",
"show_discover_desc": "Display discover content in Search",
"mdblist": "MDBList",
"mdblist_connected": "Connected",
"mdblist_desc": "Enable to add ratings & reviews",
"tmdb": "TMDB",
"tmdb_desc": "Metadata & logo source provider",
"openrouter": "OpenRouter API",
"openrouter_connected": "Connected",
"openrouter_desc": "Add your API key to enable AI chat",
"video_player": "Video Player",
"built_in": "Built-in",
"external": "External",
"preferred_audio": "Preferred Audio Language",
"preferred_subtitle": "Preferred Subtitle Language",
"subtitle_source": "Subtitle Source Priority",
"auto_select_subs": "Auto-Select Subtitles",
"auto_select_subs_desc": "Automatically select subtitles matching your preferences",
"show_trailers": "Show Trailers",
"show_trailers_desc": "Display trailers in hero section",
"enable_downloads": "Enable Downloads (Beta)",
"enable_downloads_desc": "Show Downloads tab and enable saving streams",
"notifications": "Notifications",
"notifications_desc": "Episode reminders",
"test_onboarding": "Test Onboarding",
"reset_onboarding": "Reset Onboarding",
"test_announcement": "Test Announcement",
"test_announcement_desc": "Show what's new overlay",
"reset_campaigns": "Reset Campaigns",
"reset_campaigns_desc": "Clear campaign impressions",
"clear_all_data": "Clear All Data",
"clear_all_data_desc": "Reset all settings and cached data"
},
"options": {
"horizontal": "Horizontal",
"vertical": "Vertical",
"internal_first": "Internal First",
"internal_first_desc": "Prefer embedded subtitles, then external",
"external_first": "External First",
"external_first_desc": "Prefer addon subtitles, then embedded",
"any_available": "Any Available",
"any_available_desc": "Use first available subtitle track"
}
}
}

View file

@ -37,6 +37,108 @@
"language": "Idioma",
"select_language": "Selecionar Idioma",
"english": "Inglês",
"portuguese": "Português"
"portuguese": "Português",
"account": "Conta",
"content_discovery": "Conteúdo e Descoberta",
"appearance": "Aparência",
"integrations": "Integrações",
"playback": "Reprodução",
"backup_restore": "Backup e Restauração",
"updates": "Atualizações",
"about": "Sobre",
"developer": "Desenvolvedor",
"cache": "Cache",
"settings_title": "Configurações",
"sign_in_sync": "Faça login para sincronizar",
"add_catalogs_sources": "Addons, catálogos e fontes",
"player_trailers_downloads": "Player, trailers, downloads",
"mdblist_tmdb_ai": "MDBList, TMDB, IA",
"check_updates": "Verificar atualizações",
"developer_tools": "Opções de teste e depuração",
"clear_mdblist_cache": "Limpar Cache do MDBList",
"cache_management": "GERENCIAMENTO DE CACHE",
"downloads_counter": "downloads e contando",
"made_with_love": "Feito com ❤️ por Tapframe e amigos",
"sections": {
"information": "INFORMAÇÕES",
"theme": "TEMA",
"layout": "LAYOUT",
"sources": "FONTES",
"catalogs": "CATÁLOGOS",
"discovery": "DESCOBERTA",
"metadata": "METADADOS",
"ai_assistant": "ASSISTENTE IA",
"video_player": "PLAYER DE VÍDEO",
"audio_subtitles": "ÁUDIO E LEGENDAS",
"media": "MÍDIA",
"notifications": "NOTIFICAÇÕES",
"testing": "TESTES",
"danger_zone": "AREA DE PERIGO"
},
"items": {
"privacy_policy": "Política de Privacidade",
"report_issue": "Reportar Problema",
"version": "Versão",
"contributors": "Contribuidores",
"view_contributors": "Ver todos os contribuidores",
"theme": "Tema",
"episode_layout": "Layout de Episódios",
"streams_backdrop": "Fundo de Streams",
"streams_backdrop_desc": "Mostrar fundo desfocado em streams móveis",
"addons": "Addons",
"installed": "instalados",
"debrid_integration": "Integração Debrid",
"debrid_desc": "Conectar Torbox para streams premium",
"plugins": "Plugins",
"plugins_desc": "Gerenciar plugins e repositórios",
"catalogs": "Catálogos",
"active": "ativos",
"home_screen": "Tela Inicial",
"home_screen_desc": "Layout e conteúdo",
"continue_watching": "Continuar Assistindo",
"continue_watching_desc": "Cache e comportamento de reprodução",
"show_discover": "Mostrar Seção Descobrir",
"show_discover_desc": "Exibir conteúdo de descoberta na Pesquisa",
"mdblist": "MDBList",
"mdblist_connected": "Conectado",
"mdblist_desc": "Habilitar para adicionar avaliações e resenhas",
"tmdb": "TMDB",
"tmdb_desc": "Provedor de metadados e logos",
"openrouter": "OpenRouter API",
"openrouter_connected": "Conectado",
"openrouter_desc": "Adicione sua chave API para chat IA",
"video_player": "Player de Vídeo",
"built_in": "Integrado",
"external": "Externo",
"preferred_audio": "Idioma de Áudio Preferido",
"preferred_subtitle": "Idioma de Legenda Preferido",
"subtitle_source": "Prioridade de Fonte de Legenda",
"auto_select_subs": "Auto-Selecionar Legendas",
"auto_select_subs_desc": "Selecionar legendas automaticamente",
"show_trailers": "Mostrar Trailers",
"show_trailers_desc": "Exibir trailers na seção hero",
"enable_downloads": "Habilitar Downloads (Beta)",
"enable_downloads_desc": "Mostrar aba Downloads e permitir salvar streams",
"notifications": "Notificações",
"notifications_desc": "Lembretes de episódios",
"test_onboarding": "Testar Onboarding",
"reset_onboarding": "Resetar Onboarding",
"test_announcement": "Testar Anúncio",
"test_announcement_desc": "Mostrar sobreposição de novidades",
"reset_campaigns": "Resetar Campanhas",
"reset_campaigns_desc": "Limpar impressões de campanhas",
"clear_all_data": "Limpar Todos os Dados",
"clear_all_data_desc": "Resetar todas as configurações e cache"
},
"options": {
"horizontal": "Horizontal",
"vertical": "Vertical",
"internal_first": "Interno Primeiro",
"internal_first_desc": "Preferir legendas embutidas, depois externas",
"external_first": "Externo Primeiro",
"external_first_desc": "Preferir legendas de addons, depois embutidas",
"any_available": "Qualquer Disponível",
"any_available_desc": "Usar primeira legenda disponível"
}
}
}

View file

@ -50,25 +50,15 @@ const { width } = Dimensions.get('window');
const isTablet = width >= 768;
// Settings categories for tablet sidebar
const SETTINGS_CATEGORIES = [
{ id: 'account', title: 'Account', icon: 'user' as string },
{ id: 'content', title: 'Content & Discovery', icon: 'compass' as string },
{ id: 'appearance', title: 'Appearance', icon: 'sliders' as string },
{ id: 'integrations', title: 'Integrations', icon: 'layers' as string },
{ id: 'playback', title: 'Playback', icon: 'play-circle' as string },
{ id: 'backup', title: 'Backup & Restore', icon: 'archive' as string },
{ id: 'updates', title: 'Updates', icon: 'refresh-ccw' as string },
{ id: 'about', title: 'About', icon: 'info' as string },
{ id: 'developer', title: 'Developer', icon: 'code' as string },
{ id: 'cache', title: 'Cache', icon: 'database' as string },
];
// Settings categories moved inside component for translation
// Tablet Sidebar Component
interface SidebarProps {
selectedCategory: string;
onCategorySelect: (category: string) => void;
currentTheme: any;
categories: typeof SETTINGS_CATEGORIES;
categories: any[];
extraTopPadding?: number;
}
@ -143,9 +133,21 @@ const Sidebar: React.FC<SidebarProps> = ({ selectedCategory, onCategorySelect, c
);
};
const SettingsScreen: React.FC = () => {
const { t, i18n } = useTranslation();
const SETTINGS_CATEGORIES = [
{ id: 'account', title: t('settings.account'), icon: 'user' },
{ id: 'content', title: t('settings.content_discovery'), icon: 'compass' },
{ id: 'appearance', title: t('settings.appearance'), icon: 'sliders' },
{ id: 'integrations', title: t('settings.integrations'), icon: 'layers' },
{ id: 'playback', title: t('settings.playback'), icon: 'play-circle' },
{ id: 'backup', title: t('settings.backup_restore'), icon: 'archive' },
{ id: 'updates', title: t('settings.updates'), icon: 'refresh-ccw' },
{ id: 'about', title: t('settings.about'), icon: 'info' },
{ id: 'developer', title: t('settings.developer'), icon: 'code' },
{ id: 'cache', title: t('settings.cache'), icon: 'database' },
];
const { settings, updateSetting } = useSettings();
const [hasUpdateBadge, setHasUpdateBadge] = useState(false);
const [languageModalVisible, setLanguageModalVisible] = useState(false);
@ -333,11 +335,11 @@ const SettingsScreen: React.FC = () => {
switch (categoryId) {
case 'account':
return (
<SettingsCard title="ACCOUNT" isTablet={isTablet}>
<SettingsCard title={t('settings.sections.account')} isTablet={isTablet}>
{isItemVisible('trakt') && (
<SettingItem
title="Trakt"
description={isAuthenticated ? `@${userProfile?.username || 'User'}` : "Sign in to sync"}
description={isAuthenticated ? `@${userProfile?.username || 'User'}` : t('settings.sign_in_sync')}
customIcon={<TraktIcon size={isTablet ? 24 : 20} color={currentTheme.colors.primary} />}
renderControl={() => <ChevronRight />}
onPress={() => navigation.navigate('TraktSettings')}
@ -365,16 +367,16 @@ const SettingsScreen: React.FC = () => {
case 'developer':
return __DEV__ ? (
<SettingsCard title="DEVELOPER" isTablet={isTablet}>
<SettingsCard title={t('settings.sections.testing')} isTablet={isTablet}>
<SettingItem
title="Test Onboarding"
title={t('settings.items.test_onboarding')}
icon="play-circle"
onPress={() => navigation.navigate('Onboarding')}
renderControl={() => <ChevronRight />}
isTablet={isTablet}
/>
<SettingItem
title="Reset Onboarding"
title={t('settings.items.reset_onboarding')}
icon="refresh-ccw"
onPress={async () => {
try {
@ -388,9 +390,9 @@ const SettingsScreen: React.FC = () => {
isTablet={isTablet}
/>
<SettingItem
title="Test Announcement"
title={t('settings.items.test_announcement')}
icon="bell"
description="Show what's new overlay"
description={t('settings.items.test_announcement_desc')}
onPress={async () => {
try {
await mmkvStorage.removeItem('announcement_v1.0.0_shown');
@ -403,8 +405,8 @@ const SettingsScreen: React.FC = () => {
isTablet={isTablet}
/>
<SettingItem
title="Reset Campaigns"
description="Clear campaign impressions"
title={t('settings.items.reset_campaigns')}
description={t('settings.items.reset_campaigns_desc')}
icon="refresh-cw"
onPress={async () => {
await campaignService.resetCampaigns();
@ -414,7 +416,7 @@ const SettingsScreen: React.FC = () => {
isTablet={isTablet}
/>
<SettingItem
title="Clear All Data"
title={t('settings.items.clear_all_data')}
icon="trash-2"
onPress={() => {
openAlert(
@ -444,9 +446,9 @@ const SettingsScreen: React.FC = () => {
case 'cache':
return mdblistKeySet ? (
<SettingsCard title="CACHE MANAGEMENT" isTablet={isTablet}>
<SettingsCard title={t('settings.sections.cache_management')} isTablet={isTablet}>
<SettingItem
title="Clear MDBList Cache"
title={t('settings.clear_mdblist_cache')}
icon="database"
onPress={handleClearMDBListCache}
isLast={true}
@ -457,9 +459,9 @@ const SettingsScreen: React.FC = () => {
case 'backup':
return (
<SettingsCard title="BACKUP & RESTORE" isTablet={isTablet}>
<SettingsCard title={t('settings.backup_restore').toUpperCase()} isTablet={isTablet}>
<SettingItem
title="Backup & Restore"
title={t('settings.backup_restore')}
description="Create and restore app backups"
icon="archive"
renderControl={() => <ChevronRight />}
@ -472,10 +474,10 @@ const SettingsScreen: React.FC = () => {
case 'updates':
return (
<SettingsCard title="UPDATES" isTablet={isTablet}>
<SettingsCard title={t('settings.updates').toUpperCase()} isTablet={isTablet}>
<SettingItem
title="App Updates"
description="Check for updates and manage app version"
description={t('settings.check_updates')}
icon="refresh-ccw"
renderControl={() => <ChevronRight />}
badge={Platform.OS === 'android' && hasUpdateBadge ? 1 : undefined}
@ -549,7 +551,7 @@ const SettingsScreen: React.FC = () => {
return (
<View style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
<StatusBar barStyle={'light-content'} />
<ScreenHeader title="Settings" />
<ScreenHeader title={t('settings.settings_title')} />
<View style={{ flex: 1 }}>
<View style={styles.contentContainer}>
<ScrollView
@ -560,11 +562,11 @@ const SettingsScreen: React.FC = () => {
>
{/* Account */}
{(settingsConfig?.categories?.['account']?.visible !== false) && isItemVisible('trakt') && (
<SettingsCard title="ACCOUNT">
<SettingsCard title={t('settings.account').toUpperCase()}>
{isItemVisible('trakt') && (
<SettingItem
title="Trakt"
description={isAuthenticated ? `@${userProfile?.username || 'User'}` : "Sign in to sync"}
description={isAuthenticated ? `@${userProfile?.username || 'User'}` : t('settings.sign_in_sync')}
customIcon={<TraktIcon size={20} color={currentTheme.colors.primary} />}
renderControl={() => <ChevronRight />}
onPress={() => navigation.navigate('TraktSettings')}
@ -591,8 +593,8 @@ const SettingsScreen: React.FC = () => {
/>
{(settingsConfig?.categories?.['content']?.visible !== false) && (
<SettingItem
title="Content & Discovery"
description="Addons, catalogs, and sources"
title={t('settings.content_discovery')}
description={t('settings.add_catalogs_sources')}
icon="compass"
renderControl={() => <ChevronRight />}
onPress={() => navigation.navigate('ContentDiscoverySettings')}
@ -600,7 +602,7 @@ const SettingsScreen: React.FC = () => {
)}
{(settingsConfig?.categories?.['appearance']?.visible !== false) && (
<SettingItem
title="Appearance"
title={t('settings.appearance')}
description={currentTheme.name}
icon="sliders"
renderControl={() => <ChevronRight />}
@ -609,8 +611,8 @@ const SettingsScreen: React.FC = () => {
)}
{(settingsConfig?.categories?.['integrations']?.visible !== false) && (
<SettingItem
title="Integrations"
description="MDBList, TMDB, AI"
title={t('settings.integrations')}
description={t('settings.mdblist_tmdb_ai')}
icon="layers"
renderControl={() => <ChevronRight />}
onPress={() => navigation.navigate('IntegrationsSettings')}
@ -618,8 +620,8 @@ const SettingsScreen: React.FC = () => {
)}
{(settingsConfig?.categories?.['playback']?.visible !== false) && (
<SettingItem
title="Playback"
description="Player, trailers, downloads"
title={t('settings.playback')}
description={t('settings.player_trailers_downloads')}
icon="play-circle"
renderControl={() => <ChevronRight />}
onPress={() => navigation.navigate('PlaybackSettings')}
@ -637,7 +639,7 @@ const SettingsScreen: React.FC = () => {
<SettingsCard title="DATA">
{(settingsConfig?.categories?.['backup']?.visible !== false) && (
<SettingItem
title="Backup & Restore"
title={t('settings.backup_restore')}
description="Create and restore app backups"
icon="archive"
renderControl={() => <ChevronRight />}
@ -647,7 +649,7 @@ const SettingsScreen: React.FC = () => {
{(settingsConfig?.categories?.['updates']?.visible !== false) && (
<SettingItem
title="App Updates"
description="Check for updates"
description={t('settings.check_updates')}
icon="refresh-ccw"
badge={Platform.OS === 'android' && hasUpdateBadge ? 1 : undefined}
renderControl={() => <ChevronRight />}
@ -668,7 +670,7 @@ const SettingsScreen: React.FC = () => {
{mdblistKeySet && (
<SettingsCard title="CACHE">
<SettingItem
title="Clear MDBList Cache"
title={t('settings.clear_mdblist_cache')}
icon="database"
onPress={handleClearMDBListCache}
isLast
@ -677,7 +679,7 @@ const SettingsScreen: React.FC = () => {
)}
{/* About */}
<SettingsCard title="ABOUT">
<SettingsCard title={t('settings.about').toUpperCase()}>
<SettingItem
title="About Nuvio"
description={getDisplayedAppVersion()}
@ -690,10 +692,10 @@ const SettingsScreen: React.FC = () => {
{/* Developer - only in DEV mode */}
{__DEV__ && (
<SettingsCard title="DEVELOPER">
<SettingsCard title={t('settings.sections.testing')}>
<SettingItem
title="Developer Tools"
description="Testing and debug options"
title={t('settings.items.developer_tools')}
description={t('settings.developer_tools')}
icon="code"
renderControl={() => <ChevronRight />}
onPress={() => navigation.navigate('DeveloperSettings')}
@ -709,7 +711,7 @@ const SettingsScreen: React.FC = () => {
{displayDownloads.toLocaleString()}
</Text>
<Text style={[styles.downloadsLabel, { color: currentTheme.colors.mediumEmphasis }]}>
downloads and counting
{t('settings.downloads_counter')}
</Text>
</View>
)}
@ -788,7 +790,7 @@ const SettingsScreen: React.FC = () => {
<View style={styles.footer}>
<Text style={[styles.footerText, { color: currentTheme.colors.mediumEmphasis }]}>
Made with by Tapframe and friends
{t('settings.made_with_love')}
</Text>
</View>

View file

@ -13,6 +13,7 @@ import { fetchTotalDownloads } from '../../services/githubReleaseService';
import { getDisplayedAppVersion } from '../../utils/version';
import ScreenHeader from '../../components/common/ScreenHeader';
import { SettingsCard, SettingItem, ChevronRight } from './SettingsComponents';
import { useTranslation } from 'react-i18next';
const { width } = Dimensions.get('window');
@ -29,6 +30,7 @@ export const AboutSettingsContent: React.FC<AboutSettingsContentProps> = ({
isTablet = false,
displayDownloads: externalDisplayDownloads
}) => {
const { t } = useTranslation();
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const { currentTheme } = useTheme();
@ -52,30 +54,30 @@ export const AboutSettingsContent: React.FC<AboutSettingsContentProps> = ({
return (
<>
<SettingsCard title="INFORMATION" isTablet={isTablet}>
<SettingsCard title={t('settings.sections.information')} isTablet={isTablet}>
<SettingItem
title="Privacy Policy"
title={t('settings.items.privacy_policy')}
icon="lock"
onPress={() => Linking.openURL('https://tapframe.github.io/NuvioStreaming/#privacy-policy')}
renderControl={() => <ChevronRight />}
isTablet={isTablet}
/>
<SettingItem
title="Report Issue"
title={t('settings.items.report_issue')}
icon="alert-triangle"
onPress={() => Sentry.showFeedbackWidget()}
renderControl={() => <ChevronRight />}
isTablet={isTablet}
/>
<SettingItem
title="Version"
title={t('settings.items.version')}
description={getDisplayedAppVersion()}
icon="info"
isTablet={isTablet}
/>
<SettingItem
title="Contributors"
description="View all contributors"
title={t('settings.items.contributors')}
description={t('settings.items.view_contributors')}
icon="users"
renderControl={() => <ChevronRight />}
onPress={() => navigation.navigate('Contributors')}
@ -92,6 +94,7 @@ export const AboutSettingsContent: React.FC<AboutSettingsContentProps> = ({
*/
export const AboutFooter: React.FC<{ displayDownloads: number | null }> = ({ displayDownloads }) => {
const { currentTheme } = useTheme();
const { t } = useTranslation();
return (
<>
@ -101,7 +104,7 @@ export const AboutFooter: React.FC<{ displayDownloads: number | null }> = ({ dis
{displayDownloads.toLocaleString()}
</Text>
<Text style={[styles.downloadsLabel, { color: currentTheme.colors.mediumEmphasis }]}>
downloads and counting
{t('settings.downloads_counter')}
</Text>
</View>
)}
@ -179,7 +182,7 @@ export const AboutFooter: React.FC<{ displayDownloads: number | null }> = ({ dis
<View style={styles.footer}>
<Text style={[styles.footerText, { color: currentTheme.colors.mediumEmphasis }]}>
Made with by Tapframe and Friends
{t('settings.made_with_love')}
</Text>
</View>
</>
@ -192,13 +195,14 @@ export const AboutFooter: React.FC<{ displayDownloads: number | null }> = ({ dis
const AboutSettingsScreen: React.FC = () => {
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const { currentTheme } = useTheme();
const { t } = useTranslation();
const insets = useSafeAreaInsets();
const screenIsTablet = width >= 768;
return (
<View style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
<StatusBar barStyle="light-content" />
<ScreenHeader title="About" showBackButton onBackPress={() => navigation.goBack()} />
<ScreenHeader title={t('settings.about')} showBackButton onBackPress={() => navigation.goBack()} />
<ScrollView
style={styles.scrollView}

View file

@ -9,6 +9,7 @@ import { RootStackParamList } from '../../navigation/AppNavigator';
import ScreenHeader from '../../components/common/ScreenHeader';
import { SettingsCard, SettingItem, CustomSwitch, ChevronRight } from './SettingsComponents';
import { useRealtimeConfig } from '../../hooks/useRealtimeConfig';
import { useTranslation } from 'react-i18next';
const { width } = Dimensions.get('window');
@ -24,6 +25,7 @@ export const AppearanceSettingsContent: React.FC<AppearanceSettingsContentProps>
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const { currentTheme } = useTheme();
const { settings, updateSetting } = useSettings();
const { t } = useTranslation();
const config = useRealtimeConfig();
const isItemVisible = (itemId: string) => {
@ -43,10 +45,10 @@ export const AppearanceSettingsContent: React.FC<AppearanceSettingsContentProps>
return (
<>
{hasVisibleItems(['theme']) && (
<SettingsCard title="THEME" isTablet={isTablet}>
<SettingsCard title={t('settings.sections.theme')} isTablet={isTablet}>
{isItemVisible('theme') && (
<SettingItem
title="Theme"
title={t('settings.items.theme')}
description={currentTheme.name}
icon="sliders"
renderControl={() => <ChevronRight />}
@ -59,11 +61,11 @@ export const AppearanceSettingsContent: React.FC<AppearanceSettingsContentProps>
)}
{hasVisibleItems(['episode_layout', 'streams_backdrop']) && (
<SettingsCard title="LAYOUT" isTablet={isTablet}>
<SettingsCard title={t('settings.sections.layout')} isTablet={isTablet}>
{isItemVisible('episode_layout') && (
<SettingItem
title="Episode Layout"
description={settings?.episodeLayoutStyle === 'horizontal' ? 'Horizontal' : 'Vertical'}
title={t('settings.items.episode_layout')}
description={settings?.episodeLayoutStyle === 'horizontal' ? t('settings.options.horizontal') : t('settings.options.vertical')}
icon="grid"
renderControl={() => (
<CustomSwitch
@ -77,8 +79,8 @@ export const AppearanceSettingsContent: React.FC<AppearanceSettingsContentProps>
)}
{!isTablet && isItemVisible('streams_backdrop') && (
<SettingItem
title="Streams Backdrop"
description="Show blurred backdrop on mobile streams"
title={t('settings.items.streams_backdrop')}
description={t('settings.items.streams_backdrop_desc')}
icon="image"
renderControl={() => (
<CustomSwitch
@ -102,13 +104,14 @@ export const AppearanceSettingsContent: React.FC<AppearanceSettingsContentProps>
const AppearanceSettingsScreen: React.FC = () => {
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const { currentTheme } = useTheme();
const { t } = useTranslation();
const insets = useSafeAreaInsets();
const screenIsTablet = width >= 768;
return (
<View style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
<StatusBar barStyle="light-content" />
<ScreenHeader title="Appearance" showBackButton onBackPress={() => navigation.goBack()} />
<ScreenHeader title={t('settings.appearance')} showBackButton onBackPress={() => navigation.goBack()} />
<ScrollView
style={styles.scrollView}

View file

@ -12,6 +12,7 @@ import ScreenHeader from '../../components/common/ScreenHeader';
import PluginIcon from '../../components/icons/PluginIcon';
import { SettingsCard, SettingItem, CustomSwitch, ChevronRight } from './SettingsComponents';
import { useRealtimeConfig } from '../../hooks/useRealtimeConfig';
import { useTranslation } from 'react-i18next';
const { width } = Dimensions.get('window');
@ -27,6 +28,7 @@ export const ContentDiscoverySettingsContent: React.FC<ContentDiscoverySettingsC
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const { currentTheme } = useTheme();
const { settings, updateSetting } = useSettings();
const { t } = useTranslation();
const config = useRealtimeConfig();
const [addonCount, setAddonCount] = useState<number>(0);
@ -79,11 +81,11 @@ export const ContentDiscoverySettingsContent: React.FC<ContentDiscoverySettingsC
return (
<>
{hasVisibleItems(['addons', 'debrid', 'plugins']) && (
<SettingsCard title="SOURCES" isTablet={isTablet}>
<SettingsCard title={t('settings.sections.sources')} isTablet={isTablet}>
{isItemVisible('addons') && (
<SettingItem
title="Addons"
description={`${addonCount} installed`}
title={t('settings.items.addons')}
description={`${addonCount} ${t('settings.items.installed')}`}
icon="layers"
renderControl={() => <ChevronRight />}
onPress={() => navigation.navigate('Addons')}
@ -92,8 +94,8 @@ export const ContentDiscoverySettingsContent: React.FC<ContentDiscoverySettingsC
)}
{isItemVisible('debrid') && (
<SettingItem
title="Debrid Integration"
description="Connect Torbox for premium streams"
title={t('settings.items.debrid_integration')}
description={t('settings.items.debrid_desc')}
icon="link"
renderControl={() => <ChevronRight />}
onPress={() => navigation.navigate('DebridIntegration')}
@ -102,8 +104,8 @@ export const ContentDiscoverySettingsContent: React.FC<ContentDiscoverySettingsC
)}
{isItemVisible('plugins') && (
<SettingItem
title="Plugins"
description="Manage plugins and repositories"
title={t('settings.items.plugins')}
description={t('settings.items.plugins_desc')}
customIcon={<PluginIcon size={isTablet ? 22 : 18} color={currentTheme.colors.primary} />}
renderControl={() => <ChevronRight />}
onPress={() => navigation.navigate('ScraperSettings')}
@ -115,11 +117,11 @@ export const ContentDiscoverySettingsContent: React.FC<ContentDiscoverySettingsC
)}
{hasVisibleItems(['catalogs', 'home_screen', 'continue_watching']) && (
<SettingsCard title="CATALOGS" isTablet={isTablet}>
<SettingsCard title={t('settings.sections.catalogs')} isTablet={isTablet}>
{isItemVisible('catalogs') && (
<SettingItem
title="Catalogs"
description={`${catalogCount} active`}
title={t('settings.items.catalogs')}
description={`${catalogCount} ${t('settings.items.active')}`}
icon="list"
renderControl={() => <ChevronRight />}
onPress={() => navigation.navigate('CatalogSettings')}
@ -128,8 +130,8 @@ export const ContentDiscoverySettingsContent: React.FC<ContentDiscoverySettingsC
)}
{isItemVisible('home_screen') && (
<SettingItem
title="Home Screen"
description="Layout and content"
title={t('settings.items.home_screen')}
description={t('settings.items.home_screen_desc')}
icon="home"
renderControl={() => <ChevronRight />}
onPress={() => navigation.navigate('HomeScreenSettings')}
@ -138,8 +140,8 @@ export const ContentDiscoverySettingsContent: React.FC<ContentDiscoverySettingsC
)}
{isItemVisible('continue_watching') && (
<SettingItem
title="Continue Watching"
description="Cache and playback behavior"
title={t('settings.items.continue_watching')}
description={t('settings.items.continue_watching_desc')}
icon="play-circle"
renderControl={() => <ChevronRight />}
onPress={() => navigation.navigate('ContinueWatchingSettings')}
@ -151,11 +153,11 @@ export const ContentDiscoverySettingsContent: React.FC<ContentDiscoverySettingsC
)}
{hasVisibleItems(['show_discover']) && (
<SettingsCard title="DISCOVERY" isTablet={isTablet}>
<SettingsCard title={t('settings.sections.discovery')} isTablet={isTablet}>
{isItemVisible('show_discover') && (
<SettingItem
title="Show Discover Section"
description="Display discover content in Search"
title={t('settings.items.show_discover')}
description={t('settings.items.show_discover_desc')}
icon="compass"
renderControl={() => (
<CustomSwitch
@ -179,13 +181,14 @@ export const ContentDiscoverySettingsContent: React.FC<ContentDiscoverySettingsC
const ContentDiscoverySettingsScreen: React.FC = () => {
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const { currentTheme } = useTheme();
const { t } = useTranslation();
const insets = useSafeAreaInsets();
const screenIsTablet = width >= 768;
return (
<View style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
<StatusBar barStyle="light-content" />
<ScreenHeader title="Content & Discovery" showBackButton onBackPress={() => navigation.goBack()} />
<ScreenHeader title={t('settings.content_discovery')} showBackButton onBackPress={() => navigation.goBack()} />
<ScrollView
style={styles.scrollView}

View file

@ -10,10 +10,12 @@ import { RootStackParamList } from '../../navigation/AppNavigator';
import ScreenHeader from '../../components/common/ScreenHeader';
import CustomAlert from '../../components/CustomAlert';
import { SettingsCard, SettingItem, ChevronRight } from './SettingsComponents';
import { useTranslation } from 'react-i18next';
const DeveloperSettingsScreen: React.FC = () => {
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const { currentTheme } = useTheme();
const { t } = useTranslation();
const insets = useSafeAreaInsets();
const [alertVisible, setAlertVisible] = useState(false);
@ -84,36 +86,36 @@ const DeveloperSettingsScreen: React.FC = () => {
return (
<View style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
<StatusBar barStyle="light-content" />
<ScreenHeader title="Developer" showBackButton onBackPress={() => navigation.goBack()} />
<ScreenHeader title={t('settings.developer')} showBackButton onBackPress={() => navigation.goBack()} />
<ScrollView
style={styles.scrollView}
showsVerticalScrollIndicator={false}
contentContainerStyle={[styles.scrollContent, { paddingBottom: insets.bottom + 24 }]}
>
<SettingsCard title="TESTING">
<SettingsCard title={t('settings.sections.testing')}>
<SettingItem
title="Test Onboarding"
title={t('settings.items.test_onboarding')}
icon="play-circle"
onPress={() => navigation.navigate('Onboarding')}
renderControl={() => <ChevronRight />}
/>
<SettingItem
title="Reset Onboarding"
title={t('settings.items.reset_onboarding')}
icon="refresh-ccw"
onPress={handleResetOnboarding}
renderControl={() => <ChevronRight />}
/>
<SettingItem
title="Test Announcement"
title={t('settings.items.test_announcement')}
icon="bell"
description="Show what's new overlay"
description={t('settings.items.test_announcement_desc')}
onPress={handleResetAnnouncement}
renderControl={() => <ChevronRight />}
/>
<SettingItem
title="Reset Campaigns"
description="Clear campaign impressions"
title={t('settings.items.reset_campaigns')}
description={t('settings.items.reset_campaigns_desc')}
icon="refresh-cw"
onPress={handleResetCampaigns}
renderControl={() => <ChevronRight />}
@ -121,10 +123,10 @@ const DeveloperSettingsScreen: React.FC = () => {
/>
</SettingsCard>
<SettingsCard title="DANGER ZONE">
<SettingsCard title={t('settings.sections.danger_zone')}>
<SettingItem
title="Clear All Data"
description="Reset all settings and cached data"
title={t('settings.items.clear_all_data')}
description={t('settings.items.clear_all_data_desc')}
icon="trash-2"
onPress={handleClearAllData}
isLast

View file

@ -11,6 +11,7 @@ import MDBListIcon from '../../components/icons/MDBListIcon';
import TMDBIcon from '../../components/icons/TMDBIcon';
import { SettingsCard, SettingItem, ChevronRight } from './SettingsComponents';
import { useRealtimeConfig } from '../../hooks/useRealtimeConfig';
import { useTranslation } from 'react-i18next';
const { width } = Dimensions.get('window');
@ -26,6 +27,7 @@ export const IntegrationsSettingsContent: React.FC<IntegrationsSettingsContentPr
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const { currentTheme } = useTheme();
const config = useRealtimeConfig();
const { t } = useTranslation();
const [mdblistKeySet, setMdblistKeySet] = useState<boolean>(false);
const [openRouterKeySet, setOpenRouterKeySet] = useState<boolean>(false);
@ -62,11 +64,11 @@ export const IntegrationsSettingsContent: React.FC<IntegrationsSettingsContentPr
return (
<>
{hasVisibleItems(['mdblist', 'tmdb']) && (
<SettingsCard title="METADATA" isTablet={isTablet}>
<SettingsCard title={t('settings.sections.metadata')} isTablet={isTablet}>
{isItemVisible('mdblist') && (
<SettingItem
title="MDBList"
description={mdblistKeySet ? "Connected" : "Enable to add ratings & reviews"}
title={t('settings.items.mdblist')}
description={mdblistKeySet ? t('settings.items.mdblist_connected') : t('settings.items.mdblist_desc')}
customIcon={<MDBListIcon size={isTablet ? 22 : 18} colorPrimary={currentTheme.colors.primary} colorSecondary={currentTheme.colors.white} />}
renderControl={() => <ChevronRight />}
onPress={() => navigation.navigate('MDBListSettings')}
@ -75,8 +77,8 @@ export const IntegrationsSettingsContent: React.FC<IntegrationsSettingsContentPr
)}
{isItemVisible('tmdb') && (
<SettingItem
title="TMDB"
description="Metadata & logo source provider"
title={t('settings.items.tmdb')}
description={t('settings.items.tmdb_desc')}
customIcon={<TMDBIcon size={isTablet ? 22 : 18} color={currentTheme.colors.primary} />}
renderControl={() => <ChevronRight />}
onPress={() => navigation.navigate('TMDBSettings')}
@ -88,11 +90,11 @@ export const IntegrationsSettingsContent: React.FC<IntegrationsSettingsContentPr
)}
{hasVisibleItems(['openrouter']) && (
<SettingsCard title="AI ASSISTANT" isTablet={isTablet}>
<SettingsCard title={t('settings.sections.ai_assistant')} isTablet={isTablet}>
{isItemVisible('openrouter') && (
<SettingItem
title="OpenRouter API"
description={openRouterKeySet ? "Connected" : "Add your API key to enable AI chat"}
title={t('settings.items.openrouter')}
description={openRouterKeySet ? t('settings.items.openrouter_connected') : t('settings.items.openrouter_desc')}
icon="cpu"
renderControl={() => <ChevronRight />}
onPress={() => navigation.navigate('AISettings')}
@ -112,13 +114,14 @@ export const IntegrationsSettingsContent: React.FC<IntegrationsSettingsContentPr
const IntegrationsSettingsScreen: React.FC = () => {
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const { currentTheme } = useTheme();
const { t } = useTranslation();
const insets = useSafeAreaInsets();
const screenIsTablet = width >= 768;
return (
<View style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
<StatusBar barStyle="light-content" />
<ScreenHeader title="Integrations" showBackButton onBackPress={() => navigation.goBack()} />
<ScreenHeader title={t('settings.integrations')} showBackButton onBackPress={() => navigation.goBack()} />
<ScrollView
style={styles.scrollView}

View file

@ -11,6 +11,7 @@ import { SettingsCard, SettingItem, CustomSwitch, ChevronRight } from './Setting
import { useRealtimeConfig } from '../../hooks/useRealtimeConfig';
import { MaterialIcons } from '@expo/vector-icons';
import { BottomSheetModal, BottomSheetScrollView, BottomSheetBackdrop } from '@gorhom/bottom-sheet';
import { useTranslation } from 'react-i18next';
const { width } = Dimensions.get('window');
@ -69,6 +70,7 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const { currentTheme } = useTheme();
const { settings, updateSetting } = useSettings();
const { t } = useTranslation();
const config = useRealtimeConfig();
// Bottom sheet refs
@ -116,8 +118,10 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
};
const getSourceLabel = (value: string) => {
const option = SUBTITLE_SOURCE_OPTIONS.find(o => o.value === value);
return option ? option.label : 'Internal First';
if (value === 'internal') return t('settings.options.internal_first');
if (value === 'external') return t('settings.options.external_first');
if (value === 'any') return t('settings.options.any_available');
return t('settings.options.internal_first');
};
// Render backdrop for bottom sheets
@ -151,13 +155,13 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
return (
<>
{hasVisibleItems(['video_player']) && (
<SettingsCard title="VIDEO PLAYER" isTablet={isTablet}>
<SettingsCard title={t('settings.sections.video_player')} isTablet={isTablet}>
{isItemVisible('video_player') && (
<SettingItem
title="Video Player"
title={t('settings.items.video_player')}
description={Platform.OS === 'ios'
? (settings?.preferredPlayer === 'internal' ? 'Built-in' : settings?.preferredPlayer?.toUpperCase() || 'Built-in')
: (settings?.useExternalPlayer ? 'External' : 'Built-in')
? (settings?.preferredPlayer === 'internal' ? t('settings.items.built_in') : settings?.preferredPlayer?.toUpperCase() || t('settings.items.built_in'))
: (settings?.useExternalPlayer ? t('settings.items.external') : t('settings.items.built_in'))
}
icon="play-circle"
renderControl={() => <ChevronRight />}
@ -170,9 +174,9 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
)}
{/* Audio & Subtitle Preferences */}
<SettingsCard title="AUDIO & SUBTITLES" isTablet={isTablet}>
<SettingsCard title={t('settings.sections.audio_subtitles')} isTablet={isTablet}>
<SettingItem
title="Preferred Audio Language"
title={t('settings.items.preferred_audio')}
description={getLanguageName(settings?.preferredAudioLanguage || 'en')}
icon="volume-2"
renderControl={() => <ChevronRight />}
@ -180,7 +184,7 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
isTablet={isTablet}
/>
<SettingItem
title="Preferred Subtitle Language"
title={t('settings.items.preferred_subtitle')}
description={getLanguageName(settings?.preferredSubtitleLanguage || 'en')}
icon="type"
renderControl={() => <ChevronRight />}
@ -188,7 +192,7 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
isTablet={isTablet}
/>
<SettingItem
title="Subtitle Source Priority"
title={t('settings.items.subtitle_source')}
description={getSourceLabel(settings?.subtitleSourcePreference || 'internal')}
icon="layers"
renderControl={() => <ChevronRight />}
@ -196,8 +200,8 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
isTablet={isTablet}
/>
<SettingItem
title="Auto-Select Subtitles"
description="Automatically select subtitles matching your preferences"
title={t('settings.items.auto_select_subs')}
description={t('settings.items.auto_select_subs_desc')}
icon="zap"
renderControl={() => (
<CustomSwitch
@ -211,11 +215,11 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
</SettingsCard>
{hasVisibleItems(['show_trailers', 'enable_downloads']) && (
<SettingsCard title="MEDIA" isTablet={isTablet}>
<SettingsCard title={t('settings.sections.media')} isTablet={isTablet}>
{isItemVisible('show_trailers') && (
<SettingItem
title="Show Trailers"
description="Display trailers in hero section"
title={t('settings.items.show_trailers')}
description={t('settings.items.show_trailers_desc')}
icon="film"
renderControl={() => (
<CustomSwitch
@ -228,8 +232,8 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
)}
{isItemVisible('enable_downloads') && (
<SettingItem
title="Enable Downloads (Beta)"
description="Show Downloads tab and enable saving streams"
title={t('settings.items.enable_downloads')}
description={t('settings.items.enable_downloads_desc')}
icon="download"
renderControl={() => (
<CustomSwitch
@ -245,11 +249,11 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
)}
{hasVisibleItems(['notifications']) && (
<SettingsCard title="NOTIFICATIONS" isTablet={isTablet}>
<SettingsCard title={t('settings.sections.notifications')} isTablet={isTablet}>
{isItemVisible('notifications') && (
<SettingItem
title="Notifications"
description="Episode reminders"
title={t('settings.items.notifications')}
description={t('settings.items.notifications_desc')}
icon="bell"
renderControl={() => <ChevronRight />}
onPress={() => navigation.navigate('NotificationSettings')}
@ -272,7 +276,7 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
handleIndicatorStyle={{ backgroundColor: 'rgba(255,255,255,0.3)' }}
>
<View style={styles.sheetHeader}>
<Text style={styles.sheetTitle}>Preferred Audio Language</Text>
<Text style={styles.sheetTitle}>{t('settings.items.preferred_audio')}</Text>
</View>
<BottomSheetScrollView contentContainerStyle={styles.sheetContent}>
{AVAILABLE_LANGUAGES.map((lang) => {
@ -313,7 +317,7 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
handleIndicatorStyle={{ backgroundColor: 'rgba(255,255,255,0.3)' }}
>
<View style={styles.sheetHeader}>
<Text style={styles.sheetTitle}>Preferred Subtitle Language</Text>
<Text style={styles.sheetTitle}>{t('settings.items.preferred_subtitle')}</Text>
</View>
<BottomSheetScrollView contentContainerStyle={styles.sheetContent}>
{AVAILABLE_LANGUAGES.map((lang) => {
@ -354,7 +358,7 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
handleIndicatorStyle={{ backgroundColor: 'rgba(255,255,255,0.3)' }}
>
<View style={styles.sheetHeader}>
<Text style={styles.sheetTitle}>Subtitle Source Priority</Text>
<Text style={styles.sheetTitle}>{t('settings.items.subtitle_source')}</Text>
</View>
<BottomSheetScrollView contentContainerStyle={styles.sheetContent}>
{SUBTITLE_SOURCE_OPTIONS.map((option) => {
@ -370,10 +374,12 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
>
<View style={styles.sourceItemContent}>
<Text style={[styles.sourceLabel, { color: isSelected ? currentTheme.colors.primary : '#fff' }]}>
{option.label}
{getSourceLabel(option.value)}
</Text>
<Text style={styles.sourceDescription}>
{option.description}
{option.value === 'internal' && t('settings.options.internal_first_desc')}
{option.value === 'external' && t('settings.options.external_first_desc')}
{option.value === 'any' && t('settings.options.any_available_desc')}
</Text>
</View>
{isSelected && (
@ -395,13 +401,14 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
const PlaybackSettingsScreen: React.FC = () => {
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const { currentTheme } = useTheme();
const { t } = useTranslation();
const insets = useSafeAreaInsets();
const screenIsTablet = width >= 768;
return (
<View style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
<StatusBar barStyle="light-content" />
<ScreenHeader title="Playback" showBackButton onBackPress={() => navigation.goBack()} />
<ScreenHeader title={t('settings.playback')} showBackButton onBackPress={() => navigation.goBack()} />
<ScrollView
style={styles.scrollView}