Browse Source

没有登录也可以翻译

main
Mac 2 weeks ago
parent
commit
acf47e9ab2
  1. 1
      app/constants/config.ts
  2. 7
      app/locales/en/translation.json
  3. 37
      app/locales/fr/translation.json
  4. 21
      app/locales/zh/translation.json
  5. 8
      app/navigation/TabNavigator.tsx
  6. 4
      app/screens/HomeScreen.tsx
  7. 211
      app/screens/ImageSearchResultScreen.tsx
  8. 2
      app/screens/loginList/PhoneLoginModal.tsx
  9. 106
      app/screens/setting/CountrySetting.tsx
  10. 6
      app/services/api/productApi.ts
  11. 50
      app/utils/languageUtils.ts
  12. 42
      app/utils/storage.ts

1
app/constants/config.ts

@ -18,6 +18,7 @@ export const STORAGE_KEYS = {
AUTH_TOKEN: 'auth_token',
USER_INFO: 'user_info',
LANGUAGE: 'app_language',
CURRENCY: 'app_currency',
};
// 默认请求头

7
app/locales/en/translation.json

@ -97,6 +97,7 @@
"my":"My",
"categories": "Categories",
"chat": {
"tab_label": "Chat",
"customer_service": "Customer Service",
"product_support": "Product Support",
"notifications": "Notifications",
@ -181,6 +182,12 @@
},
"settings": {
"title": "Settings",
"country": "Country",
"currency": "Currency",
"language": "Language",
"success": "Settings saved successfully",
"login_required": "Please login first",
"confirm": "Confirm",
"profile": "My Profile",
"change_password": "Change Password",
"change_phone": "Change Phone Number",

37
app/locales/fr/translation.json

@ -97,6 +97,7 @@
"my":"Mon",
"categories":"Catégories",
"chat": {
"tab_label": "Chat",
"customer_service": "Service Client",
"product_support": "Support Produit",
"notifications": "Notifications",
@ -114,13 +115,27 @@
"sales":"ventes",
"productPicture":"Image du produit",
"no_stock": "Pas de stock",
"setting": {
"title": "Sélectionner la langue et la devise",
"settings": {
"title": "Paramètres",
"country": "Pays",
"currency": "Devise",
"language": "Langue",
"confirm": "Confirmer",
"success": "Paramètres enregistrés avec succès"
"success": "Paramètres enregistrés avec succès",
"login_required": "Veuillez vous connecter d'abord",
"profile": "Mon profil",
"change_password": "Modifier le mot de passe",
"change_phone": "Changer le numéro de téléphone",
"my_address": "Mon adresse",
"feedback": "Commentaires",
"privacy_policy": "Politique de confidentialité de Brainnel",
"terms_of_use": "Conditions d'utilisation",
"clear_cache": "Vider le cache",
"language_currency": "Sélectionner la langue et la devise",
"add_new_address": "Ajouter une nouvelle adresse",
"address_management": "Gestion des adresses",
"set_default": "Définir comme adresse par défaut",
"delete": "Supprimer"
},
"login.now": "Se connecter maintenant",
"login.button": "Connexion",
@ -307,22 +322,6 @@
"submit_order": "Soumettre la commande",
"select_payment": "Moyens de paiement"
},
"settings": {
"title": "Paramètres",
"profile": "Mon profil",
"change_password": "Modifier le mot de passe",
"change_phone": "Changer le numéro de téléphone",
"my_address": "Mon adresse",
"feedback": "Commentaires",
"privacy_policy": "Politique de confidentialité de Brainnel",
"terms_of_use": "Conditions d'utilisation",
"clear_cache": "Vider le cache",
"language_currency": "Sélectionner la langue et la devise",
"add_new_address": "Ajouter une nouvelle adresse",
"address_management": "Gestion des adresses",
"set_default": "Définir comme adresse par défaut",
"delete": "Supprimer"
},
"login": {
"logInOrSignUp": "Se connecter ou s'inscrire",
"phoneNumber": "Numéro de téléphone",

21
app/locales/zh/translation.json

@ -96,6 +96,7 @@
"my": "我的",
"categories": "分类",
"chat": {
"tab_label": "聊天",
"customer_service": "客服聊天",
"product_support": "产品咨询",
"notifications": "消息通知",
@ -114,13 +115,27 @@
"no_stock": "缺货",
"loginRequired": "需要登录",
"loginPrompt": "请登录以访问此功能。登录后您将获得更好的体验并能跟踪您的订单。",
"setting": {
"title": "选择语言和货币",
"settings": {
"title": "设置",
"country": "国家",
"currency": "货币",
"language": "语言",
"confirm": "确认",
"success": "设置成功"
"success": "设置成功",
"login_required": "请先登录",
"profile": "我的资料",
"change_password": "修改密码",
"change_phone": "更换手机号",
"my_address": "我的地址",
"feedback": "意见反馈",
"privacy_policy": "Brainnel隐私政策",
"terms_of_use": "使用条款",
"clear_cache": "清除缓存",
"language_currency": "选择语言和货币",
"add_new_address": "添加新地址",
"address_management": "地址管理",
"set_default": "设为默认地址",
"delete": "删除"
},
"homePage": {
"searchPlaceholder": "搜索商品",

8
app/navigation/TabNavigator.tsx

@ -186,7 +186,7 @@ export const TabNavigator = () => {
name="Chat"
component={ChatScreen}
options={{
tabBarLabel: t('chat'),
tabBarLabel: t('chat.tab_label'),
tabBarIcon: ({ color, size }: TabBarIconProps) => (
<IconComponent name="chatbubble-outline" size={size} color={color} />
),
@ -251,11 +251,11 @@ export const TabNavigator = () => {
<Text style={styles.closeButtonText}>×</Text>
</TouchableOpacity>
<Text style={styles.modalTitle}>{t('login.loginRequired')}</Text>
<Text style={styles.modalText}>{t('login.loginPrompt')}</Text>
<Text style={styles.modalTitle}>{t('loginRequired')}</Text>
<Text style={styles.modalText}>{t('loginPrompt')}</Text>
<TouchableOpacity style={styles.modalButton} onPress={handleGoToLogin}>
<Text style={styles.modalButtonText}>{t('login.loginNow')}</Text>
<Text style={styles.modalButtonText}>{t('loginNow')}</Text>
</TouchableOpacity>
</Animated.View>
</View>

4
app/screens/HomeScreen.tsx

@ -657,7 +657,7 @@ export const HomeScreen = () => {
</View>
<View style={styles.beautyProductCard}>
<Text style={styles.beautyProductTitle} numberOfLines={2} ellipsizeMode="tail">
{getSubjectTransLanguage(item)}
{getSubjectTransLanguage(item) || item.subject_trans}
</Text>
<View style={styles.beautyProductInfoRow}>
<View style={styles.flexRowCentered}>
@ -673,7 +673,7 @@ export const HomeScreen = () => {
</View>
</View>
</View>
<Text style={styles.beautySalesInfo}>{item.sold_out || "0"}+ ventes</Text>
<Text style={styles.beautySalesInfo}>{item.sold_out || "0"}+ {t("homePage.sales")}</Text>
</View>
</TouchableOpacity>
);

211
app/screens/ImageSearchResultScreen.tsx

@ -1,4 +1,10 @@
import React, { useState, useEffect, useCallback, useRef, useMemo } from "react";
import React, {
useState,
useEffect,
useCallback,
useRef,
useMemo,
} from "react";
import {
View,
Text,
@ -17,7 +23,11 @@ import Ionicons from "@expo/vector-icons/Ionicons";
import { useNavigation, useRoute } from "@react-navigation/native";
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
import { RouteProp } from "@react-navigation/native";
import { productApi, type Product, ProductParams } from "../services/api/productApi";
import {
productApi,
type Product,
ProductParams,
} from "../services/api/productApi";
import { useTranslation } from "react-i18next";
import isSmallScreen from "../utils/isSmallScreen";
import { Svg, Path } from "react-native-svg";
@ -230,113 +240,152 @@ export const ImageSearchResultScreen = ({
const [isFilterVisible, setIsFilterVisible] = useState(false);
const [sortOrder, setSortOrder] = useState<"asc" | "desc" | null>(null);
const [sortField, setSortField] = useState<"price" | "time">("price");
const [activeTab, setActiveTab] = useState<"default" | "volume" | "price">("default");
const [activeTab, setActiveTab] = useState<"default" | "volume" | "price">(
"default"
);
// 获取初始图片URI
const imageUri = useMemo(() => {
console.log('获取图片URI', route.params?.image);
console.log("获取图片URI", route.params?.image);
return route.params?.image || null;
}, [route.params?.image]);
// 将图片URI转换为FormData
const uriToFormData = async (uri: string) => {
// 将图片URI转换为base64
const uriToBase64 = async (uri: string): Promise<string> => {
try {
console.log('开始转换图片', uri);
const formData = new FormData();
const filename = uri.split("/").pop() || "image.jpg";
const match = /\.(\w+)$/.exec(filename);
const type = match ? `image/${match[1]}` : "image/jpeg";
console.log("开始转换图片为base64", uri);
const imageUri = Platform.OS === "ios" ? uri.replace("file://", "") : uri;
const imageFetchResponse = await fetch(imageUri);
const imageBlob = await imageFetchResponse.blob();
formData.append("image", imageBlob, filename);
console.log('图片转换完成');
return formData;
// 转换为base64
const reader = new FileReader();
const base64Promise = new Promise<string>((resolve, reject) => {
reader.onload = () => {
const result = reader.result as string;
resolve(result);
};
reader.onerror = reject;
reader.readAsDataURL(imageBlob);
});
const base64Data = await base64Promise;
// 记录图片大小信息
console.log(
"原始Blob大小:",
imageBlob.size,
"bytes =",
(imageBlob.size / 1024).toFixed(2),
"KB"
);
console.log("Base64数据长度:", base64Data.length, "字符");
console.log(
"Base64数据大小:",
(base64Data.length * 0.75).toFixed(0),
"bytes ≈",
((base64Data.length * 0.75) / 1024).toFixed(2),
"KB"
);
// 提取base64数据部分(去掉data:image/jpeg;base64,前缀)
const base64String = base64Data.split(",")[1];
console.log("纯Base64字符串长度:", base64String.length);
console.log("图片转换为base64完成");
return base64String;
} catch (error) {
console.error('图片转换出错:', error);
console.error("图片转换base64出错:", error);
throw error;
}
};
// 搜索图片
const searchByImage = async (uri: string) => {
if (!uri) {
console.log('没有有效的图片URI');
console.log("没有有效的图片URI");
setLoading(false);
setShowSkeleton(false);
return;
}
try {
console.log('开始搜索图片:', uri);
console.log("开始搜索图片:", uri);
console.log("用户信息:", userStore.user);
setLoading(true);
setShowSkeleton(true);
const formData = await uriToFormData(uri);
const base64String = await uriToBase64(uri);
const userId = userStore.user?.user_id || null;
const data = {
formData: formData as FormData,
user_id: userStore.user?.user_id,
type: 'formData',
image_base64: base64String,
user_id: userId,
};
console.log('调用图片搜索API');
console.log("调用图片搜索API,用户ID:", userId);
const response = await productApi.searchByImage(data);
console.log('图片搜索完成,返回数据:', JSON.stringify(response));
console.log("图片搜索完成,返回数据:", JSON.stringify(response));
// 确保我们有有效的产品数组
const productList = Array.isArray(response) ? response : [];
setProducts(productList);
setOriginalProducts(productList);
// 立即更新状态,无需等待
setLoading(false);
setShowSkeleton(false);
} catch (error) {
console.error('图片搜索出错:', error);
} catch (error: any) {
console.error("图片搜索出错:", error);
// 更详细的错误处理
if (error.code === "ERR_NETWORK") {
console.error("网络连接错误,请检查:");
console.error("1. 手机是否连接到同一WiFi网络");
console.error("2. 开发服务器是否正在运行");
console.error("3. 后端API服务是否正常");
}
setProducts([]);
setOriginalProducts([]);
setLoading(false);
setShowSkeleton(false);
}
};
// 只在组件加载时执行一次搜索
useEffect(() => {
console.log('imageUri', imageUri);
console.log("imageUri", imageUri);
console.log(imageUri);
// 重置状态,确保每次都是新的开始
imageProcessed.current = false;
setLoading(true);
setShowSkeleton(true);
// 如果没有图片URI,立即结束加载状态
if (!imageUri) {
console.log('没有图片URI,结束加载');
console.log("没有图片URI,结束加载");
setLoading(false);
setShowSkeleton(false);
return;
}
// 如果已经处理过图片,则跳过
if (imageProcessed.current) {
console.log('已处理过图片,跳过');
console.log("已处理过图片,跳过");
return;
}
console.log('首次加载,处理图片', imageUri);
console.log("首次加载,处理图片", imageUri);
imageProcessed.current = true;
// 执行图片搜索
searchByImage(imageUri);
}, [imageUri]);
// 搜索产品的API调用
const searchProducts = useCallback(
async (keyword: string, isLoadMore = false) => {
@ -356,7 +405,7 @@ export const ImageSearchResultScreen = ({
language: "en",
user_id: userStore.user?.user_id,
};
const res = await productApi.getSearchProducts(params);
if (isLoadMore) {
setProducts((prev) => [...prev, ...res.products]);
@ -384,7 +433,7 @@ export const ImageSearchResultScreen = ({
},
[userStore.user]
);
// 处理搜索提交
const handleSearch = useCallback(() => {
if (searchText.trim()) {
@ -398,12 +447,12 @@ export const ImageSearchResultScreen = ({
searchProducts(searchText.trim());
}
}, [searchText, searchProducts]);
// 切换筛选器显示状态
const toggleFilter = useCallback(() => {
setIsFilterVisible(!isFilterVisible);
}, [isFilterVisible]);
// 处理点击产品
const handleProductPress = useCallback(
(product: Product) => {
@ -414,26 +463,24 @@ export const ImageSearchResultScreen = ({
},
[navigation]
);
// 返回上一页
const goBack = useCallback(() => {
navigation.goBack();
}, [navigation]);
// 渲染列表为空时的组件
const renderEmptyList = useCallback(
() => (
<View style={styles.emptyContainer}>
<IconComponent name="image-outline" size={48} color="#ccc" />
<Text style={styles.emptyText}>
{t("noResults")}
</Text>
<Text style={styles.emptyText}>{t("noResults")}</Text>
<Text style={styles.emptySubtext}>{t("tryDifferentImage")}</Text>
</View>
),
[t]
);
// 渲染产品项
const renderProductItem = useCallback(
({ item }: { item: Product }) => (
@ -446,29 +493,29 @@ export const ImageSearchResultScreen = ({
),
[handleProductPress, t, userStore]
);
// 创建产品列表项的key提取器
const keyExtractor = useCallback(
(item: Product, index: number) => `${item.offer_id}-${index}`,
[]
);
// 处理滚动事件
const handleScroll = useCallback((event: any) => {
const offsetY = event.nativeEvent.contentOffset.y;
// 当滚动超过屏幕高度的一半时显示回到顶部按钮
setShowBackToTop(offsetY > 300);
}, []);
// 回到顶部
const scrollToTop = useCallback(() => {
flatListRef.current?.scrollToOffset({ offset: 0, animated: true });
}, []);
// 渲染骨架屏网格
const renderSkeletonGrid = useCallback(() => {
const skeletonArray = Array(8).fill(null);
return (
<View style={styles.productGrid}>
<FlatList
@ -482,7 +529,7 @@ export const ImageSearchResultScreen = ({
</View>
);
}, []);
// 处理排序
const handleSort = useCallback(
(field: "price" | "time", order: "asc" | "desc") => {
@ -510,7 +557,7 @@ export const ImageSearchResultScreen = ({
},
[]
);
// 处理标签切换
const handleTabChange = useCallback(
(tab: "default" | "volume" | "price") => {
@ -546,7 +593,7 @@ export const ImageSearchResultScreen = ({
},
[handleSort, activeTab, sortOrder, originalProducts, scrollToTop]
);
// 渲染列表底部加载更多
const renderFooter = useCallback(() => {
if (!hasMore)
@ -564,7 +611,7 @@ export const ImageSearchResultScreen = ({
);
return <View style={styles.footerSpace} />;
}, [loadingMore, hasMore, t]);
return (
<SafeAreaView style={styles.safeArea}>
<StatusBar barStyle="dark-content" backgroundColor="#fff" />
@ -604,7 +651,7 @@ export const ImageSearchResultScreen = ({
)}
</View>
</View>
{/* 标签筛选 */}
<View style={styles.tabContainer}>
<TouchableOpacity
@ -620,7 +667,7 @@ export const ImageSearchResultScreen = ({
activeTab === "default" && styles.activeTabText,
]}
>
{t('default')}
{t("default")}
</Text>
</TouchableOpacity>
<TouchableOpacity
@ -636,7 +683,7 @@ export const ImageSearchResultScreen = ({
activeTab === "volume" && styles.activeTabText,
]}
>
{t('volume')}
{t("volume")}
</Text>
</TouchableOpacity>
<TouchableOpacity
@ -653,12 +700,14 @@ export const ImageSearchResultScreen = ({
activeTab === "price" && styles.activeTabText,
]}
>
{t('price')}
{t("price")}
</Text>
{activeTab === "price" && (
<View style={styles.tabIcon}>
<IconComponent
name={sortOrder === "desc" ? "chevron-down" : "chevron-up"}
name={
sortOrder === "desc" ? "chevron-down" : "chevron-up"
}
size={16}
color="#000"
/>
@ -667,7 +716,7 @@ export const ImageSearchResultScreen = ({
</View>
</TouchableOpacity>
</View>
{/* 搜索结果 */}
<View style={styles.resultsContainer}>
{/* 搜索结果标题栏和排序选项 */}
@ -739,7 +788,7 @@ export const ImageSearchResultScreen = ({
</View>
<View style={styles.sortDivider} />
<View style={styles.sortGroup}>
<Text style={styles.sortLabel}>{t('time')}:</Text>
<Text style={styles.sortLabel}>{t("time")}:</Text>
<View style={styles.sortButtons}>
<TouchableOpacity
style={[
@ -888,12 +937,12 @@ const styles = StyleSheet.create({
clearButton: {
position: "absolute",
right: 10,
top: '50%',
top: "50%",
marginTop: -10,
width: 20,
height: 20,
alignItems: 'center',
justifyContent: 'center',
alignItems: "center",
justifyContent: "center",
zIndex: 20,
},
titleContainer: {
@ -1142,4 +1191,4 @@ const styles = StyleSheet.create({
backgroundColor: "#EAEAEA",
borderRadius: 4,
},
});
});

2
app/screens/loginList/PhoneLoginModal.tsx

@ -235,7 +235,7 @@ const PhoneLoginModal = ({ visible, onClose }: PhoneLoginModalProps) => {
if (res.access_token) {
const token = res.token_type + " " + res.access_token;
await AsyncStorage.setItem("token", token);
const data = await settingApi.postFirstLogin(221);
const data = await settingApi.postFirstLogin(selectedCountry?.country || 221);
setSettings(data);
const user = await userApi.getProfile();
setUser(user);

106
app/screens/setting/CountrySetting.tsx

@ -16,10 +16,8 @@ import { useTranslation } from "react-i18next";
import { useGlobalStore } from "../../store/useGlobalStore";
import { userApi } from "../../services/api/userApi";
import useUserStore from "../../store/user";
import { saveCurrency, saveLanguage } from "../../utils/storage";
// Define CountryList type to match API response
type CountryList = {
country: number;
currency: string;
@ -37,14 +35,14 @@ export const CountrySetting = () => {
const navigation =
useNavigation<NativeStackNavigationProp<RootStackParamList>>();
const route = useRoute<RouteProp<RootStackParamList, "CountrySetting">>();
const [changeType, setChangeType] = useState<string>("country");
const [changeType, setChangeType] = useState<string>("language");
const [countryList, setCountryList] = useState<CountryList[]>([]);
const [currencyList, setCurrencyList] = useState<string[]>([]);
const [languageList, setLanguageList] = useState<string[]>([]);
const [country, setCountry] = useState<number>(0);
const [currency, setCurrency] = useState<string>("");
const [language, setLanguage] = useState<string>("");
const { setUser } = useUserStore();
const { user, setUser } = useUserStore();
const getCountry = async () => {
const res = await settingApi.getCountryList();
@ -70,8 +68,16 @@ export const CountrySetting = () => {
getLanguage();
}, []);
useEffect(() => {
// 根据用户登录状态设置初始选项卡
if (user?.user_id && changeType === "language") {
setChangeType("country");
} else if (!user?.user_id) {
setChangeType("language");
}
}, [user?.user_id]);
const putSettinghandel = async () => {
// Only include the property that corresponds to the active tab
let data = {};
if (changeType === "country") {
data = { country: country };
@ -79,23 +85,40 @@ export const CountrySetting = () => {
} else if (changeType === "currency") {
data = { currency: currency };
setGlobalCurrency({ currency: currency });
await saveCurrency(currency);
} else if (changeType === "language") {
data = { language: language };
setGlobalLanguage({ language: language });
await saveLanguage(language);
}
Toast.show({
text1: t('setting.success'),
text1: t('settings.success'),
type: "success",
visibilityTime: 1000,
});
await settingApi.putSetting(data);
if (changeType === "language" && language) {
await changeLanguage(language);
// 只有在用户已登录的情况下才调用服务器接口
if (user?.user_id) {
try {
await settingApi.putSetting(data);
if (changeType === "language" && language) {
await changeLanguage(language);
}
eventBus.emit("refreshSetting");
const userData = await userApi.getProfile();
setUser(userData);
} catch (error) {
console.error('保存设置到服务器失败:', error);
// 即使服务器保存失败,本地设置已经保存,不影响用户体验
}
} else {
// 用户未登录,只保存到本地
if (changeType === "language" && language) {
await changeLanguage(language);
}
console.log('用户未登录,设置已保存到本地');
}
eventBus.emit("refreshSetting");
const userData = await userApi.getProfile();
setUser(userData);
};
return (
<SafeAreaView style={styles.safeArea}>
@ -108,36 +131,43 @@ export const CountrySetting = () => {
>
<BackIcon size={fontSize(24)} />
</TouchableOpacity>
<Text style={styles.title}>{t('setting.title')}</Text>
<Text style={styles.title}>{t('settings.title')}</Text>
<View style={styles.placeholder} />
</View>
<View style={styles.changeType}>
<TouchableOpacity
style={[
styles.changeTypeText,
changeType === "country" && styles.changeTypeTextActive,
]}
onPress={() => setChangeType("country")}
>
<Text style={styles.changeTypeTextTitle}>{t('setting.country')}</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.changeTypeText,
changeType === "currency" && styles.changeTypeTextActive,
]}
onPress={() => setChangeType("currency")}
>
<Text style={styles.changeTypeTextTitle}>{t('setting.currency')}</Text>
</TouchableOpacity>
{user?.user_id && (
<TouchableOpacity
style={[
styles.changeTypeText,
changeType === "country" && styles.changeTypeTextActive,
user?.user_id ? styles.changeTypeTextLoggedIn : {},
]}
onPress={() => setChangeType("country")}
>
<Text style={styles.changeTypeTextTitle}>{t('settings.country')}</Text>
</TouchableOpacity>
)}
{user?.user_id && (
<TouchableOpacity
style={[
styles.changeTypeText,
changeType === "currency" && styles.changeTypeTextActive,
user?.user_id ? styles.changeTypeTextLoggedIn : {},
]}
onPress={() => setChangeType("currency")}
>
<Text style={styles.changeTypeTextTitle}>{t('settings.currency')}</Text>
</TouchableOpacity>
)}
<TouchableOpacity
style={[
styles.changeTypeText,
changeType === "language" && styles.changeTypeTextActive,
!user?.user_id ? styles.changeTypeTextFullWidth : {},
]}
onPress={() => setChangeType("language")}
>
<Text style={styles.changeTypeTextTitle}>{t('setting.language')}</Text>
<Text style={styles.changeTypeTextTitle}>{t('settings.language')}</Text>
</TouchableOpacity>
</View>
{changeType === "country" && (
@ -225,7 +255,7 @@ export const CountrySetting = () => {
</View>
)}
<TouchableOpacity style={styles.bottomButton} onPress={putSettinghandel}>
<Text style={styles.buttonText}>{t('setting.confirm')}</Text>
<Text style={styles.buttonText}>{t('settings.confirm')}</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
@ -296,6 +326,12 @@ const styles = StyleSheet.create({
borderBottomColor: "#ff611a",
color: "#ff611a",
},
changeTypeTextLoggedIn: {
width: "33%",
},
changeTypeTextFullWidth: {
width: "100%",
},
countryList: {
backgroundColor: "white",
borderRadius: 10,

6
app/services/api/productApi.ts

@ -209,8 +209,10 @@ export interface HotTerms {
return apiService.get<Similars>(url);
},
// 图片搜索
searchByImage: (data:{formData: FormData,user_id?:number}) => {
return apiService.upload<Products>('/api/search/image_search/?user_id='+data.user_id,data.formData);
searchByImage: (data:{image_base64: string,user_id?:number | null}) => {
return apiService.post<Products>('/api/search/image_search/?user_id='+data.user_id,{
image_base64: data.image_base64
});
}
}

50
app/utils/languageUtils.ts

@ -3,6 +3,8 @@ import { getCurrentLanguage } from '../i18n';
export const getSubjectTransLanguage = <T extends Record<string, any>>(data: T): string => {
// 获取当前i18n语言
const currentLang = getCurrentLanguage();
console.log('currentLang', currentLang);
// 特殊处理中文
if (currentLang === 'zh' && 'subject' in data) {
@ -28,28 +30,28 @@ export const getSubjectTransLanguage = <T extends Record<string, any>>(data: T):
export const getSkuTransLanguage = <T extends Record<string, any>>(data: T): string => {
// 获取当前i18n语言
const currentLang = getCurrentLanguage();
// export const getSkuTransLanguage = <T extends Record<string, any>>(data: T): string => {
// // 获取当前i18n语言
// const currentLang = getCurrentLanguage();
// 特殊处理中文
if (currentLang === 'zh' && 'value' in data) {
return data.value as string;
}
// 获取所有subject_trans开头的字段
const translationFields = Object.keys(data).filter(key =>
key.startsWith('value_trans')
);
// 查找匹配的字段
const matchedField = translationFields.find(field => {
// 从字段名中提取语言代码
const langCode = field.replace('value_trans_', '');
// 如果没有后缀,则为法语
return langCode === '' ? currentLang === 'fr' : langCode === currentLang;
});
// 返回匹配的翻译值,如果没有匹配则返回法语
return (data[matchedField || 'value_trans'] as string) || '';
};
// // 特殊处理中文
// if (currentLang === 'zh' && 'value' in data) {
// return data.value as string;
// }
// // 获取所有subject_trans开头的字段
// const translationFields = Object.keys(data).filter(key =>
// key.startsWith('value_trans')
// );
// // 查找匹配的字段
// const matchedField = translationFields.find(field => {
// // 从字段名中提取语言代码
// const langCode = field.replace('value_trans_', '');
// // 如果没有后缀,则为法语
// return langCode === '' ? currentLang === 'fr' : langCode === currentLang;
// });
// // 返回匹配的翻译值,如果没有匹配则返回法语
// return (data[matchedField || 'value_trans'] as string) || '';
// };

42
app/utils/storage.ts

@ -0,0 +1,42 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import { STORAGE_KEYS } from '../constants/config';
// 保存货币设置到本地存储
export const saveCurrency = async (currency: string) => {
try {
await AsyncStorage.setItem(STORAGE_KEYS.CURRENCY, currency);
} catch (error) {
console.error('保存货币设置失败:', error);
}
};
// 从本地存储加载货币设置
export const loadCurrency = async (): Promise<string | null> => {
try {
const savedCurrency = await AsyncStorage.getItem(STORAGE_KEYS.CURRENCY);
return savedCurrency;
} catch (error) {
console.error('加载货币设置失败:', error);
return null;
}
};
// 保存语言设置到本地存储
export const saveLanguage = async (language: string) => {
try {
await AsyncStorage.setItem(STORAGE_KEYS.LANGUAGE, language);
} catch (error) {
console.error('保存语言设置失败:', error);
}
};
// 从本地存储加载语言设置
export const loadLanguage = async (): Promise<string | null> => {
try {
const savedLanguage = await AsyncStorage.getItem(STORAGE_KEYS.LANGUAGE);
return savedLanguage;
} catch (error) {
console.error('加载语言设置失败:', error);
return null;
}
};
Loading…
Cancel
Save