- Restructured profile screens location - Updated beneficiary detail page - Updated API service - Updated all scheme files (MainScheme, ENV API, Discussion, AppStore, SysAnal) - Added PageHeader component 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
339 lines
10 KiB
TypeScript
339 lines
10 KiB
TypeScript
import React, { useState } from 'react';
|
|
import {
|
|
View,
|
|
Text,
|
|
StyleSheet,
|
|
ScrollView,
|
|
TouchableOpacity,
|
|
Alert,
|
|
} from 'react-native';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
import { router } from 'expo-router';
|
|
import { AppColors, BorderRadius, FontSizes, Spacing } from '@/constants/theme';
|
|
import { PageHeader } from '@/components/PageHeader';
|
|
|
|
interface LanguageOptionProps {
|
|
code: string;
|
|
name: string;
|
|
nativeName: string;
|
|
flag: string;
|
|
isSelected: boolean;
|
|
isAvailable: boolean;
|
|
onSelect: () => void;
|
|
}
|
|
|
|
function LanguageOption({
|
|
code,
|
|
name,
|
|
nativeName,
|
|
flag,
|
|
isSelected,
|
|
isAvailable,
|
|
onSelect,
|
|
}: LanguageOptionProps) {
|
|
return (
|
|
<TouchableOpacity
|
|
style={[styles.languageOption, isSelected && styles.languageOptionSelected]}
|
|
onPress={onSelect}
|
|
disabled={!isAvailable}
|
|
>
|
|
<Text style={styles.flag}>{flag}</Text>
|
|
<View style={styles.languageInfo}>
|
|
<Text style={[styles.languageName, !isAvailable && styles.languageNameDisabled]}>
|
|
{name}
|
|
</Text>
|
|
<Text style={styles.languageNative}>{nativeName}</Text>
|
|
</View>
|
|
{!isAvailable ? (
|
|
<View style={styles.comingSoonBadge}>
|
|
<Text style={styles.comingSoonText}>Coming Soon</Text>
|
|
</View>
|
|
) : isSelected ? (
|
|
<Ionicons name="checkmark-circle" size={24} color={AppColors.primary} />
|
|
) : null}
|
|
</TouchableOpacity>
|
|
);
|
|
}
|
|
|
|
export default function LanguageScreen() {
|
|
const [selectedLanguage, setSelectedLanguage] = useState('en');
|
|
|
|
const languages = [
|
|
{ code: 'en', name: 'English', nativeName: 'English', flag: '🇺🇸', available: true },
|
|
{ code: 'es', name: 'Spanish', nativeName: 'Español', flag: '🇪🇸', available: false },
|
|
{ code: 'fr', name: 'French', nativeName: 'Français', flag: '🇫🇷', available: false },
|
|
{ code: 'de', name: 'German', nativeName: 'Deutsch', flag: '🇩🇪', available: false },
|
|
{ code: 'it', name: 'Italian', nativeName: 'Italiano', flag: '🇮🇹', available: false },
|
|
{ code: 'pt', name: 'Portuguese', nativeName: 'Português', flag: '🇵🇹', available: false },
|
|
{ code: 'nl', name: 'Dutch', nativeName: 'Nederlands', flag: '🇳🇱', available: false },
|
|
{ code: 'pl', name: 'Polish', nativeName: 'Polski', flag: '🇵🇱', available: false },
|
|
{ code: 'ru', name: 'Russian', nativeName: 'Русский', flag: '🇷🇺', available: false },
|
|
{ code: 'ja', name: 'Japanese', nativeName: '日本語', flag: '🇯🇵', available: false },
|
|
{ code: 'zh', name: 'Chinese', nativeName: '中文', flag: '🇨🇳', available: false },
|
|
{ code: 'ko', name: 'Korean', nativeName: '한국어', flag: '🇰🇷', available: false },
|
|
];
|
|
|
|
const handleSelectLanguage = (code: string, name: string, available: boolean) => {
|
|
if (!available) {
|
|
Alert.alert(
|
|
'Coming Soon',
|
|
`${name} translation is not available yet. We're working on adding more languages!`,
|
|
[{ text: 'OK' }]
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (code === selectedLanguage) {
|
|
return;
|
|
}
|
|
|
|
Alert.alert(
|
|
'Change Language',
|
|
`Are you sure you want to change the app language to ${name}?`,
|
|
[
|
|
{ text: 'Cancel', style: 'cancel' },
|
|
{
|
|
text: 'Change',
|
|
onPress: () => {
|
|
setSelectedLanguage(code);
|
|
Alert.alert(
|
|
'Language Changed',
|
|
`The app language has been changed to ${name}. Some changes may require restarting the app.`,
|
|
[{ text: 'OK', onPress: () => router.back() }]
|
|
);
|
|
},
|
|
},
|
|
]
|
|
);
|
|
};
|
|
|
|
const availableLanguages = languages.filter(l => l.available);
|
|
const comingSoonLanguages = languages.filter(l => !l.available);
|
|
|
|
return (
|
|
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
|
|
<PageHeader title="Language" />
|
|
<ScrollView showsVerticalScrollIndicator={false}>
|
|
{/* Current Language */}
|
|
<View style={styles.section}>
|
|
<View style={styles.currentLanguageCard}>
|
|
<Ionicons name="globe" size={24} color={AppColors.primary} />
|
|
<View style={styles.currentLanguageInfo}>
|
|
<Text style={styles.currentLanguageLabel}>Current Language</Text>
|
|
<Text style={styles.currentLanguageName}>
|
|
{languages.find(l => l.code === selectedLanguage)?.name || 'English'}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Available Languages */}
|
|
<View style={styles.section}>
|
|
<Text style={styles.sectionTitle}>Available Languages</Text>
|
|
<View style={styles.languagesCard}>
|
|
{availableLanguages.map((lang, index) => (
|
|
<View key={lang.code}>
|
|
<LanguageOption
|
|
code={lang.code}
|
|
name={lang.name}
|
|
nativeName={lang.nativeName}
|
|
flag={lang.flag}
|
|
isSelected={selectedLanguage === lang.code}
|
|
isAvailable={lang.available}
|
|
onSelect={() => handleSelectLanguage(lang.code, lang.name, lang.available)}
|
|
/>
|
|
{index < availableLanguages.length - 1 && <View style={styles.divider} />}
|
|
</View>
|
|
))}
|
|
</View>
|
|
</View>
|
|
|
|
{/* Coming Soon Languages */}
|
|
<View style={styles.section}>
|
|
<Text style={styles.sectionTitle}>Coming Soon</Text>
|
|
<View style={styles.languagesCard}>
|
|
{comingSoonLanguages.map((lang, index) => (
|
|
<View key={lang.code}>
|
|
<LanguageOption
|
|
code={lang.code}
|
|
name={lang.name}
|
|
nativeName={lang.nativeName}
|
|
flag={lang.flag}
|
|
isSelected={false}
|
|
isAvailable={lang.available}
|
|
onSelect={() => handleSelectLanguage(lang.code, lang.name, lang.available)}
|
|
/>
|
|
{index < comingSoonLanguages.length - 1 && <View style={styles.divider} />}
|
|
</View>
|
|
))}
|
|
</View>
|
|
</View>
|
|
|
|
{/* Help Translate */}
|
|
<View style={styles.section}>
|
|
<TouchableOpacity
|
|
style={styles.helpTranslateCard}
|
|
onPress={() => Alert.alert(
|
|
'Help Us Translate',
|
|
'We\'d love your help translating WellNuo into more languages!\n\n' +
|
|
'If you\'re fluent in another language and would like to contribute, ' +
|
|
'please contact us at translations@wellnuo.com',
|
|
[{ text: 'OK' }]
|
|
)}
|
|
>
|
|
<View style={styles.helpTranslateIcon}>
|
|
<Ionicons name="language" size={24} color={AppColors.primary} />
|
|
</View>
|
|
<View style={styles.helpTranslateContent}>
|
|
<Text style={styles.helpTranslateTitle}>Help Us Translate</Text>
|
|
<Text style={styles.helpTranslateDescription}>
|
|
Want to see WellNuo in your language? Help us by contributing translations.
|
|
</Text>
|
|
</View>
|
|
<Ionicons name="chevron-forward" size={20} color={AppColors.textMuted} />
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
{/* Note */}
|
|
<View style={styles.noteContainer}>
|
|
<Ionicons name="information-circle" size={16} color={AppColors.textMuted} />
|
|
<Text style={styles.noteText}>
|
|
Changing the language will translate the app interface. Beneficiary data and
|
|
system notifications may remain in the original language.
|
|
</Text>
|
|
</View>
|
|
</ScrollView>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: AppColors.surface,
|
|
},
|
|
section: {
|
|
marginTop: Spacing.md,
|
|
},
|
|
sectionTitle: {
|
|
fontSize: FontSizes.sm,
|
|
fontWeight: '600',
|
|
color: AppColors.textSecondary,
|
|
paddingHorizontal: Spacing.lg,
|
|
paddingVertical: Spacing.sm,
|
|
textTransform: 'uppercase',
|
|
},
|
|
currentLanguageCard: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
backgroundColor: AppColors.background,
|
|
marginHorizontal: Spacing.lg,
|
|
padding: Spacing.md,
|
|
borderRadius: BorderRadius.lg,
|
|
},
|
|
currentLanguageInfo: {
|
|
marginLeft: Spacing.md,
|
|
},
|
|
currentLanguageLabel: {
|
|
fontSize: FontSizes.xs,
|
|
color: AppColors.textMuted,
|
|
},
|
|
currentLanguageName: {
|
|
fontSize: FontSizes.lg,
|
|
fontWeight: '600',
|
|
color: AppColors.textPrimary,
|
|
marginTop: 2,
|
|
},
|
|
languagesCard: {
|
|
backgroundColor: AppColors.background,
|
|
},
|
|
languageOption: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
paddingVertical: Spacing.md,
|
|
paddingHorizontal: Spacing.lg,
|
|
},
|
|
languageOptionSelected: {
|
|
backgroundColor: '#DBEAFE',
|
|
},
|
|
flag: {
|
|
fontSize: 28,
|
|
},
|
|
languageInfo: {
|
|
flex: 1,
|
|
marginLeft: Spacing.md,
|
|
},
|
|
languageName: {
|
|
fontSize: FontSizes.base,
|
|
fontWeight: '500',
|
|
color: AppColors.textPrimary,
|
|
},
|
|
languageNameDisabled: {
|
|
color: AppColors.textMuted,
|
|
},
|
|
languageNative: {
|
|
fontSize: FontSizes.xs,
|
|
color: AppColors.textSecondary,
|
|
marginTop: 2,
|
|
},
|
|
comingSoonBadge: {
|
|
backgroundColor: AppColors.surface,
|
|
paddingHorizontal: Spacing.sm,
|
|
paddingVertical: 4,
|
|
borderRadius: BorderRadius.sm,
|
|
},
|
|
comingSoonText: {
|
|
fontSize: FontSizes.xs,
|
|
color: AppColors.textMuted,
|
|
},
|
|
divider: {
|
|
height: 1,
|
|
backgroundColor: AppColors.border,
|
|
marginLeft: Spacing.lg + 28 + Spacing.md,
|
|
},
|
|
helpTranslateCard: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
backgroundColor: AppColors.background,
|
|
marginHorizontal: Spacing.lg,
|
|
padding: Spacing.md,
|
|
borderRadius: BorderRadius.lg,
|
|
},
|
|
helpTranslateIcon: {
|
|
width: 48,
|
|
height: 48,
|
|
borderRadius: BorderRadius.md,
|
|
backgroundColor: '#DBEAFE',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
helpTranslateContent: {
|
|
flex: 1,
|
|
marginLeft: Spacing.md,
|
|
},
|
|
helpTranslateTitle: {
|
|
fontSize: FontSizes.base,
|
|
fontWeight: '500',
|
|
color: AppColors.textPrimary,
|
|
},
|
|
helpTranslateDescription: {
|
|
fontSize: FontSizes.xs,
|
|
color: AppColors.textMuted,
|
|
marginTop: 2,
|
|
},
|
|
noteContainer: {
|
|
flexDirection: 'row',
|
|
alignItems: 'flex-start',
|
|
marginHorizontal: Spacing.lg,
|
|
marginVertical: Spacing.lg,
|
|
},
|
|
noteText: {
|
|
flex: 1,
|
|
fontSize: FontSizes.xs,
|
|
color: AppColors.textMuted,
|
|
marginLeft: Spacing.sm,
|
|
lineHeight: 18,
|
|
},
|
|
});
|