-
Notifications
You must be signed in to change notification settings - Fork 55
i18n: Fix and add translations #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
fc1a001
33f378e
8d1fbbb
632186f
ec10835
f18ca92
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| import { useTranslation } from "react-i18next"; | ||
|
|
||
| import { DropdownMenu, DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu"; | ||
| import { ArrowRightIcon, ChevronDownIcon, SparkleIcon, User, ZapIcon } from "lucide-react"; | ||
| import { RefObject, useEffect, useMemo, useState } from "react"; | ||
|
|
@@ -32,7 +34,7 @@ type MessagesProps = { | |
| }; | ||
|
|
||
| // Utility function to format dates like WhatsApp | ||
| const formatDateSeparator = (date: Date): string => { | ||
| const formatDateSeparator = (date: Date, t: any): string => { | ||
| const today = new Date(); | ||
| const yesterday = new Date(today); | ||
| yesterday.setDate(yesterday.getDate() - 1); | ||
|
Comment on lines
+37
to
40
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Tighten typing for
Suggested implementation: import { useTranslation } from "react-i18next";
import type { TFunction } from "i18next";
import { DropdownMenu, DropdownMenuTrigger } from "@radix-ui/react-dropdown_menu";
import { ArrowRightIcon, ChevronDownIcon, SparkleIcon, User, ZapIcon } from "lucide-react";
import { RefObject, useEffect, useMemo, useState } from "react";
}; // Utility function to format dates like WhatsApp
const formatDateSeparator = (date: Date, t: TFunction, locale: string): string => { // Check if it's today
if (messageDate.toDateString() === today.toDateString()) {
return t("chat.date.today");
}
// Check if it's yesterday
if (messageDate.toDateString() === yesterday.toDateString()) { return messageDate.toLocaleDateString(locale, {Because only part of the file is shown, youβll also need to:
|
||
|
|
@@ -41,22 +43,22 @@ const formatDateSeparator = (date: Date): string => { | |
|
|
||
| // Check if it's today | ||
| if (messageDate.toDateString() === today.toDateString()) { | ||
| return "Hoje"; | ||
| return t("chat.date.today"); | ||
| } | ||
|
|
||
| // Check if it's yesterday | ||
| if (messageDate.toDateString() === yesterday.toDateString()) { | ||
| return "Ontem"; | ||
| return t("chat.date.yesterday"); | ||
| } | ||
|
|
||
| // Check if it's within the last week | ||
| const daysDiff = Math.floor((today.getTime() - messageDate.getTime()) / (1000 * 60 * 60 * 24)); | ||
| if (daysDiff < 7) { | ||
| return messageDate.toLocaleDateString("pt-BR", { weekday: "long" }); | ||
| return messageDate.toLocaleDateString(t("chat.date.location"), { weekday: "long" }); | ||
| } | ||
|
|
||
| // For older dates, show the full date | ||
| return messageDate.toLocaleDateString("pt-BR", { | ||
| return messageDate.toLocaleDateString(t("chat.date.location"), { | ||
| day: "2-digit", | ||
| month: "2-digit", | ||
| year: "numeric", | ||
|
|
@@ -139,6 +141,8 @@ const getMessageText = (messageObj: any): string => { | |
|
|
||
| // Component to render different message types based on messageType | ||
| const MessageContent = ({ message }: { message: Message }) => { | ||
| const { t } = useTranslation(); | ||
|
|
||
| const messageType = message.messageType as string; | ||
|
|
||
| switch (messageType) { | ||
|
|
@@ -149,10 +153,10 @@ const MessageContent = ({ message }: { message: Message }) => { | |
| <div className="p-3 bg-muted rounded-lg max-w-xs"> | ||
| <div className="flex items-center gap-2 mb-2"> | ||
| <div className="text-xl">π€</div> | ||
| <span className="font-medium">Contact</span> | ||
| <span className="font-medium">{t("chat.contact")}</span> | ||
| </div> | ||
| {contactMsg.displayName && <p className="text-sm font-medium">{contactMsg.displayName}</p>} | ||
| {contactMsg.vcard && <p className="text-xs text-muted-foreground">Contact card</p>} | ||
| {contactMsg.vcard && <p className="text-xs text-muted-foreground">{t("chat.contactCard")}</p>} | ||
| </div> | ||
| ); | ||
| } | ||
|
|
@@ -163,7 +167,7 @@ const MessageContent = ({ message }: { message: Message }) => { | |
| <div className="p-3 bg-muted rounded-lg max-w-xs"> | ||
| <div className="flex items-center gap-2 mb-2"> | ||
| <div className="text-xl">π</div> | ||
| <span className="font-medium">Location</span> | ||
| <span className="font-medium">{t("chat.messageType.location.title")}</span> | ||
| </div> | ||
| {locationMsg.name && <p className="text-sm font-medium">{locationMsg.name}</p>} | ||
| {locationMsg.address && <p className="text-xs text-muted-foreground">{locationMsg.address}</p>} | ||
|
|
@@ -173,7 +177,7 @@ const MessageContent = ({ message }: { message: Message }) => { | |
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| className="text-primary hover:underline text-sm mt-1 inline-block"> | ||
| View on Maps | ||
| {t("chat.messageType.location.viewOnMaps")} | ||
| </a> | ||
| )} | ||
| </div> | ||
|
|
@@ -207,8 +211,8 @@ const MessageContent = ({ message }: { message: Message }) => { | |
| /> | ||
| ) : ( | ||
| <div className="rounded bg-muted p-4 max-w-xs"> | ||
| <p className="text-center text-muted-foreground">Image couldn't be loaded</p> | ||
| <p className="text-center text-xs text-muted-foreground mt-1">Missing base64 data and mediaUrl</p> | ||
| <p className="text-center text-muted-foreground">{t("chat.messageType.image.notFound")}</p> | ||
| <p className="text-center text-xs text-muted-foreground mt-1">{t("chat.media.missingData")}</p> | ||
| </div> | ||
| )} | ||
| {message.message.imageMessage?.caption && <p className="text-sm">{message.message.imageMessage.caption}</p>} | ||
|
|
@@ -235,8 +239,8 @@ const MessageContent = ({ message }: { message: Message }) => { | |
| /> | ||
| ) : ( | ||
| <div className="rounded bg-muted p-4 max-w-xs"> | ||
| <p className="text-center text-muted-foreground">Video couldn't be loaded</p> | ||
| <p className="text-center text-xs text-muted-foreground mt-1">Missing base64 data and mediaUrl</p> | ||
| <p className="text-center text-muted-foreground">{t("chat.messageType.video.notFound")}</p> | ||
| <p className="text-center text-xs text-muted-foreground mt-1">{t("chat.media.missingData")}</p> | ||
| </div> | ||
| )} | ||
| {message.message.videoMessage?.caption && <p className="text-sm">{message.message.videoMessage.caption}</p>} | ||
|
|
@@ -252,12 +256,12 @@ const MessageContent = ({ message }: { message: Message }) => { | |
| return audioSrc ? ( | ||
| <audio controls className="w-full max-w-xs"> | ||
| <source src={audioSrc} type="audio/mpeg" /> | ||
| Your browser does not support the audio element. | ||
| {t("chat.messageType.audio.notSupported")}. | ||
| </audio> | ||
| ) : ( | ||
| <div className="rounded bg-muted p-4 max-w-xs"> | ||
| <p className="text-center text-muted-foreground">Audio couldn't be loaded</p> | ||
| <p className="text-center text-xs text-muted-foreground mt-1">Missing base64 data and mediaUrl</p> | ||
| <p className="text-center text-muted-foreground">{t("chat.messageType.audio.notFound")}</p> | ||
| <p className="text-center text-xs text-muted-foreground mt-1">{t("chat.media.missingData")}</p> | ||
| </div> | ||
| ); | ||
|
|
||
|
|
@@ -280,7 +284,7 @@ const MessageContent = ({ message }: { message: Message }) => { | |
| return ( | ||
| <div className="text-xs text-muted-foreground bg-muted p-2 rounded max-w-xs"> | ||
| <details> | ||
| <summary>Unknown message type: {messageType}</summary> | ||
| <summary>{t("chat.messageType.unknownMessage")}: {messageType}</summary> | ||
| <pre className="mt-2 whitespace-pre-wrap break-all text-xs">{JSON.stringify(message.message, null, 2)}</pre> | ||
| </details> | ||
| </div> | ||
|
|
@@ -289,6 +293,7 @@ const MessageContent = ({ message }: { message: Message }) => { | |
| }; | ||
|
|
||
| function Messages({ textareaRef, handleTextareaChange, textareaHeight, lastMessageRef, scrollToBottom }: MessagesProps) { | ||
| const { t } = useTranslation(); | ||
| const { instance } = useInstance(); | ||
| const [messageText, setMessageText] = useState(""); | ||
| const [isSending, setIsSending] = useState(false); | ||
|
|
@@ -416,7 +421,7 @@ function Messages({ textareaRef, handleTextareaChange, textareaHeight, lastMessa | |
| const messageMap = new Map(); | ||
|
|
||
| // First add all messages from React Query | ||
| messages.forEach((message) => messageMap.set(message.key.id, message)); | ||
| messages.forEach((message: { key: { id: any; }; }) => messageMap.set(message.key.id, message)); | ||
|
|
||
| // Then add/update with real-time messages | ||
| realtimeMessages.forEach((message) => { | ||
|
|
@@ -523,7 +528,7 @@ function Messages({ textareaRef, handleTextareaChange, textareaHeight, lastMessa | |
| if (dateString !== currentDate) { | ||
| if (currentGroup.length > 0) { | ||
| grouped.push({ | ||
| date: formatDateSeparator(new Date(currentDate)), | ||
| date: formatDateSeparator(new Date(currentDate), t), | ||
| messages: currentGroup, | ||
| }); | ||
| } | ||
|
|
@@ -536,7 +541,7 @@ function Messages({ textareaRef, handleTextareaChange, textareaHeight, lastMessa | |
|
|
||
| if (currentGroup.length > 0) { | ||
| grouped.push({ | ||
| date: formatDateSeparator(new Date(currentDate)), | ||
| date: formatDateSeparator(new Date(currentDate), t), | ||
| messages: currentGroup, | ||
| }); | ||
| } | ||
|
|
@@ -651,7 +656,7 @@ function Messages({ textareaRef, handleTextareaChange, textareaHeight, lastMessa | |
| <div className="flex items-center rounded-3xl border border-border bg-background px-2 py-1"> | ||
| {instance && <MediaOptions instance={instance} setSelectedMedia={setSelectedMedia} />} | ||
| <Textarea | ||
| placeholder="Enviar mensagem..." | ||
| placeholder={t("chat.message.placeholder")} | ||
| name="message" | ||
| id="message" | ||
| rows={1} | ||
|
|
@@ -665,7 +670,7 @@ function Messages({ textareaRef, handleTextareaChange, textareaHeight, lastMessa | |
| /> | ||
| <Button type="button" size="icon" onClick={sendMessage} disabled={(!messageText.trim() && !selectedMedia) || isSending} className="rounded-full p-2 disabled:opacity-50"> | ||
| <ArrowRightIcon className="h-6 w-6" /> | ||
| <span className="sr-only">Enviar</span> | ||
| <span className="sr-only">{t("chat.message.send")}</span> | ||
| </Button> | ||
| </div> | ||
| </div> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Replace the inline
{ remoteJid: any }typing with the existingChatType(or equivalent).Using
{ remoteJid: any }here throws away the stronger typing you already have and reintroducesanyinto this path. Relying onChatType(or a shared interface) keeps the type information intact and the chat typing consistent across the codebase.Suggested implementation:
Ensure
ChatTypeis imported intosrc/pages/instance/Chat/index.tsxfrom the module where it is defined, e.g.:import type { ChatType } from "@/types/chat";or the correct relative path used elsewhere in the codebase.
If the array
chatsis not yet typed asChatType[], update its typing at the source (e.g., React Query hook return type) toChatType[]so TypeScript can infer the correct type in theforEachwithout needing an explicit annotation.