sanity
Version:
Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches
70 lines (61 loc) • 2.55 kB
text/typescript
import {useMemo} from 'react'
import {useTranslation} from './useTranslation'
/**
* Enforces the shape of an object allowed to be passed into `useI18nText`.
* @internal
*/
export type I18nNode<TNode extends {i18n?: {[TProp in string]: {key: string; ns: string}}}> = {
i18n?: {[K in keyof TNode['i18n']]: {key: string; ns: string}}
} & {
[K in keyof TNode['i18n']]: string
}
/**
* A React hook for localizing strings in a given object (`node`) using the
* `useTranslation` hook.
*
* This hook expects an object (`node`) with top-level keys associated with
* string values. If an `i18n` property is present within the object, it should
* contain localization configurations for each top-level key. Each
* configuration must include a `key` and `ns` (namespace) used for the
* translation of the corresponding string value.
*
* The hook uses a proxy to intercept access to properties of the `node`. For
* properties defined in the `i18n` object, it returns the translated string
* using the `t` function. If no translation is found, the original string value
* is used as a fallback. For properties not defined in the `i18n` object, their
* original values are returned.
*
* @param node - The object containing strings to be localized along with
* optional i18n configurations.
* @returns A proxy of the original `node` object where each property access
* returns localized strings.
* @internal
*/
export function useI18nText<TNode extends I18nNode<TNode>>(node: TNode): TNode {
const namespaces = useMemo(() => {
if (!node.i18n) return []
return Array.from(new Set(Object.values(node.i18n).map(({ns}) => ns))).sort()
}, [node.i18n])
const {t} = useTranslation(namespaces)
return useMemo(() => {
const {i18n} = node
if (!i18n) return node
return new Proxy(node, {
get: (target, property) => {
const defaultValue = target[property as keyof TNode]
if (typeof property === 'string' && property in i18n) {
const {key, ns} = i18n[property as keyof TNode['i18n']]
return t(key, {
ns,
// `defaultValue` is a special key in the i18next `t` API that
// allows us to provide a fallback value if no translation is found
// using the given key and namespace. if the value on the node
// is a string, then we'll use that as the fallback value
...(typeof defaultValue === 'string' && {defaultValue}),
})
}
return defaultValue
},
})
}, [node, t])
}