Commit 30bab8d2 authored by fisherdaddy's avatar fisherdaddy

feat: add Document Translator

parent 44f4813f
......@@ -8,6 +8,8 @@
<title>AI工具箱 | 智能助手集合</title>
<link rel="stylesheet" href="/src/styles/main.css" />
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
<!-- PDF.js library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.9.359/pdf.min.js"></script>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-3PSXKB099C"></script>
<script>
......
......@@ -11,6 +11,7 @@ const ImageTools = lazy(() => import('./pages/ImageTools'));
const Blog = lazy(() => import('./pages/Blog'));
const AIProduct = lazy(() => import('./pages/AIProduct'));
const Translator = lazy(() => import('./pages/Translator'));
const DocumentTranslator = lazy(() => import('./pages/DocumentTranslator'));
const JsonFormatter = lazy(() => import('./components/JsonFormatter'));
const MarkdownToImage = lazy(() => import('./components/MarkdownToImage'));
......@@ -54,6 +55,7 @@ function App() {
<Route path="/ai-products" element={<AIProduct />} />
<Route path="/blog" element={<Blog />} />
<Route path="/translator" element={<Translator />} />
<Route path="/document-translator" element={<DocumentTranslator />} />
<Route path="/markdown-to-image" element={<MarkdownToImage />} />
<Route path="/json-formatter" element={<JsonFormatter />} />
......
This diff is collapsed.
......@@ -8,11 +8,34 @@ import logo from '/assets/logo.png';
function Header() {
const { t } = useTranslation();
const navigate = useNavigate();
const user = JSON.parse(localStorage.getItem('user'));
const [user, setUser] = useState(null);
const [menuOpen, setMenuOpen] = useState(false);
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const menuRef = useRef(null);
// Check for user on component mount and localStorage changes
useEffect(() => {
const checkUser = () => {
try {
const userData = localStorage.getItem('user');
if (userData) {
setUser(JSON.parse(userData));
} else {
setUser(null);
}
} catch (error) {
console.error('Failed to parse user data');
setUser(null);
}
};
checkUser();
// Listen for storage events (in case user logs in/out in another tab)
window.addEventListener('storage', checkUser);
return () => window.removeEventListener('storage', checkUser);
}, []);
const toggleMenu = () => {
setMenuOpen(!menuOpen);
};
......@@ -32,6 +55,7 @@ function Header() {
const handleLogout = () => {
localStorage.removeItem('user');
setUser(null);
navigate('/login');
setMobileMenuOpen(false);
};
......@@ -330,6 +354,19 @@ function Header() {
>
翻译工具
</NavLink>
<NavLink
to="/document-translator"
className={({isActive}) =>
`block px-4 py-3 rounded-lg text-base font-medium transition-colors duration-200 ${
isActive
? 'bg-indigo-50 text-indigo-600'
: 'text-gray-700 hover:bg-gray-50 hover:text-indigo-600'
}`
}
onClick={() => setMobileMenuOpen(false)}
>
{t('documentTranslator.title', 'PDF Translator')}
</NavLink>
<NavLink
to="/ai-products"
className={({isActive}) =>
......
......@@ -19,8 +19,30 @@
"description": "A suite of practical image processing tools, including a handwriting font generator, Markdown to image converter, and quote to image creator, making image editing and creative design tasks easier."
},
"translator": {
"title": "Translator",
"description": "Supports text and image multi-language translation, making it easy to communicate across languages."
"title": "Translation Tool",
"description": "Supports multilingual translation for text and images, enabling easy cross-language communication.",
"tabs": {
"text": "Text Translation",
"image": "Image Translation",
"document": "Document Translation"
},
"targetLanguageLabel": "Target Language",
"textPlaceholder": "Please enter the text to be translated",
"translationPlaceholder": "Translation results will be displayed here",
"imagePlaceholder": "Please upload an image to be translated",
"translateButton": "Translate",
"uploadImageButton": "Upload Image",
"emptyImagePlaceholder": "Please upload an image first",
"displayModeLabel": "Display Mode",
"displayModes": {
"translation": "Translation",
"bilingual": "Bilingual",
"original": "Original"
},
"uploadFilePrompt": "Upload File",
"selectDocument": "Select File",
"loginRequired": "Please log in first",
"supportsPdf": "PDF files are supported"
},
"blog": {
"title": "AI News",
......
......@@ -20,7 +20,29 @@
},
"translator": {
"title": "翻訳ツール",
"description": "テキストと画像の多言語翻訳をサポートし、簡単に言語を越えて交流できます。"
"description": "テキストや画像の多言語翻訳に対応し、簡単に言語の壁を越えたコミュニケーションを実現します。",
"tabs": {
"text": "テキスト翻訳",
"image": "画像翻訳",
"document": "ドキュメント翻訳"
},
"targetLanguageLabel": "翻訳先の言語",
"textPlaceholder": "翻訳したいテキストを入力してください",
"translationPlaceholder": "翻訳結果はここに表示されます",
"imagePlaceholder": "翻訳する画像をアップロードしてください",
"translateButton": "翻訳",
"uploadImageButton": "画像をアップロード",
"emptyImagePlaceholder": "まず画像をアップロードしてください",
"displayModeLabel": "表示モード",
"displayModes": {
"translation": "翻訳",
"bilingual": "バイリンガル",
"original": "原文"
},
"uploadFilePrompt": "ファイルをアップロード",
"selectDocument": "ファイルを選択",
"loginRequired": "ログインしてください",
"supportsPdf": "PDFファイルに対応"
},
"blog": {
"title": "AIニュース",
......
......@@ -20,7 +20,29 @@
},
"translator": {
"title": "번역 도구",
"description": "텍스트 및 이미지 다국어 번역을 지원하여 교류를 쉽게 할 수 있습니다."
"description": "텍스트와 이미지의 다국어 번역을 지원하여 언어 장벽 없이 쉽게 소통할 수 있습니다.",
"tabs": {
"text": "텍스트 번역",
"image": "이미지 번역",
"document": "문서 번역"
},
"targetLanguageLabel": "대상 언어",
"textPlaceholder": "번역할 텍스트를 입력하세요",
"translationPlaceholder": "번역 결과가 여기에 표시됩니다",
"imagePlaceholder": "번역할 이미지를 업로드하세요",
"translateButton": "번역",
"uploadImageButton": "이미지 업로드",
"emptyImagePlaceholder": "먼저 이미지를 업로드해주세요",
"displayModeLabel": "표시 모드",
"displayModes": {
"translation": "번역",
"bilingual": "이중 언어",
"original": "원문"
},
"uploadFilePrompt": "파일 업로드",
"selectDocument": "파일 선택",
"loginRequired": "먼저 로그인해주세요",
"supportsPdf": "PDF 파일 지원"
},
"blog": {
"title": "AI 뉴스",
......
......@@ -20,7 +20,29 @@
},
"translator": {
"title": "翻译工具",
"description": "支持文本和图片多语言翻译,轻松实现跨语言交流。"
"description": "支持文本和图片多语言翻译,轻松实现跨语言交流。",
"tabs": {
"text": "文本翻译",
"image": "图片翻译",
"document": "文档翻译"
},
"targetLanguageLabel": "目标语言",
"textPlaceholder": "请输入要翻译的文本",
"translationPlaceholder": "翻译结果将显示在这里",
"imagePlaceholder": "请上传要翻译的图片",
"translateButton": "翻译",
"uploadImageButton": "上传图片",
"emptyImagePlaceholder": "请先上传图片",
"displayModeLabel": "显示模式",
"displayModes": {
"translation": "翻译",
"bilingual": "双语",
"original": "原文"
},
"uploadFilePrompt": "上传文件",
"selectDocument": "选择文件",
"loginRequired": "请先登录",
"supportsPdf": "支持PDF文件"
},
"blog": {
"title": "AI 资讯",
......
This diff is collapsed.
......@@ -7,6 +7,8 @@ const tools = [
{ id: 'handwrite', icon: '/assets/icon/handwrite.png', path: '/handwriting' },
{ id: 'quoteCard', icon: '/assets/icon/quotecard.png', path: '/quote-card' },
{ id: 'markdown2image', icon: '/assets/icon/markdown2image.png', path: '/markdown-to-image' },
{ id: 'translator', icon: '/assets/icon/translator.png', path: '/translator' },
{ id: 'documentTranslator', icon: '/assets/icon/pdf-translator.png', path: '/document-translator' },
{ id: 'wechatFormatter', icon: '/assets/icon/editor.png', path: '/wechat-formatter' },
{ id: 'perpetualCalendar', icon: '/assets/icon/calendar.jpg', path: '/perpetual-calendar' },
{ id: 'imageAnnotator', icon: '/assets/icon/image-annotator.png', path: '/image-annotator' },
......
......@@ -14,8 +14,21 @@ const Login = () => {
const base64Url = credential.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const decodedPayload = JSON.parse(window.atob(base64));
// Save user data to localStorage
localStorage.setItem('user', JSON.stringify(decodedPayload));
navigate('/');
// Check if there's a redirect path saved
const redirectPath = sessionStorage.getItem('redirectAfterLogin');
// 使用直接的 window.location 跳转而不是 React Router 导航
// 这样可以确保页面完全重新加载,刷新登录状态
if (redirectPath) {
sessionStorage.removeItem('redirectAfterLogin');
window.location.href = redirectPath;
} else {
window.location.href = '/';
}
};
const handleLoginError = () => {
......@@ -23,9 +36,17 @@ const Login = () => {
};
useEffect(() => {
const user = localStorage.getItem('user');
if (user) {
navigate('/');
// Redirect if user is already logged in
const userData = localStorage.getItem('user');
if (userData) {
// Check if there's a redirect path saved
const redirectPath = sessionStorage.getItem('redirectAfterLogin');
if (redirectPath) {
sessionStorage.removeItem('redirectAfterLogin');
window.location.href = redirectPath;
} else {
window.location.href = '/';
}
}
}, [navigate]);
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment