feat: add translation support for polls
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { POLL_TYPE } from '@/constants'
|
||||
import { useTranslatedEvent } from '@/hooks'
|
||||
import { useFetchPollResults } from '@/hooks/useFetchPollResults'
|
||||
import { createPollResponseDraftEvent } from '@/lib/draft-event'
|
||||
import { getPollMetadataFromEvent } from '@/lib/event-metadata'
|
||||
@@ -16,12 +17,16 @@ import { toast } from 'sonner'
|
||||
|
||||
export default function Poll({ event, className }: { event: Event; className?: string }) {
|
||||
const { t } = useTranslation()
|
||||
const translatedEvent = useTranslatedEvent(event.id)
|
||||
const { pubkey, publish, startLogin } = useNostr()
|
||||
const [isVoting, setIsVoting] = useState(false)
|
||||
const [selectedOptionIds, setSelectedOptionIds] = useState<string[]>([])
|
||||
const pollResults = useFetchPollResults(event.id)
|
||||
const [isLoadingResults, setIsLoadingResults] = useState(false)
|
||||
const poll = useMemo(() => getPollMetadataFromEvent(event), [event])
|
||||
const poll = useMemo(
|
||||
() => getPollMetadataFromEvent(translatedEvent ?? event),
|
||||
[event, translatedEvent]
|
||||
)
|
||||
const votedOptionIds = useMemo(() => {
|
||||
if (!pollResults || !pubkey) return []
|
||||
return Object.entries(pollResults.results)
|
||||
|
||||
@@ -24,9 +24,13 @@ export default function TranslateButton({
|
||||
const translatedEvent = useTranslatedEvent(event.id)
|
||||
const supported = useMemo(
|
||||
() =>
|
||||
[kinds.ShortTextNote, kinds.Highlights, ExtendedKind.COMMENT, ExtendedKind.PICTURE].includes(
|
||||
event.kind
|
||||
),
|
||||
[
|
||||
kinds.ShortTextNote,
|
||||
kinds.Highlights,
|
||||
ExtendedKind.COMMENT,
|
||||
ExtendedKind.PICTURE,
|
||||
ExtendedKind.POLL
|
||||
].includes(event.kind),
|
||||
[event]
|
||||
)
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { ExtendedKind } from '@/constants'
|
||||
import { getPollMetadataFromEvent } from '@/lib/event-metadata'
|
||||
import libreTranslate from '@/services/libre-translate.service'
|
||||
import storage from '@/services/local-storage.service'
|
||||
import translation from '@/services/translation.service'
|
||||
@@ -96,25 +98,51 @@ export function TranslationServiceProvider({ children }: { children: React.React
|
||||
const translateHighlightEvent = async (event: Event): Promise<Event> => {
|
||||
const target = i18n.language
|
||||
const comment = event.tags.find((tag) => tag[0] === 'comment')?.[1]
|
||||
if (!event.content && !comment) {
|
||||
return event
|
||||
}
|
||||
const [translatedContent, translatedComment] = await Promise.all([
|
||||
translate(event.content, target),
|
||||
!!comment && translate(comment, target)
|
||||
])
|
||||
|
||||
const translatedEvent: Event = {
|
||||
...event,
|
||||
content: translatedContent
|
||||
const texts = {
|
||||
content: event.content,
|
||||
comment
|
||||
}
|
||||
if (translatedComment) {
|
||||
translatedEvent.tags = event.tags.map((tag) =>
|
||||
tag[0] === 'comment' ? ['comment', translatedComment] : tag
|
||||
const joinedText = joinTexts(texts)
|
||||
if (!joinedText) return event
|
||||
|
||||
const translatedText = await translate(joinedText, target)
|
||||
const translatedTexts = splitTranslatedText(translatedText)
|
||||
return {
|
||||
...event,
|
||||
content: translatedTexts.content ?? event.content,
|
||||
tags: event.tags.map((tag) =>
|
||||
tag[0] === 'comment' ? ['comment', translatedTexts.comment ?? tag[1]] : tag
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const translatePollEvent = async (event: Event): Promise<Event> => {
|
||||
const target = i18n.language
|
||||
const pollMetadata = getPollMetadataFromEvent(event)
|
||||
|
||||
const texts: Record<string, string> = {
|
||||
question: event.content,
|
||||
...pollMetadata?.options.reduce(
|
||||
(acc, option) => {
|
||||
acc[option.id] = option.label
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, string>
|
||||
)
|
||||
}
|
||||
const joinedText = joinTexts(texts)
|
||||
if (!joinedText) return event
|
||||
|
||||
const translatedText = await translate(joinedText, target)
|
||||
const translatedTexts = splitTranslatedText(translatedText)
|
||||
return {
|
||||
...event,
|
||||
content: translatedTexts.question ?? '',
|
||||
tags: event.tags.map((tag) =>
|
||||
tag[0] === 'option' ? ['option', tag[1], translatedTexts[tag[1]] ?? tag[2]] : tag
|
||||
)
|
||||
}
|
||||
setTranslatedEventIdSet((prev) => new Set(prev.add(event.id)))
|
||||
return translatedEvent
|
||||
}
|
||||
|
||||
const translateEvent = async (event: Event): Promise<Event | void> => {
|
||||
@@ -134,6 +162,8 @@ export function TranslationServiceProvider({ children }: { children: React.React
|
||||
let translatedEvent: Event | undefined
|
||||
if (event.kind === kinds.Highlights) {
|
||||
translatedEvent = await translateHighlightEvent(event)
|
||||
} else if (event.kind === ExtendedKind.POLL) {
|
||||
translatedEvent = await translatePollEvent(event)
|
||||
} else {
|
||||
const translatedText = await translate(event.content, target)
|
||||
if (!translatedText) {
|
||||
@@ -178,3 +208,28 @@ export function TranslationServiceProvider({ children }: { children: React.React
|
||||
</TranslationServiceContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
function joinTexts(texts: Record<string, string | undefined>): string {
|
||||
return (
|
||||
Object.entries(texts).filter(([, content]) => content && content.trim() !== '') as [
|
||||
string,
|
||||
string
|
||||
][]
|
||||
)
|
||||
.map(([key, content]) => `=== ${key} ===\n${content.trim()}\n=== ${key} ===`)
|
||||
.join('\n\n')
|
||||
}
|
||||
|
||||
function splitTranslatedText(translated: string) {
|
||||
const regex = /=== (.+?) ===\n([\s\S]*?)\n=== \1 ===/g
|
||||
const results: Record<string, string | undefined> = {}
|
||||
|
||||
let match: RegExpExecArray | null
|
||||
while ((match = regex.exec(translated)) !== null) {
|
||||
const key = match[1].trim()
|
||||
const content = match[2].trim()
|
||||
results[key] = content
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user