diff --git a/.eslintrc.json b/.eslintrc.json
index 420e330c024..0c959230cb3 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -52,17 +52,6 @@
],
"simple-import-sort/exports": "error",
"no-unused-vars": "off",
- "no-restricted-imports": [
- "warn",
- {
- "paths": [
- {
- "name": "@/hooks/useTranslation",
- "message": "Deprecated (#18742): bind namespaces directly with useTranslations from next-intl (client) or getTranslations from next-intl/server (server)."
- }
- ]
- }
- ],
"unused-imports/no-unused-vars": [
"error",
{
diff --git a/AGENTS.md b/AGENTS.md
index 9ff803c47cc..e28946d935c 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -166,7 +166,7 @@ For pipeline mechanics, recovery, manifests, ETHGlossary integration, and the `i
3. **Follow import order** - ESLint will enforce, but be proactive
4. **Use TypeScript strictly** - No `any` types, prefer `unknown`
5. **Test in Storybook** - Create stories for new components (filename pattern: `.stories.tsx`)
-6. **Consider i18n** - All user-facing text should be translatable. Server components: `getTranslations` and `getLocale` from `next-intl/server`. Client components: `useTranslations` from `next-intl`, one namespace-bound function per namespace - to access a second namespace, bind another function (e.g. `const tCommon = useTranslations("common")`) rather than reaching across namespaces. The legacy `@/hooks/useTranslation` wrapper (`namespace:key` syntax) is deprecated for new code
+6. **Consider i18n** - All user-facing text should be translatable. Server components: `getTranslations` and `getLocale` from `next-intl/server`. Client components: `useTranslations` from `next-intl`, one namespace-bound function per namespace - to access a second namespace, bind another function (e.g. `const tCommon = useTranslations("common")`) rather than reaching across namespaces
7. **Mobile-first** - Design for mobile, enhance for desktop
8. **Accessibility** - Use Radix primitives, semantic HTML
9. **Use locale-aware formatting wrappers** - Use `numberFormat()` from `src/lib/utils/numbers.ts` instead of `new Intl.NumberFormat()`, and `dateTimeFormat()` from `src/lib/utils/date.ts` instead of `new Intl.DateTimeFormat()` / `.toLocaleDateString()` / `.toLocaleTimeString()`. Both enforce correct numbering systems and calendar for Urdu and Arabic locales.
diff --git a/app/[locale]/roadmap/_components/ReleaseCarousel.tsx b/app/[locale]/roadmap/_components/ReleaseCarousel.tsx
index 18ec259a069..b9df1beb74c 100644
--- a/app/[locale]/roadmap/_components/ReleaseCarousel.tsx
+++ b/app/[locale]/roadmap/_components/ReleaseCarousel.tsx
@@ -1,7 +1,7 @@
"use client"
import { useCallback, useEffect, useMemo, useState } from "react"
-import { useLocale } from "next-intl"
+import { useLocale, useTranslations } from "next-intl"
import { Image } from "@/components/Image"
import { ButtonLink } from "@/components/ui/buttons/Button"
@@ -19,11 +19,9 @@ import { dateTimeFormat, formatDate } from "@/lib/utils/date"
import { getReleasesData, Release } from "@/data/roadmap/releases"
-import { useTranslation } from "@/hooks/useTranslation"
-
const ReleaseCarousel = () => {
const locale = useLocale()
- const { t } = useTranslation("page-roadmap")
+ const t = useTranslations("page-roadmap")
const releasesData = useMemo(() => getReleasesData(t), [t])
diff --git a/docs/best-practices.md b/docs/best-practices.md
index 047b035de40..3ff8dd6ac03 100644
--- a/docs/best-practices.md
+++ b/docs/best-practices.md
@@ -52,7 +52,7 @@ Markdown will be translated as whole pages of content, so no specific action is
- _tl;dr Each individual JSON entry should be a complete phrase by itself_
-- This is done using the `Translation` component. However, there is an alternative method for regular JS: using the `t` function from `@/hooks/useTranslation`
+- This is done using the `Translation` component. However, there is an alternative method for regular JS: using the `t` function from `next-intl`
- **Method one: `` component (preferred if only needed in JSX)**
@@ -66,14 +66,19 @@ Markdown will be translated as whole pages of content, so no specific action is
- **Method two: `t()`**
```tsx
- import { useTranslation } from "@/hooks/useTranslation"
+ // Client components
+ import { useTranslations } from "next-intl"
- // Utilize anywhere in JS using
- const { t } = useTranslation()
+ // Bind one function per namespace, then access keys within it
+ const t = useTranslations("common")
t("language-json-key")
```
```tsx
+ // Server components
+ import { getTranslations } from "next-intl/server"
+
+ const t = await getTranslations("common")
const siteTitle = t("site-title")
```
diff --git a/src/components/FindWalletProductTable/FindWalletLanguageSelectInput.tsx b/src/components/FindWalletProductTable/FindWalletLanguageSelectInput.tsx
index cb9378cf246..1eb862ae6b5 100644
--- a/src/components/FindWalletProductTable/FindWalletLanguageSelectInput.tsx
+++ b/src/components/FindWalletProductTable/FindWalletLanguageSelectInput.tsx
@@ -1,5 +1,5 @@
import { useEffect, useRef, useState } from "react"
-import { useLocale } from "next-intl"
+import { useLocale, useTranslations } from "next-intl"
import { FilterInputState, Lang } from "@/lib/types"
@@ -16,8 +16,6 @@ import { getLanguageCodeName } from "@/lib/utils/intl"
import { trackCustomEvent } from "@/lib/utils/matomo"
import { getLanguageCountWalletsData } from "@/lib/utils/wallets"
-import { useTranslation } from "@/hooks/useTranslation"
-
interface FindWalletLanguageSelectInputProps {
filterIndex: number
itemIndex: number
@@ -39,7 +37,7 @@ const FindWalletLanguageSelectInput = ({
const [searchQuery, setSearchQuery] = useState("")
const [isSelectOpen, setIsSelectOpen] = useState(false)
const searchInputRef = useRef(null)
- const { t } = useTranslation("page-wallets-find-wallet")
+ const t = useTranslations("page-wallets-find-wallet")
const languageCountWalletsData = getLanguageCountWalletsData(locale as string)
const countSortedLanguagesCount = [...languageCountWalletsData].sort(
(a, b) => b.count - a.count
diff --git a/src/components/FindWalletProductTable/FindWalletsNoResults.tsx b/src/components/FindWalletProductTable/FindWalletsNoResults.tsx
index 1296c1d6e57..65989678414 100644
--- a/src/components/FindWalletProductTable/FindWalletsNoResults.tsx
+++ b/src/components/FindWalletProductTable/FindWalletsNoResults.tsx
@@ -1,11 +1,11 @@
+import { useTranslations } from "next-intl"
+
import { trackCustomEvent } from "@/lib/utils/matomo"
import { Button } from "../ui/buttons/Button"
-import { useTranslation } from "@/hooks/useTranslation"
-
const FindWalletsNoResults = ({ resetFilters }) => {
- const { t } = useTranslation("page-wallets-find-wallet")
+ const t = useTranslations("page-wallets-find-wallet")
// Track empty state
trackCustomEvent({
diff --git a/src/components/FindWalletProductTable/PersonaTags.tsx b/src/components/FindWalletProductTable/PersonaTags.tsx
index 7619ac50eae..ef267535944 100644
--- a/src/components/FindWalletProductTable/PersonaTags.tsx
+++ b/src/components/FindWalletProductTable/PersonaTags.tsx
@@ -1,15 +1,14 @@
import { memo } from "react"
+import { useTranslations } from "next-intl"
import { Tag } from "../ui/tag"
-import { useTranslation } from "@/hooks/useTranslation"
-
type PersonaTagsProps = {
walletPersonas: string[]
}
const PersonaTags = ({ walletPersonas }: PersonaTagsProps) => {
- const { t } = useTranslation("page-wallets-find-wallet")
+ const t = useTranslations("page-wallets-find-wallet")
if (walletPersonas.length === 0) return null
diff --git a/src/components/FindWalletProductTable/WalletInfo.tsx b/src/components/FindWalletProductTable/WalletInfo.tsx
index a75973d8a36..ac9726b3a2c 100644
--- a/src/components/FindWalletProductTable/WalletInfo.tsx
+++ b/src/components/FindWalletProductTable/WalletInfo.tsx
@@ -1,5 +1,6 @@
import { memo, useMemo } from "react"
import { ChevronDown, ChevronUp } from "lucide-react"
+import { useTranslations } from "next-intl"
import type { ChainName, Wallet } from "@/lib/types"
@@ -17,14 +18,12 @@ import { TagsInlineText } from "../ui/tag"
import PersonaTags from "./PersonaTags"
-import { useTranslation } from "@/hooks/useTranslation"
-
interface WalletInfoProps {
wallet: Wallet
}
const WalletInfo = ({ wallet }: WalletInfoProps) => {
- const { t } = useTranslation("page-wallets-find-wallet")
+ const t = useTranslations("page-wallets-find-wallet")
const walletPersonas = useMemo(() => {
return getWalletPersonas(wallet)
diff --git a/src/components/FindWalletProductTable/WalletSubComponent.tsx b/src/components/FindWalletProductTable/WalletSubComponent.tsx
index 7c0be894f15..0a7c352dca0 100644
--- a/src/components/FindWalletProductTable/WalletSubComponent.tsx
+++ b/src/components/FindWalletProductTable/WalletSubComponent.tsx
@@ -1,5 +1,5 @@
import { Globe, Info } from "lucide-react"
-import { useLocale } from "next-intl"
+import { useLocale, useTranslations } from "next-intl"
import { FilterOption, Lang, WalletData } from "@/lib/types"
@@ -16,8 +16,6 @@ import InlineLink from "@/components/ui/Link"
import { cn } from "@/lib/utils/cn"
import { getLocaleFormattedDate } from "@/lib/utils/date"
-import { useTranslation } from "@/hooks/useTranslation"
-
const SocialLink = (props) => (
{
const locale = useLocale()
- const { t } = useTranslation("page-wallets-find-wallet")
+ const t = useTranslations("page-wallets-find-wallet")
const walletFiltersOptions: FilterOption[] = useWalletFilters()
const walletFilterDisplayOrder = [
diff --git a/src/components/FindWalletProductTable/hooks/useWalletFilters.tsx b/src/components/FindWalletProductTable/hooks/useWalletFilters.tsx
index ff9a9cf5c17..61d34506635 100644
--- a/src/components/FindWalletProductTable/hooks/useWalletFilters.tsx
+++ b/src/components/FindWalletProductTable/hooks/useWalletFilters.tsx
@@ -1,6 +1,6 @@
import { useRef } from "react"
import { Brain, LayersPlus } from "lucide-react"
-import { useLocale } from "next-intl"
+import { useLocale, useTranslations } from "next-intl"
import { FilterOption } from "@/lib/types"
@@ -15,11 +15,10 @@ import { trackCustomEvent } from "@/lib/utils/matomo"
import { DEFAULT_LOCALE } from "@/lib/constants"
-import { useTranslation } from "@/hooks/useTranslation"
-
export const useWalletFilters = (): FilterOption[] => {
const locale = useLocale()
- const { t } = useTranslation("page-wallets-find-wallet")
+ const t = useTranslations("page-wallets-find-wallet")
+ const tTable = useTranslations("table")
const prevNetworkArray = useRef([])
return [
{
@@ -31,7 +30,7 @@ export const useWalletFilters = (): FilterOption[] => {
filterLabel: t("page-find-wallet-mobile"),
description: "",
inputState: false,
- optionsLegend: t("table:table-mobile-platforms") as string,
+ optionsLegend: tTable("table-mobile-platforms") as string,
input: (filterIndex, itemIndex, inputState, updateFilterState) => {
return (
{
filterLabel: t("page-find-wallet-desktop"),
description: "",
inputState: false,
- optionsLegend: t("table:table-desktop-platforms") as string,
+ optionsLegend: tTable("table-desktop-platforms") as string,
input: (filterIndex, itemIndex, inputState, updateFilterState) => {
return (
{
filterLabel: t("page-find-wallet-browser"),
description: "",
inputState: false,
- optionsLegend: t("table:table-browser-engines") as string,
+ optionsLegend: tTable("table-browser-engines") as string,
input: (filterIndex, itemIndex, inputState, updateFilterState) => {
return (
{
- const { t } = useTranslation("page-wallets-find-wallet")
+ const t = useTranslations("page-wallets-find-wallet")
const personas: WalletPersonas[] = [
{
title: t("page-find-wallet-new-to-crypto-title"),
diff --git a/src/components/FindWalletProductTable/index.tsx b/src/components/FindWalletProductTable/index.tsx
index c32dfe2237a..f97bb776d44 100644
--- a/src/components/FindWalletProductTable/index.tsx
+++ b/src/components/FindWalletProductTable/index.tsx
@@ -1,4 +1,5 @@
"use client"
+import { useTranslations } from "next-intl"
import type { WalletRow } from "@/lib/types"
@@ -13,10 +14,8 @@ import FindWalletsNoResults from "./FindWalletsNoResults"
import List from "./List"
import WalletSubComponent from "./WalletSubComponent"
-import { useTranslation } from "@/hooks/useTranslation"
-
const FindWalletProductTable = ({ wallets }: { wallets: WalletRow[] }) => {
- const { t } = useTranslation("page-wallets-find-wallet")
+ const t = useTranslations("page-wallets-find-wallet")
const walletPersonas = useWalletPersonaPresets()
const walletFilterOptions = useWalletFilters()
diff --git a/src/hooks/useTranslation.ts b/src/hooks/useTranslation.ts
deleted file mode 100644
index 46a2ffbbb11..00000000000
--- a/src/hooks/useTranslation.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import { useTranslations } from "next-intl"
-
-/**
- * Cases to handle:
- *
- * - using t("key")
- * - & useTranslation() => "common.key"
- * - & useTranslation("namespace") => "namespace.key"
- * - & useTranslation(["namespace1", "namespace2"]) => "namespace1.key"
- *
- * - using t("namespace:key")
- * - & useTranslation("namespace") and t("namespace:key") => "namespace.key"
- * - & useTranslation(["namespace1", "namespace2"]) and t("namespace1:key") => "namespace1.key"
- * - & useTranslation(["namespace1", "namespace2"]) and t("namespace2:key") => "namespace2.key"
- */
-
-const DEFAULT_NAMESPACE = "common"
-
-/**
- * @deprecated Use namespace-bound `useTranslations` from "next-intl" directly — one binding per namespace (e.g. `const tCommon = useTranslations("common")` for a second namespace); use `t.raw`/`t.rich` for strings with embedded HTML. Migration: #18742. This file will be deleted once no consumers remain.
- */
-export function useTranslation(namespaces?: string[] | string) {
- const t = useTranslations()
-
- const customT = (
- fullKey: string,
- values?: Record
- ) => {
- try {
- if (fullKey.includes(":")) {
- const [namespace, key] = fullKey.split(":")
-
- if (values) {
- return t(`${namespace}.${key}`, values)
- }
-
- return t.raw(`${namespace}.${key}`)
- }
-
- const namespace = Array.isArray(namespaces)
- ? namespaces[0]
- : namespaces || DEFAULT_NAMESPACE
-
- return t.raw(`${namespace}.${fullKey}`)
- } catch (error) {
- // Suppress errors by default, enable if needed to debug
- // console.error(error)
- return fullKey
- }
- }
-
- // keep the original methods
- customT.raw = t.raw
- customT.rich = t.rich
- customT.markup = t.markup
- customT.has = t.has
-
- return { t: customT }
-}
-
-export default useTranslation