-
-
Notifications
You must be signed in to change notification settings - Fork 764
/
Copy pathappWithTranslation.tsx
130 lines (109 loc) · 3.89 KB
/
appWithTranslation.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import React, { useMemo, useRef } from 'react'
import hoistNonReactStatics from 'hoist-non-react-statics'
import { I18nextProvider } from 'react-i18next'
import type { AppProps as NextJsAppProps } from 'next/app'
import { createConfig } from './config/createConfig'
import createClient from './createClient'
import { SSRConfig, UserConfig } from './types'
import { i18n as I18NextClient, Resource } from 'i18next'
import { useIsomorphicLayoutEffect } from './utils'
export {
Trans,
useTranslation,
withTranslation,
} from 'react-i18next'
export let globalI18n: I18NextClient | null = null
const addResourcesToI18next = (instance: I18NextClient, resources: Resource) => {
if (resources && instance.isInitialized) {
for (const locale of Object.keys(resources)) {
for (const ns of Object.keys(resources[locale])) {
if (!instance?.store?.data || !instance.store.data[locale] || !instance.store.data[locale][ns]) {
instance.addResourceBundle(
locale,
ns,
resources[locale][ns],
true,
true
)
}
}
}
}
}
export const appWithTranslation = <Props extends NextJsAppProps>(
WrappedComponent: React.ComponentType<Props>,
configOverride: UserConfig | null = null
) => {
const AppWithTranslation = (
props: Props & { pageProps: Props['pageProps'] & SSRConfig }
) => {
const { _nextI18Next } = props.pageProps || {} // pageProps may be undefined on strange setups, i.e. https://github.com/i18next/next-i18next/issues/2109
let locale: string | undefined =
_nextI18Next?.initialLocale ?? props?.router?.locale
const ns = _nextI18Next?.ns
const instanceRef = useRef<I18NextClient | null>(null)
/**
* Memoize i18n instance and reuse it rather than creating new instance.
* When the locale or resources are changed after instance was created,
* we will update the instance by calling addResourceBundle method on it.
*/
const i18n: I18NextClient | null = useMemo(() => {
if (!_nextI18Next && !configOverride) return null
const userConfig = configOverride ?? _nextI18Next?.userConfig
if (!userConfig) {
throw new Error(
'appWithTranslation was called without a next-i18next config'
)
}
if (!userConfig?.i18n) {
throw new Error(
'appWithTranslation was called without config.i18n'
)
}
if (!userConfig?.i18n?.defaultLocale) {
throw new Error(
'config.i18n does not include a defaultLocale property'
)
}
const { initialI18nStore } = _nextI18Next || {}
const resources = configOverride?.resources
? configOverride.resources
: initialI18nStore
if (!locale) locale = userConfig.i18n.defaultLocale
let instance = instanceRef.current
if (instance) {
addResourcesToI18next(instance, resources)
} else {
instance = createClient({
...createConfig({
...userConfig,
lng: locale,
}),
lng: locale,
...(ns && { ns }),
resources,
}).i18n
addResourcesToI18next(instance, resources)
globalI18n = instance
instanceRef.current = instance
}
return instance
}, [_nextI18Next, locale, ns])
/**
* Since calling changeLanguage method on existing i18n instance cause state update in react,
* we need to call the method in `useLayoutEffect` to prevent state update in render phase.
*/
useIsomorphicLayoutEffect(() => {
if (!i18n || !locale) return
i18n.changeLanguage(locale)
}, [i18n, locale])
return i18n !== null ? (
<I18nextProvider i18n={i18n}>
<WrappedComponent {...props} />
</I18nextProvider>
) : (
<WrappedComponent key={locale} {...props} />
)
}
return hoistNonReactStatics(AppWithTranslation, WrappedComponent)
}