Internationalize remaining TODO-marked UI text in Expo app

This commit is contained in:
Adolfo Reyna
2026-02-20 22:30:20 -05:00
parent a8d21d31f8
commit 06e620dbf6
16 changed files with 230 additions and 69 deletions

View File

@@ -4,6 +4,7 @@ import { StyleSheet, SafeAreaView, FlatList, View, ActivityIndicator } from 'rea
import API from "../API";
import GroupCard from "../components/GroupCard";
import AsyncStorage from '@react-native-async-storage/async-storage';
import i18n from "../i18nMessages.js";
const GROUPS_CACHE_KEY = 'groups_following';
@@ -113,7 +114,7 @@ const Groups = ({navigation}) => {
}} />
</View> :
<Searchbar
placeholder="Search Groups"
placeholder={i18n.t("message.searchGroups")}
onChangeText={onChangeSearch}
value={searchQuery}
clearButtonMode="while-editing"
@@ -125,7 +126,7 @@ const Groups = ({navigation}) => {
/>
}
</View>
<Title style={styles.title} >{searchQuery ? "Results:" : "Your Groups:"}</Title>
<Title style={styles.title} >{searchQuery ? i18n.t("message.results") : i18n.t("message.yourGroups")}</Title>
</>
}
style={{backgroundColor: "#edf2f7",}}

View File

@@ -72,10 +72,12 @@ let InviteView = ()=>{
onPress={()=>{setChecked(!checked)}}
/>
<Divider />
<Button mode="outlined" onPress={sendInvite}>Invite</Button>
<Button mode="outlined" onPress={sendInvite}>
{i18n.t("message.invite")}
</Button>
</ImageBackground>
</View>
)
}
export default InviteView;
export default InviteView;

View File

@@ -30,7 +30,7 @@ export default function App({ navigation, route }) {
style={styles.logo}
source={require('./../assets/icon.png')}
/>
<Text style={styles.header}>EMI Fellowship</Text>
<Text style={styles.header}>{i18n.t("message.appName")}</Text>
<View style={{ flexDirection: "row", justifyContent: "center", width: "100%" }}>
<Button disabled={isLogin} onPress={() => setIsLogin(true)}>
{i18n.t("message.login")}

View File

@@ -71,33 +71,33 @@ let MenuView = ({ navigation }) => {
>
<ProfileCardHorizontal profileObj={viewer} skipFollow={true} skiptOnPress={true} key={viewer._id} />
<List.Accordion
title="Change Active Profile"
title={i18n.t("message.changeActiveProfile")}
left={props => <List.Icon {...props} icon="published-with-changes" />}
>
{profileLists}
</List.Accordion>
<List.Section title="User Actions">
<List.Section title={i18n.t("message.userActions")}>
<List.Item key='ProfileEditor' title={i18n.t('message.profile')} onPress={() => { navigation.navigate("ProfileSettings") }} left={props => <List.Icon {...props} icon="person" />} />
<List.Item key='Settings' title={i18n.t('message.settings')} left={props => <List.Icon {...props} icon="settings" />} />
<List.Item key="Logout" title={i18n.t('message.logout')} onPress={() => { navigation.navigate("Logout") }} left={props => <List.Icon {...props} icon="logout" />} />
</List.Section>
<List.Section title="Fellowship App">
<List.Section title={i18n.t("message.fellowshipApp")}>
<List.Item key='Invite' title={i18n.t('message.invite')} onPress={() => { navigation.navigate("Invite") }} left={props => <List.Icon {...props} icon="person-add" />} />
<List.Item key='About' title={i18n.t('message.about')} left={props => <List.Icon {...props} icon="more" />} />
</List.Section>
<View style={{ padding: 10 }}>
<Text>App Language:</Text>
<Text>{i18n.t("message.appLanguage")}:</Text>
<RadioButton.Group onValueChange={newValue => changeLang(newValue)} value={value}>
<RadioButton.Item value="es" label="Español" />
<RadioButton.Item value="en" label="English" />
<RadioButton.Item value="da" label="Danish" />
<RadioButton.Item value="fr" label="French" />
<RadioButton.Item value="es" label={i18n.t("message.languageSpanish")} />
<RadioButton.Item value="en" label={i18n.t("message.languageEnglish")} />
<RadioButton.Item value="da" label={i18n.t("message.languageDanish")} />
<RadioButton.Item value="fr" label={i18n.t("message.languageFrench")} />
</RadioButton.Group>
</View>
<View style={{ padding: 10, alignContent: "center", flex: 1 }}>
<Text>Version: {Updates.runtimeVersion}</Text>
<Text>Channel: {Updates.Channel}</Text>
<Text>{i18n.t("message.version")}: {Updates.runtimeVersion}</Text>
<Text>{i18n.t("message.channel")}: {Updates.Channel}</Text>
</View>
</ImageBackground>
</ScrollView>

View File

@@ -3,6 +3,7 @@ import { StyleSheet, SafeAreaView, ImageBackground, View, ScrollView, Alert, Ima
import { Title, TextInput, Button, Text, Switch } from 'react-native-paper';
import API from "../API";
import * as ImagePicker from 'expo-image-picker';
import i18n from "../i18nMessages.js";
const DefaultPhoto = "https://social.emmint.com/uploads/e6f9be6d665dc43417701bf16a90122c.png";
@@ -108,33 +109,33 @@ const NewGroup = ({ navigation }) => {
imageStyle={styles.backgroundImage}
>
<ScrollView contentContainerStyle={styles.container}>
<Title style={styles.title}>New Group</Title>
<Title style={styles.title}>{i18n.t("message.newGroup")}</Title>
<View style={styles.photoRow}>
<Image
source={{ uri: selectedPhoto?.uri || DefaultPhoto }}
style={styles.photoPreview}
/>
<Button mode="outlined" icon="photo" onPress={pickImage} disabled={isSubmitting}>
{selectedPhoto ? "Change image" : "Add group image"}
{selectedPhoto ? i18n.t("message.changeImage") : i18n.t("message.addGroupImage")}
</Button>
</View>
<TextInput
mode="outlined"
label="Group name"
label={i18n.t("message.groupName")}
value={title}
onChangeText={setTitle}
style={styles.input}
/>
<TextInput
mode="outlined"
label="Subtitle (optional)"
label={i18n.t("message.subtitleOptional")}
value={subtitle}
onChangeText={setSubtitle}
style={styles.input}
/>
<TextInput
mode="outlined"
label="Description"
label={i18n.t("message.description")}
value={description}
onChangeText={setDescription}
multiline
@@ -143,8 +144,8 @@ const NewGroup = ({ navigation }) => {
/>
<View style={styles.switchRow}>
<View style={styles.switchTextWrap}>
<Text style={styles.switchTitle}>Private group</Text>
<Text style={styles.switchSubtitle}>Require approval before people can join.</Text>
<Text style={styles.switchTitle}>{i18n.t("message.privateGroup")}</Text>
<Text style={styles.switchSubtitle}>{i18n.t("message.requireApprovalBeforeJoin")}</Text>
</View>
<Switch value={isPrivate} onValueChange={setIsPrivate} />
</View>
@@ -155,7 +156,7 @@ const NewGroup = ({ navigation }) => {
disabled={isSubmitting}
style={styles.button}
>
Create Group
{i18n.t("message.createGroupAction")}
</Button>
</ScrollView>
</ImageBackground>

View File

@@ -170,7 +170,7 @@ let NewPostView = (props) => {
{
toProfile._id ?
<Text style={{ paddingLeft: 10, paddingBottom: 5 }}>
Posting on: {toProfile.profile?.firstName} {toProfile.profile?.lastName}
{i18n.t("message.postingOn")}: {toProfile.profile?.firstName} {toProfile.profile?.lastName}
</Text> : null
}
<Divider bold={true} />
@@ -178,7 +178,7 @@ let NewPostView = (props) => {
<TextInput
value={postContent}
onChangeText={setPostContent}
placeholder="What is on your mind today?"
placeholder={i18n.t("message.whatIsOnYourMindToday")}
multiline={true}
numberOfLines={8}
style={{
@@ -194,18 +194,18 @@ let NewPostView = (props) => {
{/* Button to pick images from the gallery */}
<View style={{ flexDirection: "row", marginTop: 10, justifyContent: "space-around" }}>
<Button icon="add-a-photo" mode="outlined" onPress={pickImage}>
Add Photos
{i18n.t("message.addPhotos")}
</Button>
{isUploading && (
<Button icon="cancel" mode="outlined" onPress={handleCancelUpload}>
Cancel Upload
{i18n.t("message.cancelUpload")}
</Button>
)}
</View>
{/* Display uploading state and selected image preview */}
{photo && (
<View>
<Text>Uploading...</Text>
<Text>{i18n.t("message.uploading")}</Text>
<Image
source={{ uri: photo.uri }}
style={{ width: 100, height: 100 }}
@@ -215,7 +215,7 @@ let NewPostView = (props) => {
{/* Display upload progress if in progress */}
{
uploadProgress > 0 && uploadProgress < 100 && (
<Text>Upload Progress: {uploadProgress.toFixed(2)}%</Text>
<Text>{i18n.t("message.uploadProgress")}: {uploadProgress.toFixed(2)}%</Text>
)
}
{/* Display media content if extra content is available */}
@@ -229,4 +229,4 @@ let NewPostView = (props) => {
)
}
export default NewPostView;
export default NewPostView;

View File

@@ -9,6 +9,7 @@ import ProfileHeader from '../components/ProfileHeader.js';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useSnapshot } from 'valtio';
import GlobalState from '../contexts/GlobalState.js';
import i18n from "../i18nMessages.js";
const PROFILE_LOG_PREFIX = '[Profile]';
const logProfile = (...args) => {
@@ -203,18 +204,18 @@ let Profile = ({ navigation, route }) => {
navigation.navigate('NewPost', {toProfile: profile._id})
}} />
</View>
<Button style={{paddingLeft:12, backgroundColor:"#fff"}} title="Images" icon={tag == 'images' ? 'remove' : "image"} mode="outlined" onPress={()=>{
<Button style={{paddingLeft:12, backgroundColor:"#fff"}} title={i18n.t("message.images")} icon={tag == 'images' ? 'remove' : "image"} mode="outlined" onPress={()=>{
if(tag == 'images') return setTag('');
setTag('images');
}}>{tag == 'images' ? "Images" : ''}</Button>
<Button style={{paddingLeft:12, backgroundColor:"#fff"}} title="Media" icon={tag == 'media' ? 'remove' : "subscriptions"} mode="outlined" onPress={()=>{
}}>{tag == 'images' ? i18n.t("message.images") : ''}</Button>
<Button style={{paddingLeft:12, backgroundColor:"#fff"}} title={i18n.t("message.media")} icon={tag == 'media' ? 'remove' : "subscriptions"} mode="outlined" onPress={()=>{
if(tag == 'media') return setTag('');
setTag('media');
}}>{tag == 'media' ? "Media" : ''}</Button>
<Button style={{paddingLeft:12, backgroundColor:"#fff"}} title="Embedded" icon={tag == 'embedded' ? 'remove' : "folder"} mode="outlined" onPress={()=>{
}}>{tag == 'media' ? i18n.t("message.media") : ''}</Button>
<Button style={{paddingLeft:12, backgroundColor:"#fff"}} title={i18n.t("message.embedded")} icon={tag == 'embedded' ? 'remove' : "folder"} mode="outlined" onPress={()=>{
if(tag == 'embedded') return setTag('');
setTag('embedded');
}}>{tag == 'embedded' ? "Files" : ''}</Button>
}}>{tag == 'embedded' ? i18n.t("message.files") : ''}</Button>
</View>
{isOwnedGroup ? (
<View style={styles.deleteGroupRow}>
@@ -226,7 +227,7 @@ let Profile = ({ navigation, route }) => {
disabled={isDeletingGroup}
onPress={handleDeleteGroup}
>
Delete Group
{i18n.t("message.deleteGroup")}
</Button>
</View>
) : <></>}

View File

@@ -41,12 +41,12 @@ let ProfileSettings = () => {
};
const languageOptions = [
{ value: "es", label: "Español" },
{ value: "en", label: "English" },
{ value: "da", label: "Danish" },
{ value: "fr", label: "French" },
{ value: "es", label: i18n.t("message.languageSpanish") },
{ value: "en", label: i18n.t("message.languageEnglish") },
{ value: "da", label: i18n.t("message.languageDanish") },
{ value: "fr", label: i18n.t("message.languageFrench") },
];
const currentLanguageLabel = languageOptions.find((opt) => opt.value === language)?.label || "English";
const currentLanguageLabel = languageOptions.find((opt) => opt.value === language)?.label || i18n.t("message.languageEnglish");
const pickImage = async () => {
if (uploading) return;
@@ -176,8 +176,10 @@ let ProfileSettings = () => {
<View style={{ paddingTop: 10 }}>
<ProfileCardHorizontal profileObj={previewProfile} skipFollow={true} skiptOnPress={true} key={updateKey} />
</View>
<Text style={{ marginBottom: 10, marginTop: 10, fontSize: 20 }}>Profile Setting</Text>
<Button icon="photo" mode="outlined" onPress={pickImage}>{!uploading ? i18n.t("message.updatePhoto") : "uploading"}</Button>
<Text style={{ marginBottom: 10, marginTop: 10, fontSize: 20 }}>{i18n.t("message.profileSetting")}</Text>
<Button icon="photo" mode="outlined" onPress={pickImage}>
{!uploading ? i18n.t("message.updatePhoto") : i18n.t("message.uploading")}
</Button>
<Divider />
<View style={{ flexDirection: "row", justifyContent: "space-between" }}>
<TextInput
@@ -201,7 +203,7 @@ let ProfileSettings = () => {
value={description}
onChangeText={text => setDescription(text)}
/>
<Text style={{ marginBottom: 4 }}>Language:</Text>
<Text style={{ marginBottom: 4 }}>{i18n.t("message.language")}:</Text>
<View style={{ marginBottom: 10 }}>
<Menu
visible={languageMenuVisible}

View File

@@ -6,6 +6,7 @@ import ProfileCardHorizontal from "../components/ProfileCardHorizontal";
import { useSnapshot } from 'valtio';
import GlobalState from '../contexts/GlobalState.js';
import ProfileHeader from "../components/ProfileHeader";
import i18n from "../i18nMessages.js";
const Search = () => {
const viewer = useSnapshot(GlobalState).me;
@@ -47,7 +48,7 @@ const Search = () => {
return (
<SafeAreaView style={{ flex: 1, backgroundColor: "#edf2f7", }}>
<Searchbar
placeholder="Search Users"
placeholder={i18n.t("message.searchUsers")}
onChangeText={onChangeSearch}
value={searchQuery}
/>
@@ -66,7 +67,7 @@ const Search = () => {
data={followers}
renderItem={renderFollowing}
keyExtractor={item => item}
ListHeaderComponent={<Text style={{fontSize:20, padding:10, alignSelf: "center"}}>Recently Following</Text>}
ListHeaderComponent={<Text style={{fontSize:20, padding:10, alignSelf: "center"}}>{i18n.t("message.recentlyFollowing")}</Text>}
/>
}
</SafeAreaView>

View File

@@ -43,23 +43,23 @@ let MenuView = ({ navigation }) => {
style={{ paddingTop: 10 }}
imageStyle={{ resizeMode: "contain", opacity: 0.05 }}
>
<List.Section title="Current Profile">
<List.Section title={i18n.t("message.currentProfile")}>
<ProfileCardHorizontal profileObj={viewer} skipFollow={true} skiptOnPress={true} />
</List.Section>
<List.Section title="User Actions">
<List.Section title={i18n.t("message.userActions")}>
<List.Item title={i18n.t('message.profile')} onPress={() => { navigation.navigate("ProfileSettings") }} left={props => <List.Icon {...props} icon="person" />} />
<List.Item title={i18n.t('message.settings')} left={props => <List.Icon {...props} icon="settings" />} />
<List.Item title={i18n.t('message.logout')} onPress={() => { navigation.navigate("Logout") }} left={props => <List.Icon {...props} icon="logout" />} />
</List.Section>
<List.Section title="Fellowship App">
<List.Section title={i18n.t("message.fellowshipApp")}>
<List.Item title={i18n.t('message.invite')} onPress={() => { navigation.navigate("Invite") }} left={props => <List.Icon {...props} icon="person-add" />} />
<List.Item title={i18n.t('message.about')} left={props => <List.Icon {...props} icon="more" />} />
</List.Section>
<View style={{ padding: 10 }}>
<Text>Language:</Text>
<Text>{i18n.t("message.language")}:</Text>
<RadioButton.Group onValueChange={newValue => changeLang(newValue)} value={value}>
<RadioButton.Item value="es" label="Español" />
<RadioButton.Item value="en" label="English" />
<RadioButton.Item value="es" label={i18n.t("message.languageSpanish")} />
<RadioButton.Item value="en" label={i18n.t("message.languageEnglish")} />
</RadioButton.Group>
</View>
</ImageBackground>
@@ -67,4 +67,4 @@ let MenuView = ({ navigation }) => {
)
}
export default MenuView;
export default MenuView;