Translate messages

This commit is contained in:
MartinBraquet
2026-01-02 14:59:53 +02:00
parent 593c2ac024
commit 0bb52e72f7
4 changed files with 58 additions and 26 deletions

View File

@@ -12,6 +12,7 @@ import {useClickOutside} from "web/hooks/use-click-outside"
import {PrivateChatMessage} from "common/chat-message";
import {updateReactionUI} from "web/lib/supabase/chat-messages";
import {useIsMobile} from "web/hooks/use-is-mobile";
import {useT} from 'web/lib/locale'
const REACTIONS = ['👍', '❤️', '😂', '😮', '😢', '👎']
@@ -37,6 +38,7 @@ export function MessageActions(props: {
const user = useUser()
const isOwner = user?.id === message.userId
const isMobile = useIsMobile()
const t = useT()
useClickOutside(emojiPickerRef, () => {
setShowEmojiPicker(false)
@@ -50,17 +52,17 @@ export function MessageActions(props: {
}, [openEmojiPickerKey])
const handleDelete = async () => {
if (!confirm('Are you sure you want to delete this message?')) return
if (!confirm(t('messages.delete_confirm', 'Are you sure you want to delete this message?'))) return
const messageId = message.id
try {
await api('delete-message', {messageId})
toast.success('Message deleted')
toast.success(t('messages.deleted', 'Message deleted'))
setMessages?.((prevMessages) => {
if (!prevMessages) return prevMessages
return prevMessages.filter((m) => m.id !== messageId)
})
} catch (error) {
toast.error('Failed to delete message')
toast.error(t('messages.delete_failed', 'Failed to delete message'))
console.error(error)
}
}
@@ -97,17 +99,17 @@ export function MessageActions(props: {
<DropdownMenu
items={[
isOwner && {
name: 'Edit',
name: t('messages.action.edit', 'Edit'),
icon: <PencilIcon className="h-4 w-4"/>,
onClick: onRequestEdit,
},
isOwner && {
name: 'Delete',
name: t('messages.action.delete', 'Delete'),
icon: <TrashIcon className="h-4 w-4"/>,
onClick: handleDelete,
},
{
name: 'Add Reaction',
name: t('messages.action.add_reaction', 'Add Reaction'),
icon: <EmojiHappyIcon className="h-4 w-4"/>,
onClick: () => {
setShowEmojiPicker(!showEmojiPicker)
@@ -121,7 +123,7 @@ export function MessageActions(props: {
/>
)}
{/*{message.isEdited && (*/}
{/* <span className="text-xs text-gray-400">edited</span>*/}
{/* <span className="text-xs text-gray-400">{t('messages.edited', 'edited')}</span>*/}
{/*)}*/}
</div>
)

View File

@@ -11,14 +11,16 @@ import { SelectUsers } from 'web/components/select-users'
import { DisplayUser } from 'common/api/user-types'
import { usePrivateUser } from 'web/hooks/use-user'
import { buildArray } from 'common/util/array'
import { useT } from 'web/lib/locale'
export default function NewMessageButton() {
const [open, setOpen] = useState(false)
const t = useT()
return (
<>
<Button className="h-fit gap-1" onClick={() => setOpen(true)}>
<PlusIcon className="h-5 w-5" aria-hidden="true" />
New Message
{t('messages.new_message', 'New Message')}
</Button>
<MessageModal open={open} setOpen={setOpen} />
</>
@@ -32,6 +34,7 @@ function MessageModal(props: {
const { open, setOpen } = props
const privateUser = usePrivateUser()
const router = useRouter()
const t = useT()
const [users, setUsers] = useState<DisplayUser[]>([])
const createChannel = async () => {
@@ -62,7 +65,7 @@ function MessageModal(props: {
</Col>
<Row className={'bg-canvas-0 justify-end rounded-b-md p-2'}>
<Button disabled={users.length === 0} onClick={createChannel}>
Create
{t('messages.create', 'Create')}
</Button>
</Row>
</Modal>

View File

@@ -408,7 +408,31 @@
"help.actions.faq_button": "Consulter la FAQ",
"messages.title": "Messages",
"messages.seo.description": "Vos messages",
"messages.input_placeholder": "Envoyer un message",
"messages.menu.see_members": "Voir les membres",
"messages.menu.mute_1_day": "Couper le son pendant 1 jour",
"messages.menu.mute_forever": "Couper le son définitivement",
"messages.menu.leave_chat": "Quitter la conversation",
"messages.action.edit": "Modifier",
"messages.action.delete": "Supprimer",
"messages.action.add_reaction": "Ajouter une réaction",
"messages.toast.muting_1_day.loading": "Couper le son pour 1 jour...",
"messages.toast.muting_1_day.success": "Son coupé pour 1 jour",
"messages.toast.muting_1_day.error": "Échec de la coupure du son",
"messages.toast.muting_forever.loading": "Couper le son définitivement...",
"messages.toast.muting_forever.success": "Son coupé définitivement",
"messages.toast.muting_forever.error": "Échec de la coupure du son",
"messages.empty": "Vous n'avez pas encore de messages.",
"messages.delete_confirm": "Êtes-vous sûr de vouloir supprimer ce message ?",
"messages.deleted": "Message supprimé",
"messages.delete_failed": "Échec de la suppression du message",
"messages.more": " de plus",
"messages.you_prefix": "Vous : "
"messages.new_message": "Nouveau message",
"messages.create": "Créer",
"messages.you_prefix": "Vous : ",
"messages.edited": "modifié",
"messages.toast.edit_failed": "Échec de la modification du message.",
"messages.toast.send_failed": "Échec de l'envoi du message. Veuillez réessayer plus tard ou contacter le support si le problème persiste.",
"aboutsettings.copied": "Copié !",
"aboutsettings.copy_info": "Copier les infos"
}

View File

@@ -34,9 +34,11 @@ import {ChatMessage} from 'common/chat-message'
import {BackButton} from 'web/components/back-button'
import {SEO} from "web/components/SEO"
import {cleanDoc} from "common/util/parse";
import {useT} from 'web/lib/locale'
export default function PrivateMessagesPage() {
const router = useRouter()
const t = useT()
const {channelId: channelIdString} = router.query as { channelId: string }
const channelId = router.isReady ? parseInt(channelIdString) : undefined
const user = useUser()
@@ -47,8 +49,8 @@ export default function PrivateMessagesPage() {
return (
<PageBase trackPageView={'private messages page'}>
<SEO
title={'Messages'}
description={'Messages'}
title={t('messages.title', 'Messages')}
description={t('messages.seo.description', 'Messages')}
url={`/messages/${channelIdString}`}
/>
{router.isReady && channelId && user ? (
@@ -96,6 +98,7 @@ export const PrivateChat = (props: {
memberIds: string[]
}) => {
const {user, channel, memberIds} = props
const t = useT()
const channelId = channel.channel_id
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
const isMobile = useIsMobile()
@@ -147,7 +150,7 @@ export const PrivateChat = (props: {
const editor = useTextEditor({
key: `private-message-${channelId}-${user.id}`,
size: 'sm',
placeholder: 'Send a message',
placeholder: t('messages.input_placeholder', 'Send a message'),
})
useEffect(() => {
@@ -211,8 +214,8 @@ export const PrivateChat = (props: {
} catch (e) {
toast.error(
editingMessage
? "Couldn't edit message."
: "Couldn't send message. Please try again later or contact support if the problem persists."
? t('messages.toast.edit_failed', "Couldn't edit message.")
: t('messages.toast.send_failed', "Couldn't send message. Please try again later or contact support if the problem persists.")
)
console.error(e)
} finally {
@@ -277,14 +280,14 @@ export const PrivateChat = (props: {
items={buildArray(
{
icon: <FaUserFriends className={'h-5 w-5'}/>,
name: 'See members',
name: t('messages.menu.see_members', 'See members'),
onClick: () => {
setShowUsers(true)
},
},
{
icon: <GiSpeakerOff className="h-5 w-5"/>,
name: 'Mute 1 day',
name: t('messages.menu.mute_1_day', 'Mute 1 day'),
onClick: async () => {
await toast.promise(
api('update-private-user-message-channel', {
@@ -292,16 +295,16 @@ export const PrivateChat = (props: {
notifyAfterTime: Date.now() + DAY_MS,
}),
{
loading: 'Muting for 1 day...',
success: 'Muted for 1 day',
error: 'Failed to mute',
loading: t('messages.toast.muting_1_day.loading', 'Muting for 1 day...'),
success: t('messages.toast.muting_1_day.success', 'Muted for 1 day'),
error: t('messages.toast.muting_1_day.error', 'Failed to mute'),
}
)
},
},
{
icon: <GiSpeakerOff className="h-5 w-5"/>,
name: 'Mute forever',
name: t('messages.menu.mute_forever', 'Mute forever'),
onClick: async () => {
await toast.promise(
api('update-private-user-message-channel', {
@@ -309,16 +312,16 @@ export const PrivateChat = (props: {
notifyAfterTime: Date.now() + 100 * YEAR_MS,
}),
{
loading: 'Muting forever...',
success: 'Muted forever',
error: 'Failed to mute',
loading: t('messages.toast.muting_forever.loading', 'Muting forever...'),
success: t('messages.toast.muting_forever.success', 'Muted forever'),
error: t('messages.toast.muting_forever.error', 'Failed to mute'),
}
)
},
},
{
icon: <FaUserMinus className="h-5 w-5"/>,
name: 'Leave chat',
name: t('messages.menu.leave_chat', 'Leave chat'),
onClick: async () => {
await api('leave-private-user-message-channel', {
channelId: channelId,
@@ -410,7 +413,7 @@ export const PrivateChat = (props: {
)}
{realtimeMessages && messages.length === 0 && (
<div className="text-ink-500 dark:text-ink-600 p-2">
No messages yet.
{t('messages.empty', 'No messages yet.')}
</div>
)}
</div>