feat: 国际化
This commit is contained in:
parent
a250529de3
commit
b349e6d901
@ -5,7 +5,7 @@ import MoreSvg from "@/assets/icons/svg/more.svg";
|
||||
import { Message, Video } from "@/types/ask";
|
||||
import { MaterialItem } from "@/types/personal-info";
|
||||
import { TFunction } from "i18next";
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Image,
|
||||
Pressable,
|
||||
@ -36,6 +36,7 @@ interface RenderMessageProps {
|
||||
|
||||
const MessageItem = ({ t, insets, item, sessionId, setModalVisible, modalVisible, setModalDetailsVisible, modalDetailsVisible, setSelectedImages, selectedImages }: RenderMessageProps) => {
|
||||
const isUser = item.role === 'User';
|
||||
const [cancel, setCancel] = useState(false);
|
||||
|
||||
return (
|
||||
<View className={`flex-row items-start gap-2 w-full ${isUser ? 'justify-end' : 'justify-start'}`}>
|
||||
@ -76,7 +77,7 @@ const MessageItem = ({ t, insets, item, sessionId, setModalVisible, modalVisible
|
||||
items={[
|
||||
{
|
||||
svg: <DownloadSvg width={20} height={20} />,
|
||||
label: "保存",
|
||||
label: t("ask:ask.save"),
|
||||
onPress: () => {
|
||||
const imageUrl = image?.preview_file_info?.url || image.video?.preview_file_info?.url;
|
||||
if (imageUrl) {
|
||||
@ -87,11 +88,14 @@ const MessageItem = ({ t, insets, item, sessionId, setModalVisible, modalVisible
|
||||
},
|
||||
{
|
||||
svg: <CancelSvg width={20} height={20} color='red' />,
|
||||
label: "取消",
|
||||
onPress: () => console.log('取消'),
|
||||
label: t("ask:ask.cancel"),
|
||||
onPress: () => {
|
||||
setCancel(true);
|
||||
},
|
||||
textStyle: { color: 'red' }
|
||||
}
|
||||
]}
|
||||
cancel={cancel}
|
||||
menuStyle={{
|
||||
backgroundColor: 'white',
|
||||
borderRadius: 8,
|
||||
@ -115,7 +119,6 @@ const MessageItem = ({ t, insets, item, sessionId, setModalVisible, modalVisible
|
||||
loadingIndicatorSource={require('@/assets/images/png/placeholder.png')}
|
||||
/>
|
||||
</ContextMenu>
|
||||
|
||||
</Pressable>
|
||||
))}
|
||||
</View>
|
||||
@ -194,7 +197,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
background: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)', // 添加半透明黑色背景
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
centeredView: {
|
||||
flex: 1,
|
||||
|
||||
@ -4,7 +4,7 @@ import FolderSvg from "@/assets/icons/svg/folder.svg";
|
||||
import ReturnArrow from "@/assets/icons/svg/returnArrow.svg";
|
||||
import YesSvg from "@/assets/icons/svg/yes.svg";
|
||||
import { TFunction } from "i18next";
|
||||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import { FlatList, Image, Modal, StyleSheet, TouchableOpacity, View } from "react-native";
|
||||
import ContextMenu from "../gusture/contextMenu";
|
||||
import { ThemedText } from "../ThemedText";
|
||||
@ -19,6 +19,7 @@ interface SelectModelProps {
|
||||
t: TFunction;
|
||||
}
|
||||
const SelectModel = ({ modalDetailsVisible, setModalDetailsVisible, insets, setSelectedImages, selectedImages, t }: SelectModelProps) => {
|
||||
const [cancel, setCancel] = useState(false)
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@ -75,7 +76,7 @@ const SelectModel = ({ modalDetailsVisible, setModalDetailsVisible, insets, setS
|
||||
items={[
|
||||
{
|
||||
svg: <DownloadSvg width={20} height={20} />,
|
||||
label: "保存",
|
||||
label: t("ask:ask.save"),
|
||||
onPress: () => {
|
||||
const imageUrl = item?.file_info?.url || item.video?.file_info?.url;
|
||||
if (imageUrl) {
|
||||
@ -86,11 +87,12 @@ const SelectModel = ({ modalDetailsVisible, setModalDetailsVisible, insets, setS
|
||||
},
|
||||
{
|
||||
svg: <CancelSvg width={20} height={20} color='red' />,
|
||||
label: "取消",
|
||||
onPress: () => console.log('取消'),
|
||||
label: t("ask:ask.cancel"),
|
||||
onPress: () => setCancel(true),
|
||||
textStyle: { color: 'red' }
|
||||
}
|
||||
]}
|
||||
cancel={cancel}
|
||||
menuStyle={{
|
||||
backgroundColor: 'white',
|
||||
borderRadius: 8,
|
||||
|
||||
@ -4,7 +4,6 @@ import SunSvg from '@/assets/icons/svg/sun.svg';
|
||||
import VideoSvg from '@/assets/icons/svg/video.svg';
|
||||
import React, { Dispatch, SetStateAction, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
EventSubscription,
|
||||
Keyboard,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
@ -14,6 +13,7 @@ import {
|
||||
} from 'react-native';
|
||||
|
||||
import { Message } from '@/types/ask';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ThemedText } from '../ThemedText';
|
||||
import { createNewConversation, getConversation } from './utils';
|
||||
|
||||
@ -28,14 +28,12 @@ interface Props {
|
||||
export default function SendMessage(props: Props) {
|
||||
const { setIsHello, conversationId, setUserMessages, setConversationId, selectedImages, setSelectedImages } = props;
|
||||
|
||||
const { t } = useTranslation()
|
||||
|
||||
// 用户询问
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
|
||||
|
||||
|
||||
// 添加一个ref来跟踪键盘状态
|
||||
const keyboardDidShowListener = useRef<EventSubscription | null>(null);
|
||||
const keyboardDidHideListener = useRef<EventSubscription | null>(null);
|
||||
const isKeyboardVisible = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
@ -49,8 +47,7 @@ export default function SendMessage(props: Props) {
|
||||
setUserMessages([
|
||||
{
|
||||
content: {
|
||||
text: '想打开记忆盲盒吗?描述你的回忆,我来帮你找回照片、生成影片或解锁隐藏彩蛋✨'
|
||||
|
||||
text: t("ask:ask.introduction1")
|
||||
},
|
||||
role: 'Assistant',
|
||||
timestamp: new Date().toISOString()
|
||||
@ -139,8 +136,8 @@ export default function SendMessage(props: Props) {
|
||||
{
|
||||
content: {
|
||||
text: type === "search"
|
||||
? '想找合适的图片?试试这样搜更精准:\n\n• 明确主题:比如"秋日森林""极简风书桌""复古海报设计"\n\n• 加上细节:想找特定风格?试试"水彩风猫咪""赛博朋克城市夜景";需要特定用途?比如"无版权风景图""可商用图标"\n\n• 描述场景:比如"阳光透过树叶的光斑""雨天咖啡馆窗外"\n\n输入这些关键词,说不定就能找到你想要的画面啦~'
|
||||
: '想让你的视频内容更吸睛、更有故事感吗?不妨试试从搜索图片入手吧!\n\n你可以先确定视频的主题——是治愈系的自然风景,还是复古风的城市街景,或是充满活力的生活瞬间?然后根据主题去搜索相关的图片,比如想做"春日限定"主题,就搜"樱花飘落""草地野餐""嫩芽初绽"之类的画面。\n\n这些图片能帮你快速理清视频的画面脉络,甚至能激发新的创意——比如一张老照片里的复古物件,或许能延伸出一段关于时光的故事;一组星空图片,说不定能串联成关于梦想与远方的叙事。把这些图片按你的想法串联起来,配上合适的音乐和文案,一段有温度的视频就诞生啦,试试看吧~'
|
||||
? t("ask:ask.introduction2")
|
||||
: t("ask:ask.introduction3")
|
||||
},
|
||||
role: 'Assistant',
|
||||
timestamp: new Date().toISOString()
|
||||
@ -154,10 +151,10 @@ export default function SendMessage(props: Props) {
|
||||
<ScrollView horizontal={true}>
|
||||
<TouchableOpacity style={[styles.button, { borderColor: '#FFB645' }]} onPress={() => handleQuitly('search')}>
|
||||
<SunSvg width={18} height={18} />
|
||||
<ThemedText>检索素材</ThemedText>
|
||||
<ThemedText>{t("ask:ask.search")}</ThemedText>
|
||||
</TouchableOpacity><TouchableOpacity style={[styles.button, { borderColor: '#E2793F' }]} onPress={() => handleQuitly('video')}>
|
||||
<VideoSvg width={18} height={18} />
|
||||
<ThemedText>创作视频</ThemedText>
|
||||
<ThemedText>{t("ask:ask.video")}</ThemedText>
|
||||
</TouchableOpacity>
|
||||
</ScrollView>
|
||||
<TextInput
|
||||
|
||||
@ -10,7 +10,7 @@ interface TypewriterTextProps {
|
||||
|
||||
const TypewriterText: React.FC<TypewriterTextProps> = ({
|
||||
text,
|
||||
speed = 150,
|
||||
speed = 100,
|
||||
loop = false,
|
||||
delay = 2000,
|
||||
}) => {
|
||||
|
||||
@ -3,7 +3,9 @@ import { Message } from "@/types/ask";
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import * as MediaLibrary from 'expo-media-library';
|
||||
import { useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Alert } from 'react-native';
|
||||
const { t } = useTranslation()
|
||||
|
||||
// 实现一个函数,从两个数组中轮流插入新数组
|
||||
export const mergeArrays = (arr1: any[], arr2: any[]) => {
|
||||
@ -64,7 +66,7 @@ export const saveMediaToGallery = async (mediaUrl: string) => {
|
||||
const { status } = await MediaLibrary.requestPermissionsAsync();
|
||||
|
||||
if (status !== 'granted') {
|
||||
Alert.alert('需要相册权限', '请允许应用访问相册以保存媒体文件');
|
||||
Alert.alert(t("ask:ask.mediaAuth"), t("ask:ask.mediaAuthDesc"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -104,15 +106,15 @@ export const saveMediaToGallery = async (mediaUrl: string) => {
|
||||
);
|
||||
|
||||
Alert.alert(
|
||||
'保存成功',
|
||||
isVideo ? '视频已保存到相册' : '图片已保存到相册'
|
||||
t("ask:ask.saveSuccess"),
|
||||
isVideo ? t("ask:ask.videoSave") : t("ask:ask.imgSave")
|
||||
);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('保存失败:', error);
|
||||
console.log('保存失败:', error);
|
||||
Alert.alert(
|
||||
'保存失败',
|
||||
error instanceof Error ? error.message : '保存媒体文件时出错,请重试'
|
||||
t("ask:ask.saveError"),
|
||||
error instanceof Error ? error.message : t("ask:ask.saveError")
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
@ -122,7 +124,7 @@ export const saveMediaToGallery = async (mediaUrl: string) => {
|
||||
await FileSystem.deleteAsync(fileUri, { idempotent: true }).catch(console.warn);
|
||||
}
|
||||
} catch (cleanupError) {
|
||||
console.warn('清理临时文件时出错:', cleanupError);
|
||||
console.log('清理临时文件时出错:', cleanupError);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Dimensions,
|
||||
Modal,
|
||||
@ -32,6 +32,7 @@ interface ContextMenuProps {
|
||||
onClose?: () => void;
|
||||
longPressDuration?: number;
|
||||
activeOpacity?: number;
|
||||
cancel?: boolean;
|
||||
}
|
||||
|
||||
const ContextMenu: React.FC<ContextMenuProps> = ({
|
||||
@ -41,6 +42,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
|
||||
menuItemStyle,
|
||||
menuTextStyle,
|
||||
dividerStyle,
|
||||
calcel,
|
||||
onOpen,
|
||||
onClose,
|
||||
longPressDuration = 500,
|
||||
@ -75,6 +77,10 @@ const ContextMenu: React.FC<ContextMenuProps> = ({
|
||||
runOnJS(showMenu)(absoluteX, absoluteY);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setMenuVisible(!calcel);
|
||||
}, [calcel])
|
||||
|
||||
return (
|
||||
<>
|
||||
<View ref={containerRef} collapsable={false} style={{ flex: 1 }}>
|
||||
|
||||
@ -16,6 +16,13 @@
|
||||
"issue": "have some issue",
|
||||
"case1": "Find last year's baby/pet material",
|
||||
"case2": "Find last year's food",
|
||||
"case3": "Find recent travel material"
|
||||
"case3": "Find recent travel material",
|
||||
"mediaAuth": "need album permission",
|
||||
"mediaAuthDesc": "allow app to access album to save media files",
|
||||
"saveSuccess": "save success",
|
||||
"imgSave": "image saved to album",
|
||||
"videoSave": "video saved to album",
|
||||
"saveError": "save failed",
|
||||
"saveErrorDesc": "save media files error, please try again"
|
||||
}
|
||||
}
|
||||
@ -16,6 +16,20 @@
|
||||
"issue": "发生了一些问题",
|
||||
"case1": "找去年我家宝宝/宠物的素材片段",
|
||||
"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": "创作视频"
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user