Skip to content

Commit

Permalink
feat: add i18n support (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
IZUMI-Zu authored Nov 26, 2024
1 parent 5815f02 commit b87e441
Show file tree
Hide file tree
Showing 34 changed files with 1,761 additions and 103 deletions.
1 change: 1 addition & 0 deletions App.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {GestureHandlerRootView} from "react-native-gesture-handler";
import {useMigrations} from "drizzle-orm/expo-sqlite/migrator";
import {ActionSheetProvider} from "@expo/react-native-action-sheet";

import "./i18n";
import Header from "./Header";
import NavigationBar from "./NavigationBar";
import {db} from "./db/client";
Expand Down
28 changes: 22 additions & 6 deletions CasdoorLoginPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import EnterCasdoorSdkConfig from "./EnterCasdoorSdkConfig";
import ScanQRCodeForLogin from "./ScanLogin";
import useStore from "./useStorage";
import DefaultCasdoorSdkConfig from "./DefaultCasdoorSdkConfig";
import {useTranslation} from "react-i18next";

let sdk = null;

Expand All @@ -33,6 +34,7 @@ function CasdoorLoginPage({onWebviewClose, initialMethod}) {
};

const {notify} = useNotifications();
const {t} = useTranslation();
const [casdoorLoginURL, setCasdoorLoginURL] = useState("");
const [currentView, setCurrentView] = useState(initialMethod === "scan" ? "scanner" : "config");

Expand Down Expand Up @@ -113,11 +115,21 @@ function CasdoorLoginPage({onWebviewClose, initialMethod}) {
const userInfo = sdk.JwtDecode(accessToken);
setToken(accessToken);
setUserInfo(userInfo);
notify("success", {params: {title: "Success", description: "Logged in successfully!"}});
notify("success", {
params: {
title: t("common.success"),
description: t("casdoorLoginPage.Logged in successfully!"),
},
});
setCurrentView("config");
onWebviewClose();
} catch (error) {
notify("error", {params: {title: "Error in login", description: error.message}});
notify("error", {
params: {
title: t("common.error"),
description: error.message,
},
});
}
};

Expand All @@ -139,20 +151,25 @@ function CasdoorLoginPage({onWebviewClose, initialMethod}) {
}}
onLogin={handleQRLogin}
onError={(message) => {
notify("error", {params: {title: "Error", description: message}});
notify("error", {params: {title: t("common.error"), description: message}});
}}
/>
),
webview: casdoorLoginURL && !token && (
<SafeAreaView style={styles.safeArea}>
<TouchableOpacity style={styles.backButton} onPress={() => setCurrentView("config")}>
<Text style={styles.backButtonText}>Back to Config</Text>
<Text style={styles.backButtonText}>{t("casdoorLoginPage.Back to Config")}</Text>
</TouchableOpacity>
<WebView
source={{uri: casdoorLoginURL}}
onNavigationStateChange={onNavigationStateChange}
onError={({nativeEvent}) => {
notify("error", {params: {title: "Error", description: nativeEvent.description}});
notify("error", {
params: {
title: t("common.error"),
description: nativeEvent.description,
},
});
setCurrentView("config");
}}
style={styles.webview}
Expand Down Expand Up @@ -186,7 +203,6 @@ const styles = StyleSheet.create({
flex: 1,
backgroundColor: "white",
paddingTop: Platform.OS === "android" ? StatusBar.currentHeight : 0,

},
});

Expand Down
6 changes: 4 additions & 2 deletions EditAccountDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import React, {useState} from "react";
import {Text, TextInput, View} from "react-native";
import {Button, IconButton} from "react-native-paper";
import PropTypes from "prop-types";
import {useTranslation} from "react-i18next";

export default function EnterAccountDetails({onClose, onEdit, placeholder}) {
EnterAccountDetails.propTypes = {
Expand All @@ -24,6 +25,7 @@ export default function EnterAccountDetails({onClose, onEdit, placeholder}) {
placeholder: PropTypes.string.isRequired,
};

const {t} = useTranslation();
const [accountName, setAccountName] = useState("");

const handleConfirm = () => {
Expand All @@ -32,7 +34,7 @@ export default function EnterAccountDetails({onClose, onEdit, placeholder}) {

return (
<View style={{flex: 1, justifyContent: "center", alignItems: "center"}}>
<Text style={{fontSize: 24, marginBottom: 5}}>Enter new account name</Text>
<Text style={{fontSize: 24, marginBottom: 5}}>{t("editAccount.Enter new account name")}</Text>
<View style={{flexDirection: "row", alignItems: "center"}}>
<IconButton icon="account-details" size={35} />
<TextInput
Expand All @@ -55,7 +57,7 @@ export default function EnterAccountDetails({onClose, onEdit, placeholder}) {
}}
onPress={handleConfirm}
>
<Text style={{fontSize: 18, width: 280}}>Confirm</Text>
<Text style={{fontSize: 18, width: 280}}>{t("common.confirm")}</Text>
</Button>
<IconButton icon={"close"} size={30} onPress={onClose} style={{position: "absolute", top: 5, right: 5}} />
</View>
Expand Down
35 changes: 21 additions & 14 deletions EnterAccountDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {View} from "react-native";
import {Button, IconButton, Menu, Text, TextInput} from "react-native-paper";
import {useNotifications} from "react-native-notificated";
import PropTypes from "prop-types";
import {useTranslation} from "react-i18next";

const EnterAccountDetails = ({onClose, onAdd, validateSecret}) => {
EnterAccountDetails.propTypes = {
Expand All @@ -26,7 +27,7 @@ const EnterAccountDetails = ({onClose, onAdd, validateSecret}) => {
};

const {notify} = useNotifications();

const {t} = useTranslation();
const [accountName, setAccountName] = useState("");
const [secretKey, setSecretKey] = useState("");
const [secretError, setSecretError] = useState("");
Expand All @@ -45,25 +46,25 @@ const EnterAccountDetails = ({onClose, onAdd, validateSecret}) => {

const handleAddAccount = useCallback(() => {
if (accountName.trim() === "") {
setAccountNameError("Account Name is required");
setAccountNameError(t("editAccount.Account Name is required"));
}

if (secretKey.trim() === "") {
setSecretError("Secret Key is required");
setSecretError(t("editAccount.Secret Key is required"));
}

if (accountName.trim() === "" || secretKey.trim() === "") {
notify("error", {
title: "Error",
description: "Please fill in all the fields!",
title: t("common.error"),
description: t("editAccount.Please fill in all the fields!"),
});
return;
}

if (secretError) {
notify("error", {
title: "Error",
description: "Invalid Secret Key",
title: t("common.error"),
description: t("editAccount.Invalid Secret Key"),
});
return;
}
Expand Down Expand Up @@ -94,7 +95,7 @@ const EnterAccountDetails = ({onClose, onAdd, validateSecret}) => {
<View style={styles.container}>
<View style={styles.content}>
<View style={styles.header}>
<Text style={styles.title}>Add Account</Text>
<Text style={styles.title}>{t("editAccount.Add Account")}</Text>
<IconButton
icon="close"
size={24}
Expand All @@ -103,15 +104,15 @@ const EnterAccountDetails = ({onClose, onAdd, validateSecret}) => {
/>
</View>
<TextInput
label="Account Name"
label={t("editAccount.Account Name")}
value={accountName}
onChangeText={handleAccountNameChange}
error={!!accountNameError}
style={styles.input}
mode="outlined"
/>
<TextInput
label="Secret Key"
label={t("editAccount.Secret Key")}
value={secretKey}
onChangeText={handleSecretKeyChange}
secureTextEntry={!showPassword}
Expand All @@ -137,21 +138,27 @@ const EnterAccountDetails = ({onClose, onAdd, validateSecret}) => {
contentStyle={styles.menuButtonContent}
style={styles.menuButton}
>
{selectedItem}
{t(`editAccount.${selectedItem}`)}
</Button>
}
contentStyle={styles.menuContent}
>
<Menu.Item onPress={() => handleMenuItemPress("Time based")} title="Time based" />
<Menu.Item onPress={() => handleMenuItemPress("Counter based")} title="Counter based" />
<Menu.Item
onPress={() => handleMenuItemPress("Time based")}
title={t("editAccount.Time based")}
/>
<Menu.Item
onPress={() => handleMenuItemPress("Counter based")}
title={t("editAccount.Counter based")}
/>
</Menu>
<Button
mode="contained"
onPress={handleAddAccount}
style={styles.addButton}
labelStyle={styles.buttonLabel}
>
Add Account
{t("editAccount.Add Account")}
</Button>
</View>
</View>
Expand Down
20 changes: 11 additions & 9 deletions EnterCasdoorSdkConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ import {Button, Portal, TextInput} from "react-native-paper";
import {useNotifications} from "react-native-notificated";
import PropTypes from "prop-types";
import useStore from "./useStorage";
import {useTranslation} from "react-i18next";

function EnterCasdoorSdkConfig({onClose, onWebviewClose, usePortal = true}) {
const {t} = useTranslation();
const {
serverUrl,
clientId,
Expand All @@ -43,8 +45,8 @@ function EnterCasdoorSdkConfig({onClose, onWebviewClose, usePortal = true}) {
if (!serverUrl || !clientId || !appName || !organizationName || !redirectPath) {
notify("error", {
params: {
title: "Error",
description: "Please fill in all the fields!",
title: t("common.error"),
description: t("enterCasdoorSDKConfig.Please fill in all the fields!"),
},
});
return;
Expand All @@ -55,34 +57,34 @@ function EnterCasdoorSdkConfig({onClose, onWebviewClose, usePortal = true}) {
const content = (
<View style={styles.container}>
<View style={styles.content}>
<Text style={styles.title}>Casdoor Configuration</Text>
<Text style={styles.title}>{t("enterCasdoorSDKConfig.Casdoor Configuration")}</Text>
<View style={styles.formContainer}>
<TextInput
label="Endpoint"
label={t("enterCasdoorSDKConfig.Server URL")}
value={serverUrl}
onChangeText={setServerUrl}
autoCapitalize="none"
style={styles.input}
mode="outlined"
/>
<TextInput
label="Client ID"
label={t("enterCasdoorSDKConfig.Client ID")}
value={clientId}
onChangeText={setClientId}
autoCapitalize="none"
style={styles.input}
mode="outlined"
/>
<TextInput
label="App Name"
label={t("enterCasdoorSDKConfig.Application Name")}
value={appName}
onChangeText={setAppName}
autoCapitalize="none"
style={styles.input}
mode="outlined"
/>
<TextInput
label="Organization Name"
label={t("enterCasdoorSDKConfig.Organization Name")}
value={organizationName}
onChangeText={setOrganizationName}
autoCapitalize="none"
Expand All @@ -97,15 +99,15 @@ function EnterCasdoorSdkConfig({onClose, onWebviewClose, usePortal = true}) {
style={styles.button}
labelStyle={styles.buttonLabel}
>
Cancel
{t("common.cancel")}
</Button>
<Button
mode="contained"
onPress={handleSave}
style={styles.button}
labelStyle={styles.buttonLabel}
>
Confirm
{t("common.confirm")}
</Button>
</View>
</View>
Expand Down
13 changes: 7 additions & 6 deletions Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import CasdoorLoginPage, {CasdoorLogout} from "./CasdoorLoginPage";
import useStore from "./useStorage";
import {useAccountSync} from "./useAccountStore";
import LoginMethodSelector from "./LoginMethodSelector";
import {useTranslation} from "react-i18next";

const {width} = Dimensions.get("window");

Expand All @@ -31,7 +32,7 @@ const Header = () => {
const [menuVisible, setMenuVisible] = React.useState(false);
const [loginMethod, setLoginMethod] = React.useState(null);
const {notify} = useNotifications();

const {t} = useTranslation();
const openMenu = () => setMenuVisible(true);
const closeMenu = () => setMenuVisible(false);

Expand Down Expand Up @@ -64,8 +65,8 @@ const Header = () => {
const handleSyncErrorPress = () => {
notify("error", {
params: {
title: "Error",
description: syncError || "An unknown error occurred during synchronization.",
title: t("common.error"),
description: syncError || t("header.An unknown error occurred during synchronization"),
},
});
};
Expand All @@ -75,7 +76,7 @@ const Header = () => {
<Appbar.Content
title={
<View style={styles.titleContainer}>
<Text style={styles.titleTextCasdoor}>Casdoor</Text>
<Text style={styles.titleTextCasdoor}>{t("header.Casdoor")}</Text>
</View>
}
style={styles.titleWrapper}
Expand Down Expand Up @@ -119,13 +120,13 @@ const Header = () => {
styles.buttonText,
userInfo === null && {marginLeft: 0},
]}>
{userInfo === null ? "Login" : userInfo.name}
{userInfo === null ? t("common.login") : userInfo.name}
</Text>
</View>
</TouchableRipple>
}
>
<Menu.Item onPress={handleMenuLogoutClicked} title="Logout" />
<Menu.Item onPress={handleMenuLogoutClicked} title={t("common.logout")} />
</Menu>
</View>
{showLoginPage && (
Expand Down
Loading

0 comments on commit b87e441

Please sign in to comment.