Skip to content

Commit b176281

Browse files
committed
create custom abstractions for t, Translation, and load namespaces
1 parent abdc67f commit b176281

File tree

4 files changed

+85
-19
lines changed

4 files changed

+85
-19
lines changed

src/components/Translation.tsx

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,22 @@
11
import htmr, { type HtmrOptions } from "htmr"
2-
import type { TOptions } from "i18next"
3-
import { useRouter } from "next/router"
4-
import { useTranslation } from "next-i18next"
5-
6-
import { getRequiredNamespacesForPage } from "@/lib/utils/translations"
2+
import type { TranslationValues } from "next-intl"
73

84
import TooltipLink from "./TooltipLink"
95

6+
import useTranslation from "@/hooks/useTranslation"
7+
108
type TranslationProps = {
119
id: string
12-
options?: TOptions
10+
ns?: string
11+
values?: TranslationValues
1312
transform?: HtmrOptions["transform"]
1413
}
1514

1615
// Renders the translation string for the given translation key `id`. It
1716
// fallback to English if it doesn't find the given key in the current language
18-
const Translation = ({ id, options, transform = {} }: TranslationProps) => {
19-
const { asPath } = useRouter()
20-
const requiredNamespaces = getRequiredNamespacesForPage(asPath)
21-
22-
const { t } = useTranslation(requiredNamespaces)
23-
const translatedText = t(id, options)
17+
const Translation = ({ id, ns, values, transform = {} }: TranslationProps) => {
18+
const { t } = useTranslation(ns)
19+
const translatedText = t(id, values)
2420

2521
// Custom components mapping to be used by `htmr` when parsing the translation
2622
// text

src/hooks/useTranslation.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { useTranslations } from "next-intl"
2+
3+
/**
4+
* Cases to handle:
5+
*
6+
* - using t("key")
7+
* - & useTranslation() => "common.key"
8+
* - & useTranslation("namespace") => "namespace.key"
9+
* - & useTranslation(["namespace1", "namespace2"]) => "namespace1.key"
10+
*
11+
* - using t("namespace:key")
12+
* - & useTranslation("namespace") and t("namespace:key") => "namespace.key"
13+
* - & useTranslation(["namespace1", "namespace2"]) and t("namespace1:key") => "namespace1.key"
14+
* - & useTranslation(["namespace1", "namespace2"]) and t("namespace2:key") => "namespace2.key"
15+
*/
16+
17+
const DEFAULT_NAMESPACE = "common"
18+
19+
export default function useTranslation(namespaces?: string[] | string) {
20+
const t = useTranslations()
21+
22+
const customT: typeof t = (fullKey, values) => {
23+
if (fullKey.includes(":")) {
24+
const [namespace, key] = fullKey.split(":")
25+
26+
if (values) {
27+
return t(`${namespace}.${key}`, values)
28+
}
29+
30+
return t.raw(`${namespace}.${key}`)
31+
}
32+
33+
const namespace = Array.isArray(namespaces)
34+
? namespaces[0]
35+
: namespaces || DEFAULT_NAMESPACE
36+
37+
return t.raw(`${namespace}.${fullKey}`)
38+
}
39+
40+
// keep the original methods
41+
customT.raw = t.raw
42+
customT.rich = t.rich
43+
customT.markup = t.markup
44+
customT.has = t.has
45+
46+
return { t: customT }
47+
}

src/i18n/loadNamespaces.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export default async function loadNamespaces(
2+
locale: string,
3+
namespaces: string[]
4+
) {
5+
const byNamespace = await Promise.all(
6+
namespaces.map(async (namespace) => {
7+
return (await import(`../intl/${locale}/${namespace}.json`)).default
8+
})
9+
)
10+
11+
return byNamespace.reduce((acc, namespace, index) => {
12+
acc[namespaces[index]] = namespace
13+
return acc
14+
}, {})
15+
}

src/i18n/request.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
1-
import createMiddleware from "next-intl/middleware"
1+
import { getRequestConfig } from "next-intl/server"
22

3-
import { LOCALES_CODES } from "@/lib/constants"
3+
import { Lang } from "@/lib/types"
44

55
import { routing } from "./routing"
66

7-
export default createMiddleware(routing)
7+
export default getRequestConfig(async ({ requestLocale }) => {
8+
// This typically corresponds to the `[locale]` segment
9+
let locale = await requestLocale
810

9-
export const config = {
10-
// Match only internationalized pathnames
11-
matcher: ["/", `/${LOCALES_CODES.join("|")}/:path*`],
12-
}
11+
// Ensure that the incoming locale is valid
12+
if (!locale || !routing.locales.includes(locale as Lang)) {
13+
locale = routing.defaultLocale
14+
}
15+
16+
return {
17+
locale,
18+
messages: (await import(`../../intl/${locale}/common.json`)).default,
19+
}
20+
})

0 commit comments

Comments
 (0)