diff --git a/components/Announcement.tsx b/components/Announcement.tsx index 8fd42fe..2f97297 100644 --- a/components/Announcement.tsx +++ b/components/Announcement.tsx @@ -1,5 +1,6 @@ import Link from "next/link"; import React, { MouseEventHandler } from "react"; +import { Trans } from "next-i18next"; type Props = { toggleAnnouncementBar: MouseEventHandler; @@ -9,19 +10,22 @@ export default function Announcement({ toggleAnnouncementBar }: Props) { const announcementId = localStorage.getItem("announcementId"); return ( -
+

- See what's new in{" "} - - Linkwarden {announcementId} - - ! + , + ]} + />

-
    - {permissions === true ? ( +
      + {permissions === true && (
    • - Edit Collection Info + {t("edit_collection_info")}
    • - ) : undefined} + )}
    • - {permissions === true ? "Share and Collaborate" : "View Team"} + {permissions === true + ? t("share_and_collaborate") + : t("view_team")}
    • @@ -112,7 +116,9 @@ export default function CollectionCard({ collection, className }: Props) { setDeleteCollectionModal(true); }} > - {permissions === true ? "Delete Collection" : "Leave Collection"} + {permissions === true + ? t("delete_collection") + : t("leave_collection")}
diff --git a/components/CollectionListing.tsx b/components/CollectionListing.tsx index 18d36f3..fbe2f7b 100644 --- a/components/CollectionListing.tsx +++ b/components/CollectionListing.tsx @@ -16,12 +16,14 @@ import { CollectionIncludingMembersAndLinkCount } from "@/types/global"; import { useRouter } from "next/router"; import useAccountStore from "@/store/account"; import toast from "react-hot-toast"; +import { useTranslation } from "next-i18next"; interface ExtendedTreeItem extends TreeItem { data: Collection; } const CollectionListing = () => { + const { t } = useTranslation(); const { collections, updateCollection } = useCollectionStore(); const { account, updateAccount } = useAccountStore(); @@ -141,9 +143,7 @@ const CollectionListing = () => { (destinationCollection?.ownerId !== account.id && destination.parentId !== "root") ) { - return toast.error( - "You can't make change to a collection you don't own." - ); + return toast.error(t("cant_change_collection_you_dont_own")); } setTree((currentTree) => moveItemOnTree(currentTree!, source, destination)); @@ -203,7 +203,7 @@ const CollectionListing = () => { if (!tree) { return (

- You Have No Collections... + {t("you_have_no_collections")}

); } else diff --git a/components/FilterSearchDropdown.tsx b/components/FilterSearchDropdown.tsx index 01b8906..68bb55b 100644 --- a/components/FilterSearchDropdown.tsx +++ b/components/FilterSearchDropdown.tsx @@ -1,5 +1,6 @@ import { dropdownTriggerer } from "@/lib/client/utils"; import React from "react"; +import { useTranslation } from "next-i18next"; type Props = { setSearchFilter: Function; @@ -16,6 +17,8 @@ export default function FilterSearchDropdown({ setSearchFilter, searchFilter, }: Props) { + const { t } = useTranslation(); + return (
{ - setSearchFilter({ ...searchFilter, name: !searchFilter.name }); - }} + onChange={() => + setSearchFilter({ ...searchFilter, name: !searchFilter.name }) + } /> - Name + {t("name")}
  • @@ -56,11 +59,11 @@ export default function FilterSearchDropdown({ name="search-filter-checkbox" className="checkbox checkbox-primary" checked={searchFilter.url} - onChange={() => { - setSearchFilter({ ...searchFilter, url: !searchFilter.url }); - }} + onChange={() => + setSearchFilter({ ...searchFilter, url: !searchFilter.url }) + } /> - Link + {t("link")}
  • @@ -74,14 +77,14 @@ export default function FilterSearchDropdown({ name="search-filter-checkbox" className="checkbox checkbox-primary" checked={searchFilter.description} - onChange={() => { + onChange={() => setSearchFilter({ ...searchFilter, description: !searchFilter.description, - }); - }} + }) + } /> - Description + {t("description")}
  • @@ -95,14 +98,11 @@ export default function FilterSearchDropdown({ name="search-filter-checkbox" className="checkbox checkbox-primary" checked={searchFilter.tags} - onChange={() => { - setSearchFilter({ - ...searchFilter, - tags: !searchFilter.tags, - }); - }} + onChange={() => + setSearchFilter({ ...searchFilter, tags: !searchFilter.tags }) + } /> - Tags + {t("tags")}
  • @@ -116,16 +116,17 @@ export default function FilterSearchDropdown({ name="search-filter-checkbox" className="checkbox checkbox-primary" checked={searchFilter.textContent} - onChange={() => { + onChange={() => setSearchFilter({ ...searchFilter, textContent: !searchFilter.textContent, - }); - }} + }) + } /> - Full Content - -
    Slower
    + {t("full_content")} +
    + {t("slower")} +
  • diff --git a/components/InstallApp.tsx b/components/InstallApp.tsx index b8bcf84..4b70309 100644 --- a/components/InstallApp.tsx +++ b/components/InstallApp.tsx @@ -1,5 +1,6 @@ import { isPWA } from "@/lib/client/utils"; import React, { useState } from "react"; +import { Trans } from "next-i18next"; type Props = {}; @@ -25,15 +26,17 @@ const InstallApp = (props: Props) => { />

    - Install Linkwarden to your home screen for a faster access and - enhanced experience.{" "} - - Learn more - + , + ]} + />

    + +
    +
    + )} + + {bulkDeleteLinksModal && ( + { + setBulkDeleteLinksModal(false); + }} + /> + )} + + {bulkEditLinksModal && ( + { + setBulkEditLinksModal(false); + }} + /> + )} + + ); +}; + +export default LinkListOptions; diff --git a/components/LinkViews/LinkCard.tsx b/components/LinkViews/LinkCard.tsx index 6385a9a..70f553c 100644 --- a/components/LinkViews/LinkCard.tsx +++ b/components/LinkViews/LinkCard.tsx @@ -20,6 +20,7 @@ import useAccountStore from "@/store/account"; import usePermissions from "@/hooks/usePermissions"; import toast from "react-hot-toast"; import LinkTypeBadge from "./LinkComponents/LinkTypeBadge"; +import { useTranslation } from "next-i18next"; type Props = { link: LinkIncludingShortenedCollectionAndTags; @@ -30,6 +31,8 @@ type Props = { }; export default function LinkCard({ link, flipDropdown, editMode }: Props) { + const { t } = useTranslation(); + const viewMode = localStorage.getItem("viewMode") || "card"; const { collections } = useCollectionStore(); const { account } = useAccountStore(); @@ -121,9 +124,7 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) { selectable ? handleCheckboxClick(link) : editMode - ? toast.error( - "You don't have permission to edit or delete this item." - ) + ? toast.error(t("link_selection_error")) : undefined } > @@ -167,7 +168,7 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) {
    -

    +

    {unescapeString(link.name)}

    @@ -197,7 +198,9 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) { >
    -

    Description

    +

    + {t("description")} +


    @@ -205,13 +208,15 @@ export default function LinkCard({ link, flipDropdown, editMode }: Props) { unescapeString(link.description) ) : ( - No description provided. + {t("no_description")} )}

    {link.tags && link.tags[0] && ( <> -

    Tags

    +

    + {t("tags")} +


    diff --git a/components/LinkViews/LinkComponents/LinkActions.tsx b/components/LinkViews/LinkComponents/LinkActions.tsx index 7844e8b..46aff1d 100644 --- a/components/LinkViews/LinkComponents/LinkActions.tsx +++ b/components/LinkViews/LinkComponents/LinkActions.tsx @@ -11,6 +11,7 @@ import useLinkStore from "@/store/links"; import { toast } from "react-hot-toast"; import useAccountStore from "@/store/account"; import { dropdownTriggerer } from "@/lib/client/utils"; +import { useTranslation } from "next-i18next"; type Props = { link: LinkIncludingShortenedCollectionAndTags; @@ -30,6 +31,8 @@ export default function LinkActions({ alignToTop, flipDropdown, }: Props) { + const { t } = useTranslation(); + const permissions = usePermissions(link.collection.id as number); const [editLinkModal, setEditLinkModal] = useState(false); @@ -43,7 +46,7 @@ export default function LinkActions({ const pinLink = async () => { const isAlreadyPinned = link?.pinnedBy && link.pinnedBy[0]; - const load = toast.loading("Applying..."); + const load = toast.loading(t("applying")); const response = await updateLink({ ...link, @@ -53,17 +56,17 @@ export default function LinkActions({ toast.dismiss(load); response.ok && - toast.success(`Link ${isAlreadyPinned ? "Unpinned!" : "Pinned!"}`); + toast.success(isAlreadyPinned ? t("link_unpinned") : t("link_unpinned")); }; const deleteLink = async () => { - const load = toast.loading("Deleting..."); + const load = toast.loading(t("deleting")); const response = await removeLink(link.id as number); toast.dismiss(load); - response.ok && toast.success(`Link Deleted.`); + response.ok && toast.success(t("deleted")); }; return ( @@ -96,8 +99,8 @@ export default function LinkActions({ }} > {link?.pinnedBy && link.pinnedBy[0] - ? "Unpin" - : "Pin to Dashboard"} + ? t("unpin") + : t("pin_to_dashboard")}
    {linkInfo !== undefined && toggleShowInfo ? ( @@ -110,7 +113,7 @@ export default function LinkActions({ toggleShowInfo(); }} > - {!linkInfo ? "Show" : "Hide"} Link Details + {!linkInfo ? t("show_link_details") : t("hide_link_details")}
    ) : undefined} @@ -124,7 +127,7 @@ export default function LinkActions({ setEditLinkModal(true); }} > - Edit Link + {t("edit_link")} ) : undefined} @@ -138,7 +141,7 @@ export default function LinkActions({ setPreservedFormatsModal(true); }} > - Preserved Formats + {t("preserved_formats")} )} @@ -152,7 +155,7 @@ export default function LinkActions({ e.shiftKey ? deleteLink() : setDeleteLinkModal(true); }} > - Delete + {t("delete")} ) : undefined} diff --git a/components/LinkViews/LinkComponents/LinkGroupedIconURL.tsx b/components/LinkViews/LinkComponents/LinkGroupedIconURL.tsx deleted file mode 100644 index 2d34f9c..0000000 --- a/components/LinkViews/LinkComponents/LinkGroupedIconURL.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; -import Image from "next/image"; -import isValidUrl from "@/lib/shared/isValidUrl"; -import React from "react"; -import Link from "next/link"; - -export default function LinkGroupedIconURL({ - link, -}: { - link: LinkIncludingShortenedCollectionAndTags; -}) { - const url = - isValidUrl(link.url || "") && link.url ? new URL(link.url) : undefined; - - const [showFavicon, setShowFavicon] = React.useState(true); - - let shortendURL; - - try { - shortendURL = new URL(link.url || "").host.toLowerCase(); - } catch (error) { - console.log(error); - } - - return ( - -
    - {link.url && url && showFavicon ? ( - { - setShowFavicon(false); - }} - /> - ) : showFavicon === false ? ( - - ) : link.type === "pdf" ? ( - - ) : link.type === "image" ? ( - - ) : undefined} -

    -

    {shortendURL}

    -

    -
    - - ); -} diff --git a/components/LinkViews/LinkList.tsx b/components/LinkViews/LinkList.tsx index 10b008d..0f944d8 100644 --- a/components/LinkViews/LinkList.tsx +++ b/components/LinkViews/LinkList.tsx @@ -10,13 +10,13 @@ import LinkActions from "@/components/LinkViews/LinkComponents/LinkActions"; import LinkDate from "@/components/LinkViews/LinkComponents/LinkDate"; import LinkCollection from "@/components/LinkViews/LinkComponents/LinkCollection"; import LinkIcon from "@/components/LinkViews/LinkComponents/LinkIcon"; -import Link from "next/link"; import { isPWA } from "@/lib/client/utils"; import { generateLinkHref } from "@/lib/client/generateLinkHref"; import useAccountStore from "@/store/account"; import usePermissions from "@/hooks/usePermissions"; import toast from "react-hot-toast"; import LinkTypeBadge from "./LinkComponents/LinkTypeBadge"; +import { useTranslation } from "next-i18next"; type Props = { link: LinkIncludingShortenedCollectionAndTags; @@ -31,6 +31,8 @@ export default function LinkCardCompact({ flipDropdown, editMode, }: Props) { + const { t } = useTranslation(); + const { collections } = useCollectionStore(); const { account } = useAccountStore(); const { links, setSelectedLinks, selectedLinks } = useLinkStore(); @@ -96,9 +98,7 @@ export default function LinkCardCompact({ selectable ? handleCheckboxClick(link) : editMode - ? toast.error( - "You don't have permission to edit or delete this item." - ) + ? toast.error(t("link_selection_error")) : undefined } > diff --git a/components/LinkViews/LinkMasonry.tsx b/components/LinkViews/LinkMasonry.tsx index 165c43f..369a004 100644 --- a/components/LinkViews/LinkMasonry.tsx +++ b/components/LinkViews/LinkMasonry.tsx @@ -20,6 +20,7 @@ import useAccountStore from "@/store/account"; import usePermissions from "@/hooks/usePermissions"; import toast from "react-hot-toast"; import LinkTypeBadge from "./LinkComponents/LinkTypeBadge"; +import { useTranslation } from "next-i18next"; type Props = { link: LinkIncludingShortenedCollectionAndTags; @@ -30,6 +31,8 @@ type Props = { }; export default function LinkMasonry({ link, flipDropdown, editMode }: Props) { + const { t } = useTranslation(); + const { collections } = useCollectionStore(); const { account } = useAccountStore(); @@ -120,9 +123,7 @@ export default function LinkMasonry({ link, flipDropdown, editMode }: Props) { selectable ? handleCheckboxClick(link) : editMode - ? toast.error( - "You don't have permission to edit or delete this item." - ) + ? toast.error(t("link_selection_error")) : undefined } > @@ -164,7 +165,7 @@ export default function LinkMasonry({ link, flipDropdown, editMode }: Props) { )}
    -

    +

    {unescapeString(link.name)}

    @@ -210,7 +211,9 @@ export default function LinkMasonry({ link, flipDropdown, editMode }: Props) { >
    -

    Description

    +

    + {t("description")} +


    @@ -218,13 +221,15 @@ export default function LinkMasonry({ link, flipDropdown, editMode }: Props) { unescapeString(link.description) ) : ( - No description provided. + {t("no_description")} )}

    {link.tags[0] && ( <> -

    Tags

    +

    + {t("tags")} +


    diff --git a/components/Loader.tsx b/components/Loader.tsx deleted file mode 100644 index 87e6f3e..0000000 --- a/components/Loader.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export default function Loading() { - return ( -
    -

    Loading...

    -
    - ); -} diff --git a/components/MobileNavigation.tsx b/components/MobileNavigation.tsx index e5cbbf7..fda7f98 100644 --- a/components/MobileNavigation.tsx +++ b/components/MobileNavigation.tsx @@ -5,10 +5,12 @@ import NewLinkModal from "./ModalContent/NewLinkModal"; import NewCollectionModal from "./ModalContent/NewCollectionModal"; import UploadFileModal from "./ModalContent/UploadFileModal"; import MobileNavigationButton from "./MobileNavigationButton"; +import { useTranslation } from "next-i18next"; type Props = {}; export default function MobileNavigation({}: Props) { + const { t } = useTranslation(); const [newLinkModal, setNewLinkModal] = useState(false); const [newCollectionModal, setNewCollectionModal] = useState(false); const [uploadFileModal, setUploadFileModal] = useState(false); @@ -49,21 +51,21 @@ export default function MobileNavigation({}: Props) { tabIndex={0} role="button" > - New Link + {t("new_link")} + + +
  • +
    { + (document?.activeElement as HTMLElement)?.blur(); + setUploadFileModal(true); + }} + tabIndex={0} + role="button" + > + {t("upload_file")}
  • - {/*
  • -
    { - (document?.activeElement as HTMLElement)?.blur(); - setUploadFileModal(true); - }} - tabIndex={0} - role="button" - > - Upload File -
    -
  • */}
  • { @@ -73,7 +75,7 @@ export default function MobileNavigation({}: Props) { tabIndex={0} role="button" > - New Collection + {t("new_collection")}
  • diff --git a/components/ModalContent/BulkDeleteLinksModal.tsx b/components/ModalContent/BulkDeleteLinksModal.tsx index 28c565c..5dc08f7 100644 --- a/components/ModalContent/BulkDeleteLinksModal.tsx +++ b/components/ModalContent/BulkDeleteLinksModal.tsx @@ -3,20 +3,18 @@ import useLinkStore from "@/store/links"; import toast from "react-hot-toast"; import Modal from "../Modal"; import Button from "../ui/Button"; +import { useTranslation } from "next-i18next"; type Props = { onClose: Function; }; export default function BulkDeleteLinksModal({ onClose }: Props) { + const { t } = useTranslation(); const { selectedLinks, setSelectedLinks, deleteLinksById } = useLinkStore(); const deleteLink = async () => { - const load = toast.loading( - `Deleting ${selectedLinks.length} Link${ - selectedLinks.length > 1 ? "s" : "" - }...` - ); + const load = toast.loading(t("deleting")); const response = await deleteLinksById( selectedLinks.map((link) => link.id as number) @@ -25,12 +23,7 @@ export default function BulkDeleteLinksModal({ onClose }: Props) { toast.dismiss(load); if (response.ok) { - toast.success( - `Deleted ${selectedLinks.length} Link${ - selectedLinks.length > 1 ? "s" : "" - }` - ); - + toast.success(t("deleted")); setSelectedLinks([]); onClose(); } else toast.error(response.data as string); @@ -39,33 +32,32 @@ export default function BulkDeleteLinksModal({ onClose }: Props) { return (

    - Delete {selectedLinks.length} Link{selectedLinks.length > 1 ? "s" : ""} + {selectedLinks.length === 1 + ? t("delete_link") + : t("delete_links", { count: selectedLinks.length })}

    - {selectedLinks.length > 1 ? ( -

    Are you sure you want to delete {selectedLinks.length} links?

    - ) : ( -

    Are you sure you want to delete this link?

    - )} +

    + {selectedLinks.length === 1 + ? t("link_deletion_confirmation_message") + : t("links_deletion_confirmation_message", { + count: selectedLinks.length, + })} +

    - - Warning: This action is irreversible! - + {t("warning_irreversible")}
    -

    - Hold the Shift key while clicking - 'Delete' to bypass this confirmation in the future. -

    +

    {t("shift_key_tip")}

    diff --git a/components/ModalContent/BulkEditLinksModal.tsx b/components/ModalContent/BulkEditLinksModal.tsx index 07b3914..5f3d31a 100644 --- a/components/ModalContent/BulkEditLinksModal.tsx +++ b/components/ModalContent/BulkEditLinksModal.tsx @@ -5,12 +5,14 @@ import useLinkStore from "@/store/links"; import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; import toast from "react-hot-toast"; import Modal from "../Modal"; +import { useTranslation } from "next-i18next"; type Props = { onClose: Function; }; export default function BulkEditLinksModal({ onClose }: Props) { + const { t } = useTranslation(); const { updateLinks, selectedLinks, setSelectedLinks } = useLinkStore(); const [submitLoader, setSubmitLoader] = useState(false); const [removePreviousTags, setRemovePreviousTags] = useState(false); @@ -20,7 +22,6 @@ export default function BulkEditLinksModal({ onClose }: Props) { const setCollection = (e: any) => { const collectionId = e?.value || null; - console.log(updatedValues); setUpdatedValues((prevValues) => ({ ...prevValues, collectionId })); }; @@ -33,7 +34,7 @@ export default function BulkEditLinksModal({ onClose }: Props) { if (!submitLoader) { setSubmitLoader(true); - const load = toast.loading("Updating..."); + const load = toast.loading(t("updating")); const response = await updateLinks( selectedLinks, @@ -44,7 +45,7 @@ export default function BulkEditLinksModal({ onClose }: Props) { toast.dismiss(load); if (response.ok) { - toast.success(`Updated!`); + toast.success(t("updated")); setSelectedLinks([]); onClose(); } else toast.error(response.data as string); @@ -57,13 +58,15 @@ export default function BulkEditLinksModal({ onClose }: Props) { return (

    - Edit {selectedLinks.length} Link{selectedLinks.length > 1 ? "s" : ""} + {selectedLinks.length === 1 + ? t("edit_link") + : t("edit_links", { count: selectedLinks.length })}

    -

    Move to Collection

    +

    {t("move_to_collection")}

    -

    Add Tags

    +

    {t("add_tags")}

    @@ -84,7 +87,7 @@ export default function BulkEditLinksModal({ onClose }: Props) { checked={removePreviousTags} onChange={(e) => setRemovePreviousTags(e.target.checked)} /> - Remove previous tags + {t("remove_previous_tags")}
    @@ -94,7 +97,7 @@ export default function BulkEditLinksModal({ onClose }: Props) { className="btn btn-accent dark:border-violet-400 text-white" onClick={submit} > - Save Changes + {t("save_changes")}
    diff --git a/components/ModalContent/DeleteCollectionModal.tsx b/components/ModalContent/DeleteCollectionModal.tsx index 6548553..504d3a3 100644 --- a/components/ModalContent/DeleteCollectionModal.tsx +++ b/components/ModalContent/DeleteCollectionModal.tsx @@ -7,6 +7,7 @@ import { useRouter } from "next/router"; import usePermissions from "@/hooks/usePermissions"; import Modal from "../Modal"; import Button from "../ui/Button"; +import { useTranslation } from "next-i18next"; type Props = { onClose: Function; @@ -17,42 +18,40 @@ export default function DeleteCollectionModal({ onClose, activeCollection, }: Props) { + const { t } = useTranslation(); const [collection, setCollection] = useState(activeCollection); + const [submitLoader, setSubmitLoader] = useState(false); + const { removeCollection } = useCollectionStore(); + const router = useRouter(); + const [inputField, setInputField] = useState(""); + const permissions = usePermissions(collection.id as number); useEffect(() => { setCollection(activeCollection); }, []); - const [submitLoader, setSubmitLoader] = useState(false); - const { removeCollection } = useCollectionStore(); - const router = useRouter(); - const [inputField, setInputField] = useState(""); - - const permissions = usePermissions(collection.id as number); - const submit = async () => { - if (permissions === true) if (collection.name !== inputField) return null; - + if (permissions === true && collection.name !== inputField) return; if (!submitLoader) { setSubmitLoader(true); if (!collection) return null; setSubmitLoader(true); - const load = toast.loading("Deleting..."); + const load = toast.loading(t("deleting_collection")); - let response; - - response = await removeCollection(collection.id as any); + let response = await removeCollection(collection.id as number); toast.dismiss(load); if (response.ok) { - toast.success(`Deleted.`); + toast.success(t("deleted")); onClose(); router.push("/collections"); - } else toast.error(response.data as string); + } else { + toast.error(response.data as string); + } setSubmitLoader(false); } @@ -61,7 +60,7 @@ export default function DeleteCollectionModal({ return (

    - {permissions === true ? "Delete" : "Leave"} Collection + {permissions === true ? t("delete_collection") : t("leave_collection")}

    @@ -69,32 +68,26 @@ export default function DeleteCollectionModal({
    {permissions === true ? ( <> -
    -

    - To confirm, type " - {collection.name} - " in the box below: -

    - - setInputField(e.target.value)} - placeholder={`Type "${collection.name}" Here.`} - className="w-3/4 mx-auto" - /> -
    +

    {t("confirm_deletion_prompt", { name: collection.name })}

    + setInputField(e.target.value)} + placeholder={t("type_name_placeholder", { + name: collection.name, + })} + className="w-3/4 mx-auto" + />
    - Warning: Deleting this collection will permanently erase - all its contents, and it will become inaccessible to everyone, - including members with previous access. + {t("warning")}: + {t("deletion_warning")}
    ) : ( -

    Click the button below to leave the current collection.

    +

    {t("leave_prompt")}

    )}
    diff --git a/components/ModalContent/DeleteLinkModal.tsx b/components/ModalContent/DeleteLinkModal.tsx index b884dab..92de104 100644 --- a/components/ModalContent/DeleteLinkModal.tsx +++ b/components/ModalContent/DeleteLinkModal.tsx @@ -5,6 +5,7 @@ import toast from "react-hot-toast"; import Modal from "../Modal"; import { useRouter } from "next/router"; import Button from "../ui/Button"; +import { useTranslation } from "next-i18next"; type Props = { onClose: Function; @@ -12,11 +13,10 @@ type Props = { }; export default function DeleteLinkModal({ onClose, activeLink }: Props) { + const { t } = useTranslation(); const [link, setLink] = useState(activeLink); - const { removeLink } = useLinkStore(); - const router = useRouter(); useEffect(() => { @@ -24,13 +24,13 @@ export default function DeleteLinkModal({ onClose, activeLink }: Props) { }, []); const deleteLink = async () => { - const load = toast.loading("Deleting..."); + const load = toast.loading(t("deleting")); const response = await removeLink(link.id as number); toast.dismiss(load); - response.ok && toast.success(`Link Deleted.`); + response.ok && toast.success(t("deleted")); if (router.pathname.startsWith("/links/[id]")) { router.push("/dashboard"); @@ -41,28 +41,25 @@ export default function DeleteLinkModal({ onClose, activeLink }: Props) { return ( -

    Delete Link

    +

    {t("delete_link")}

    -

    Are you sure you want to delete this Link?

    +

    {t("link_deletion_confirmation_message")}

    - Warning: This action is irreversible! + {t("warning")}: {t("irreversible_warning")}
    -

    - Hold the Shift key while clicking - 'Delete' to bypass this confirmation in the future. -

    +

    {t("shift_key_tip")}

    diff --git a/components/ModalContent/DeleteUserModal.tsx b/components/ModalContent/DeleteUserModal.tsx index 3c4c1a5..73a81ed 100644 --- a/components/ModalContent/DeleteUserModal.tsx +++ b/components/ModalContent/DeleteUserModal.tsx @@ -2,6 +2,7 @@ import toast from "react-hot-toast"; import Modal from "../Modal"; import useUserStore from "@/store/admin/users"; import Button from "../ui/Button"; +import { useTranslation } from "next-i18next"; type Props = { onClose: Function; @@ -9,39 +10,40 @@ type Props = { }; export default function DeleteUserModal({ onClose, userId }: Props) { + const { t } = useTranslation(); const { removeUser } = useUserStore(); const deleteUser = async () => { - const load = toast.loading("Deleting..."); + const load = toast.loading(t("deleting_user")); const response = await removeUser(userId); toast.dismiss(load); - response.ok && toast.success(`User Deleted.`); + response.ok && toast.success(t("user_deleted")); onClose(); }; return ( -

    Delete User

    +

    {t("delete_user")}

    -

    Are you sure you want to remove this user?

    +

    {t("confirm_user_deletion")}

    - Warning: This action is irreversible! + {t("warning")}: {t("irreversible_action_warning")}
    diff --git a/components/ModalContent/EditCollectionModal.tsx b/components/ModalContent/EditCollectionModal.tsx index fbbf53e..b2105f5 100644 --- a/components/ModalContent/EditCollectionModal.tsx +++ b/components/ModalContent/EditCollectionModal.tsx @@ -5,6 +5,7 @@ import toast from "react-hot-toast"; import { HexColorPicker } from "react-colorful"; import { CollectionIncludingMembersAndLinkCount } from "@/types/global"; import Modal from "../Modal"; +import { useTranslation } from "next-i18next"; type Props = { onClose: Function; @@ -15,6 +16,7 @@ export default function EditCollectionModal({ onClose, activeCollection, }: Props) { + const { t } = useTranslation(); const [collection, setCollection] = useState(activeCollection); @@ -28,16 +30,14 @@ export default function EditCollectionModal({ setSubmitLoader(true); - const load = toast.loading("Updating..."); + const load = toast.loading(t("updating_collection")); - let response; - - response = await updateCollection(collection as any); + let response = await updateCollection(collection as any); toast.dismiss(load); if (response.ok) { - toast.success(`Updated!`); + toast.success(t("updated")); onClose(); } else toast.error(response.data as string); @@ -47,29 +47,35 @@ export default function EditCollectionModal({ return ( -

    Edit Collection Info

    +

    {t("edit_collection_info")}

    -

    Name

    +

    {t("name")}

    setCollection({ ...collection, name: e.target.value }) } />
    -

    Color

    -
    +

    {t("color")}

    +
    + + setCollection({ ...collection, color }) + } + />
    - Reset + {t("reset")}
    - setCollection({ ...collection, color: e })} - />
    -

    Description

    +

    {t("description")}