@@ -10,6 +10,7 @@ import React, {
1010 useMemo ,
1111 useState ,
1212} from 'react'
13+ import ReactDOM from 'react-dom'
1314import 'intl-pluralrules'
1415
1516const LOCALE_ITEM_STORAGE = 'locale'
@@ -21,6 +22,27 @@ const prefixKeys = prefix => obj =>
2122 return acc
2223 } , { } )
2324
25+ const areNamespacesLoaded = ( namespaces , loadedNamespaces ) =>
26+ namespaces . every ( n => loadedNamespaces . includes ( n ) )
27+
28+ const getLocaleFallback = locale => locale . split ( '-' ) [ 0 ] . split ( '_' ) [ 0 ]
29+
30+ const getCurrentLocale = ( {
31+ defaultLocale,
32+ supportedLocales,
33+ localeItemStorage,
34+ } ) => {
35+ const languages = navigator . languages || [ navigator . language ]
36+ const browserLocales = [ ...new Set ( languages . map ( getLocaleFallback ) ) ]
37+ const localeStorage = localStorage . getItem ( localeItemStorage )
38+
39+ return (
40+ localeStorage ||
41+ browserLocales . find ( locale => supportedLocales . includes ( locale ) ) ||
42+ defaultLocale
43+ )
44+ }
45+
2446const I18nContext = createContext ( )
2547
2648export const useI18n = ( ) => {
@@ -37,16 +59,19 @@ export const useTranslation = (namespaces = [], load) => {
3759 if ( context === undefined ) {
3860 throw new Error ( 'useTranslation must be used within a I18nProvider' )
3961 }
40- const { loadTranslations } = context
62+ const { loadTranslations, namespaces : loadedNamespaces } = context
4163
4264 const key = namespaces . join ( ',' )
43- useEffect (
44- ( ) =>
45- key . split ( ',' ) . map ( async namespace => loadTranslations ( namespace , load ) ) ,
46- [ loadTranslations , key , load ] ,
65+ useEffect ( ( ) => {
66+ key . split ( ',' ) . map ( async namespace => loadTranslations ( namespace , load ) )
67+ } , [ loadTranslations , key , load ] )
68+
69+ const isLoaded = useMemo (
70+ ( ) => areNamespacesLoaded ( namespaces , loadedNamespaces ) ,
71+ [ loadedNamespaces , namespaces ] ,
4772 )
4873
49- return context
74+ return { ... context , isLoaded }
5075}
5176
5277// https://formatjs.io/docs/intl-messageformat/
@@ -66,27 +91,18 @@ const I18nContextProvider = ({
6691 localeItemStorage,
6792 supportedLocales,
6893} ) => {
69- const [ currentLocale , setCurrentLocale ] = useState ( defaultLocale )
70- const [ locales , setLocales ] = useState ( supportedLocales )
94+ const [ currentLocale , setCurrentLocale ] = useState (
95+ getCurrentLocale ( { defaultLocale, localeItemStorage, supportedLocales } ) ,
96+ )
7197 const [ translations , setTranslations ] = useState ( defaultTranslations )
98+ const [ namespaces , setNamespaces ] = useState ( [ ] )
7299 const [ dateFnsLocale , setDateFnsLocale ] = useState ( )
73100
74- const getLocaleFallback = useCallback (
75- locale => locale . split ( '-' ) [ 0 ] . split ( '_' ) [ 0 ] ,
76- [ ] ,
77- )
78-
79- const getCurrentLocale = useCallback ( ( ) => {
80- const languages = navigator . languages || [ navigator . language ]
81- const browserLocales = [ ...new Set ( languages . map ( getLocaleFallback ) ) ]
82- const localeStorage = localStorage . getItem ( localeItemStorage )
83-
84- return (
85- localeStorage ||
86- browserLocales . find ( locale => locales . includes ( locale ) ) ||
87- defaultLocale
88- )
89- } , [ defaultLocale , getLocaleFallback , locales , localeItemStorage ] )
101+ useEffect ( ( ) => {
102+ loadDateLocale ( currentLocale === 'en' ? 'en-GB' : currentLocale )
103+ . then ( setDateFnsLocale )
104+ . catch ( ( ) => loadDateLocale ( 'en-GB' ) . then ( setDateFnsLocale ) )
105+ } , [ loadDateLocale , currentLocale ] )
90106
91107 const loadTranslations = useCallback (
92108 async ( namespace , load = defaultLoad ) => {
@@ -114,15 +130,22 @@ const I18nContextProvider = ({
114130 const { prefix, ...values } = trad
115131 const preparedValues = prefix ? prefixKeys ( `${ prefix } .` ) ( values ) : values
116132
117- setTranslations ( prevState => ( {
118- ...prevState ,
119- ...{
120- [ currentLocale ] : {
121- ...prevState [ currentLocale ] ,
122- ...preparedValues ,
133+ // avoid a lot of render when async update
134+ ReactDOM . unstable_batchedUpdates ( ( ) => {
135+ setTranslations ( prevState => ( {
136+ ...prevState ,
137+ ...{
138+ [ currentLocale ] : {
139+ ...prevState [ currentLocale ] ,
140+ ...preparedValues ,
141+ } ,
123142 } ,
124- } ,
125- } ) )
143+ } ) )
144+
145+ setNamespaces ( prevState => [
146+ ...new Set ( [ ...( prevState || [ ] ) , namespace ] ) ,
147+ ] )
148+ } )
126149
127150 return namespace
128151 } ,
@@ -131,12 +154,12 @@ const I18nContextProvider = ({
131154
132155 const switchLocale = useCallback (
133156 locale => {
134- if ( locales . includes ( locale ) ) {
157+ if ( supportedLocales . includes ( locale ) ) {
135158 localStorage . setItem ( localeItemStorage , locale )
136159 setCurrentLocale ( locale )
137160 }
138161 } ,
139- [ setCurrentLocale , locales , localeItemStorage ] ,
162+ [ localeItemStorage , setCurrentLocale , supportedLocales ] ,
140163 )
141164
142165 const formatNumber = useCallback (
@@ -205,14 +228,6 @@ const I18nContextProvider = ({
205228 [ translate ] ,
206229 )
207230
208- useEffect ( ( ) => {
209- loadDateLocale ( currentLocale === 'en' ? 'en-GB' : currentLocale )
210- . then ( setDateFnsLocale )
211- . catch ( ( ) => loadDateLocale ( 'en-GB' ) . then ( setDateFnsLocale ) )
212-
213- setCurrentLocale ( getCurrentLocale ( ) )
214- } , [ loadDateLocale , currentLocale , getCurrentLocale ] )
215-
216231 const value = useMemo (
217232 ( ) => ( {
218233 currentLocale,
@@ -221,11 +236,11 @@ const I18nContextProvider = ({
221236 formatList,
222237 formatNumber,
223238 loadTranslations,
224- locales,
239+ locales : supportedLocales ,
225240 namespaceTranslation,
241+ namespaces,
226242 relativeTime,
227243 relativeTimeStrict,
228- setLocales,
229244 setTranslations,
230245 switchLocale,
231246 t : translate ,
@@ -235,15 +250,15 @@ const I18nContextProvider = ({
235250 currentLocale ,
236251 dateFnsLocale ,
237252 datetime ,
238- formatNumber ,
239253 formatList ,
254+ formatNumber ,
240255 loadTranslations ,
241- locales ,
242256 namespaceTranslation ,
257+ namespaces ,
243258 relativeTime ,
244259 relativeTimeStrict ,
245- setLocales ,
246260 setTranslations ,
261+ supportedLocales ,
247262 switchLocale ,
248263 translate ,
249264 translations ,
0 commit comments