feat: 国际化

This commit is contained in:
jinyaqiu 2025-07-31 14:26:54 +08:00
parent 438876d7a6
commit 26e3b4d0c9
7 changed files with 44 additions and 29 deletions

View File

@ -78,7 +78,7 @@ const MessageItem = ({ setCancel, cancel = true, t, insets, item, sessionId, set
items={[ items={[
{ {
svg: <DownloadSvg width={20} height={20} />, svg: <DownloadSvg width={20} height={20} />,
label: "保存", label: t("ask:ask.save"),
onPress: () => { onPress: () => {
const imageUrl = image?.preview_file_info?.url || image.video?.preview_file_info?.url; const imageUrl = image?.preview_file_info?.url || image.video?.preview_file_info?.url;
if (imageUrl) { if (imageUrl) {
@ -89,11 +89,14 @@ const MessageItem = ({ setCancel, cancel = true, t, insets, item, sessionId, set
}, },
{ {
svg: <CancelSvg width={20} height={20} color='red' />, svg: <CancelSvg width={20} height={20} color='red' />,
label: "取消", label: t("ask:ask.cancel"),
onPress: () => console.log('取消'), onPress: () => {
setCancel(true);
},
textStyle: { color: 'red' } textStyle: { color: 'red' }
} }
]} ]}
cancel={cancel}
menuStyle={{ menuStyle={{
backgroundColor: 'white', backgroundColor: 'white',
borderRadius: 8, borderRadius: 8,
@ -117,7 +120,6 @@ const MessageItem = ({ setCancel, cancel = true, t, insets, item, sessionId, set
loadingIndicatorSource={require('@/assets/images/png/placeholder.png')} loadingIndicatorSource={require('@/assets/images/png/placeholder.png')}
/> />
</ContextMenu> </ContextMenu>
</Pressable> </Pressable>
))} ))}
</View> </View>
@ -198,7 +200,7 @@ const styles = StyleSheet.create({
}, },
background: { background: {
...StyleSheet.absoluteFillObject, ...StyleSheet.absoluteFillObject,
backgroundColor: 'rgba(0, 0, 0, 0.5)', // 添加半透明黑色背景 backgroundColor: 'rgba(0, 0, 0, 0.5)',
}, },
centeredView: { centeredView: {
flex: 1, flex: 1,

View File

@ -77,7 +77,7 @@ const SelectModel = ({ modalDetailsVisible, setModalDetailsVisible, insets, setS
items={[ items={[
{ {
svg: <DownloadSvg width={20} height={20} />, svg: <DownloadSvg width={20} height={20} />,
label: "保存", label: t("ask:ask.save"),
onPress: () => { onPress: () => {
const imageUrl = item?.file_info?.url || item.video?.file_info?.url; const imageUrl = item?.file_info?.url || item.video?.file_info?.url;
if (imageUrl) { if (imageUrl) {
@ -88,11 +88,12 @@ const SelectModel = ({ modalDetailsVisible, setModalDetailsVisible, insets, setS
}, },
{ {
svg: <CancelSvg width={20} height={20} color='red' />, svg: <CancelSvg width={20} height={20} color='red' />,
label: "取消", label: t("ask:ask.cancel"),
onPress: () => console.log('取消'), onPress: () => setCancel(true),
textStyle: { color: 'red' } textStyle: { color: 'red' }
} }
]} ]}
cancel={cancel}
menuStyle={{ menuStyle={{
backgroundColor: 'white', backgroundColor: 'white',
borderRadius: 8, borderRadius: 8,

View File

@ -4,7 +4,6 @@ import SunSvg from '@/assets/icons/svg/sun.svg';
import VideoSvg from '@/assets/icons/svg/video.svg'; import VideoSvg from '@/assets/icons/svg/video.svg';
import React, { Dispatch, SetStateAction, useCallback, useEffect, useRef, useState } from 'react'; import React, { Dispatch, SetStateAction, useCallback, useEffect, useRef, useState } from 'react';
import { import {
EventSubscription,
Keyboard, Keyboard,
ScrollView, ScrollView,
StyleSheet, StyleSheet,
@ -14,6 +13,7 @@ import {
} from 'react-native'; } from 'react-native';
import { Message } from '@/types/ask'; import { Message } from '@/types/ask';
import { useTranslation } from 'react-i18next';
import { ThemedText } from '../ThemedText'; import { ThemedText } from '../ThemedText';
import { createNewConversation, getConversation } from './utils'; import { createNewConversation, getConversation } from './utils';
@ -28,14 +28,12 @@ interface Props {
export default function SendMessage(props: Props) { export default function SendMessage(props: Props) {
const { setIsHello, conversationId, setUserMessages, setConversationId, selectedImages, setSelectedImages } = props; const { setIsHello, conversationId, setUserMessages, setConversationId, selectedImages, setSelectedImages } = props;
const { t } = useTranslation()
// 用户询问 // 用户询问
const [inputValue, setInputValue] = useState(''); const [inputValue, setInputValue] = useState('');
// 添加一个ref来跟踪键盘状态 // 添加一个ref来跟踪键盘状态
const keyboardDidShowListener = useRef<EventSubscription | null>(null);
const keyboardDidHideListener = useRef<EventSubscription | null>(null);
const isKeyboardVisible = useRef(false); const isKeyboardVisible = useRef(false);
useEffect(() => { useEffect(() => {
@ -49,8 +47,7 @@ export default function SendMessage(props: Props) {
setUserMessages([ setUserMessages([
{ {
content: { content: {
text: '想打开记忆盲盒吗?描述你的回忆,我来帮你找回照片、生成影片或解锁隐藏彩蛋✨' text: t("ask:ask.introduction1")
}, },
role: 'Assistant', role: 'Assistant',
timestamp: new Date().toISOString() timestamp: new Date().toISOString()
@ -139,8 +136,8 @@ export default function SendMessage(props: Props) {
{ {
content: { content: {
text: type === "search" text: type === "search"
? '想找合适的图片?试试这样搜更精准:\n\n• 明确主题:比如"秋日森林""极简风书桌""复古海报设计"\n\n• 加上细节:想找特定风格?试试"水彩风猫咪""赛博朋克城市夜景";需要特定用途?比如"无版权风景图""可商用图标"\n\n• 描述场景:比如"阳光透过树叶的光斑""雨天咖啡馆窗外"\n\n输入这些关键词说不定就能找到你想要的画面啦' ? t("ask:ask.introduction2")
: '想让你的视频内容更吸睛、更有故事感吗?不妨试试从搜索图片入手吧!\n\n你可以先确定视频的主题——是治愈系的自然风景还是复古风的城市街景或是充满活力的生活瞬间然后根据主题去搜索相关的图片比如想做"春日限定"主题,就搜"樱花飘落""草地野餐""嫩芽初绽"之类的画面。\n\n这些图片能帮你快速理清视频的画面脉络甚至能激发新的创意——比如一张老照片里的复古物件或许能延伸出一段关于时光的故事一组星空图片说不定能串联成关于梦想与远方的叙事。把这些图片按你的想法串联起来配上合适的音乐和文案一段有温度的视频就诞生啦试试看吧' : t("ask:ask.introduction3")
}, },
role: 'Assistant', role: 'Assistant',
timestamp: new Date().toISOString() timestamp: new Date().toISOString()
@ -154,10 +151,10 @@ export default function SendMessage(props: Props) {
<ScrollView horizontal={true}> <ScrollView horizontal={true}>
<TouchableOpacity style={[styles.button, { borderColor: '#FFB645' }]} onPress={() => handleQuitly('search')}> <TouchableOpacity style={[styles.button, { borderColor: '#FFB645' }]} onPress={() => handleQuitly('search')}>
<SunSvg width={18} height={18} /> <SunSvg width={18} height={18} />
<ThemedText></ThemedText> <ThemedText>{t("ask:ask.search")}</ThemedText>
</TouchableOpacity><TouchableOpacity style={[styles.button, { borderColor: '#E2793F' }]} onPress={() => handleQuitly('video')}> </TouchableOpacity><TouchableOpacity style={[styles.button, { borderColor: '#E2793F' }]} onPress={() => handleQuitly('video')}>
<VideoSvg width={18} height={18} /> <VideoSvg width={18} height={18} />
<ThemedText></ThemedText> <ThemedText>{t("ask:ask.video")}</ThemedText>
</TouchableOpacity> </TouchableOpacity>
</ScrollView> </ScrollView>
<TextInput <TextInput

View File

@ -10,7 +10,7 @@ interface TypewriterTextProps {
const TypewriterText: React.FC<TypewriterTextProps> = ({ const TypewriterText: React.FC<TypewriterTextProps> = ({
text, text,
speed = 150, speed = 100,
loop = false, loop = false,
delay = 2000, delay = 2000,
}) => { }) => {

View File

@ -65,7 +65,7 @@ export const saveMediaToGallery = async (mediaUrl: string, t: TFunction) => {
const { status } = await MediaLibrary.requestPermissionsAsync(); const { status } = await MediaLibrary.requestPermissionsAsync();
if (status !== 'granted') { if (status !== 'granted') {
Alert.alert('需要相册权限', '请允许应用访问相册以保存媒体文件'); Alert.alert(t("ask:ask.mediaAuth"), t("ask:ask.mediaAuthDesc"));
return false; return false;
} }
@ -105,15 +105,15 @@ export const saveMediaToGallery = async (mediaUrl: string, t: TFunction) => {
); );
Alert.alert( Alert.alert(
'保存成功', t("ask:ask.saveSuccess"),
isVideo ? '视频已保存到相册' : '图片已保存到相册' isVideo ? t("ask:ask.videoSave") : t("ask:ask.imgSave")
); );
return true; return true;
} catch (error) { } catch (error) {
console.error('保存失败:', error); console.log('保存失败:', error);
Alert.alert( Alert.alert(
'保存失败', t("ask:ask.saveError"),
error instanceof Error ? error.message : '保存媒体文件时出错,请重试' error instanceof Error ? error.message : t("ask:ask.saveError")
); );
return false; return false;
} finally { } finally {
@ -123,7 +123,7 @@ export const saveMediaToGallery = async (mediaUrl: string, t: TFunction) => {
await FileSystem.deleteAsync(fileUri, { idempotent: true }).catch(console.warn); await FileSystem.deleteAsync(fileUri, { idempotent: true }).catch(console.warn);
} }
} catch (cleanupError) { } catch (cleanupError) {
console.warn('清理临时文件时出错:', cleanupError); console.log('清理临时文件时出错:', cleanupError);
} }
} }
}; };

View File

@ -1,4 +1,4 @@
import React, { useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { import {
Dimensions, Dimensions,
Modal, Modal,
@ -32,6 +32,7 @@ interface ContextMenuProps {
onClose?: () => void; onClose?: () => void;
longPressDuration?: number; longPressDuration?: number;
activeOpacity?: number; activeOpacity?: number;
cancel?: boolean;
} }
const ContextMenu: React.FC<ContextMenuProps> = ({ const ContextMenu: React.FC<ContextMenuProps> = ({

View File

@ -16,6 +16,20 @@
"issue": "发生了一些问题", "issue": "发生了一些问题",
"case1": "找去年我家宝宝/宠物的素材片段", "case1": "找去年我家宝宝/宠物的素材片段",
"case2": "找去年吃过的美食", "case2": "找去年吃过的美食",
"case3": "找近期旅游的素材" "case3": "找近期旅游的素材",
"mediaAuth": "需要相册权限",
"mediaAuthDesc": "请允许应用访问相册以保存媒体文件",
"saveSuccess": "保存成功",
"imgSave": "图片已保存到相册",
"videoSave": "视频已保存到相册",
"saveError": "保存失败",
"saveErrorDesc": "保存媒体文件时出错,请重试",
"save": "保存",
"cancel": "取消",
"introduction1": "想打开记忆盲盒吗?描述你的回忆,我来帮你找回照片、生成影片或解锁隐藏彩蛋✨",
"introduction2": "想找合适的图片?试试这样搜更精准:\n\n• 明确主题:比如'秋日森林'、'极简风书桌'、'复古海报设计'\n\n• 加上细节:想找特定风格?试试'水彩风猫咪'、'赛博朋克城市夜景';需要特定用途?比如'无版权风景图'、'可商用图标'\n\n• 描述场景:比如'阳光透过树叶的光斑'、'雨天咖啡馆窗外'\n\n输入这些关键词说不定就能找到你想要的画面啦",
"introduction3": "想让你的视频内容更吸睛、更有故事感吗?不妨试试从搜索图片入手吧!\n\n你可以先确定视频的主题——是治愈系的自然风景还是复古风的城市街景或是充满活力的生活瞬间然后根据主题去搜索相关的图片比如想做'春日限定'主题,就搜'樱花飘落''草地野餐''嫩芽初绽'之类的画面。\n\n这些图片能帮你快速理清视频的画面脉络甚至能激发新的创意——比如一张老照片里的复古物件或许能延伸出一段关于时光的故事一组星空图片说不定能串联成关于梦想与远方的叙事。把这些图片按你的想法串联起来配上合适的音乐和文案一段有温度的视频就诞生啦试试看吧",
"search": "检索素材",
"video": "创作视频"
} }
} }