"use client"; import { ChangeEvent, useEffect, useState } from "react"; import Button from "@/components/Button"; import IconClick from "@/components/IconClick"; import { useAudioPlayer } from "@/hooks/useAudioPlayer"; import IMAGES from "@/config/images"; import { getTTSAudioUrl } from "@/utils"; export default function Home() { const [ipaEnabled, setIPAEnabled] = useState(true); const [voicesData, setVoicesData] = useState<{ locale: string, short_name: string }[] | null>(null); const [loading, setLoading] = useState(true); const [targetLang, setTargetLang] = useState('Italian'); const [sourceText, setSourceText] = useState(''); const [targetText, setTargetText] = useState(''); const [sourceIPA, setSourceIPA] = useState(''); const [targetIPA, setTargetIPA] = useState(''); const [sourceLocale, setSourceLocale] = useState(null); const [targetLocale, setTargetLocale] = useState(null); const [translating, setTranslating] = useState(false); const { playAudio } = useAudioPlayer(); useEffect(() => { fetch('/list_of_voices.json') .then(res => res.json()) .then(setVoicesData) .catch(() => setVoicesData(null)) .finally(() => setLoading(false)); }, []); if (loading) return
加载中...
; if (!voicesData) return
加载失败
; const tl = ['English', 'Italian', 'Japanese']; const inputLanguage = () => { const lang = prompt('Input a language.')?.trim(); if (lang) { setTargetLang(lang); } } const translate = () => { if (translating) return; if (sourceText.length === 0) return; setTranslating(true); setTargetText(''); setSourceLocale(null); setTargetLocale(null); setSourceIPA(''); setTargetIPA(''); const params = new URLSearchParams({ text: sourceText, target: targetLang }) fetch(`/api/translate?${params}`) .then(res => res.json()) .then(obj => { setSourceLocale(obj.source_locale); setTargetLocale(obj.target_locale); setTargetText(obj.target_text); if (ipaEnabled) { const params = new URLSearchParams({ text: sourceText }); fetch(`/api/ipa?${params}`) .then(res => res.json()) .then(data => { setSourceIPA(data.ipa); }).catch(e => { console.error(e); setSourceIPA(''); }) const params2 = new URLSearchParams({ text: obj.target_text }); fetch(`/api/ipa?${params2}`) .then(res => res.json()) .then(data => { setTargetIPA(data.ipa); }).catch(e => { console.error(e); setTargetIPA(''); }) } }).catch(r => { console.error(r); setSourceLocale(''); setTargetLocale(''); setTargetText(''); }).finally(() => setTranslating(false)); } const handleInputChange = (e: ChangeEvent) => { setSourceText(e.target.value.trim()); setTargetText(''); setSourceLocale(null); setTargetLocale(null); setSourceIPA(''); setTargetIPA(''); } const readSource = async () => { if (sourceText.length === 0) return; if (sourceIPA.length === 0 && ipaEnabled) { const params = new URLSearchParams({ text: sourceText }); fetch(`/api/ipa?${params}`) .then(res => res.json()) .then(data => { setSourceIPA(data.ipa); }).catch(e => { console.error(e); setSourceIPA(''); }) } if (!sourceLocale) { try { const params = new URLSearchParams({ text: sourceText.slice(0, 30) }); const res = await fetch(`/api/locale?${params}`); const info = await res.json(); setSourceLocale(info.locale); const voice = voicesData.find(v => v.locale.startsWith(info.locale)); if (!voice) { return; } const url = await getTTSAudioUrl(sourceText, voice.short_name); await playAudio(url); URL.revokeObjectURL(url); } catch (e) { console.error(e); setSourceLocale(null); return; } } else { const voice = voicesData.find(v => v.locale.startsWith(sourceLocale!)); if (!voice) { return; } const url = await getTTSAudioUrl(sourceText, voice.short_name); await playAudio(url); URL.revokeObjectURL(url); } } const readTarget = async () => { if (targetText.length === 0) return; if (targetIPA.length === 0 && ipaEnabled) { const params = new URLSearchParams({ text: targetText }); fetch(`/api/ipa?${params}`) .then(res => res.json()) .then(data => { setTargetIPA(data.ipa); }).catch(e => { console.error(e); setTargetIPA(''); }) } const voice = voicesData.find(v => v.locale.startsWith(targetLocale!)); if (!voice) return; const url = await getTTSAudioUrl(targetText, voice.short_name); await playAudio(url); URL.revokeObjectURL(url); } return ( <>
{sourceIPA}
{ if (sourceText.length !== 0) await navigator.clipboard.writeText(sourceText); }} src={IMAGES.copy_all} alt="copy">
detect language
{ targetText }
{targetIPA}
{ if (targetText.length !== 0) await navigator.clipboard.writeText(targetText); }} src={IMAGES.copy_all} alt="copy">
translate into
); }