1
- import { Flags } from '@masknet/flags'
2
1
import list from './i18n-cache-query-list.js'
3
2
4
- export type Bundle = [ namespace : string , lang : string , json : object ]
3
+ export type Bundle = [ namespace : string , lang : string , json : Record < string , string > ]
5
4
export async function queryRemoteI18NBundle ( lang : string ) : Promise < Bundle [ ] > {
6
- if ( ! Flags . i18nTranslationHotUpdate ) return [ ]
7
5
// skip fetching in development. if you need to debug this, please comment this code.
8
6
if ( process . env . NODE_ENV === 'development' ) return [ ]
9
7
@@ -12,25 +10,47 @@ export async function queryRemoteI18NBundle(lang: string): Promise<Bundle[]> {
12
10
13
11
const responses = updateLang === 'en-US' ? fetchEnglishBundle ( ) : fetchTranslatedBundle ( lang )
14
12
const results = await Promise . allSettled ( responses )
15
- return results . filter ( ( x ) : x is PromiseFulfilledResult < Bundle > => x . status === 'fulfilled' ) . map ( ( x ) => x . value )
13
+ return results
14
+ . filter ( ( x ) : x is PromiseFulfilledResult < Bundle | null > => x . status === 'fulfilled' )
15
+ . map ( ( x ) => x . value ! )
16
+ . filter ( Boolean )
16
17
}
17
18
18
19
const I18N_LOCALES_HOST = 'https://maskbook.pages.dev/'
19
20
20
21
function fetchTranslatedBundle ( lang : string ) {
21
- return Object . entries ( list ) . map ( async ( [ url , namespace ] ) : Promise < Bundle > => {
22
- const path = url . replace ( '%locale%' , lang )
23
- const response = await fetch ( I18N_LOCALES_HOST + path , fetchOption )
24
- return [ namespace , lang , await response . json ( ) ]
22
+ return Object . entries ( list ) . map ( async ( [ url , namespace ] ) : Promise < Bundle | null > => {
23
+ try {
24
+ const path = url . replace ( '%locale%' , lang )
25
+ const response = await fetch ( I18N_LOCALES_HOST + path , fetchOption )
26
+ const json = await response . json ( )
27
+ if ( ! isValidTranslation ( json ) ) return null
28
+ return [ namespace , lang , json ]
29
+ } catch {
30
+ return null
31
+ }
25
32
} )
26
33
}
27
34
function fetchEnglishBundle ( ) {
28
- return Object . entries ( list ) . map ( async ( [ url , namespace ] ) : Promise < Bundle > => {
29
- const path = url . replace ( '%locale%' , 'en-US' )
30
- const response = await fetch ( I18N_LOCALES_HOST + path , fetchOption )
31
- return [ namespace , 'en-US' , await response . json ( ) ]
35
+ return Object . entries ( list ) . map ( async ( [ url , namespace ] ) : Promise < Bundle | null > => {
36
+ try {
37
+ const path = url . replace ( '%locale%' , 'en-US' )
38
+ const response = await fetch ( I18N_LOCALES_HOST + path , fetchOption )
39
+ const json = await response . json ( )
40
+ if ( ! isValidTranslation ( json ) ) return null
41
+ return [ namespace , 'en-US' , json ]
42
+ } catch {
43
+ return null
44
+ }
32
45
} )
33
46
}
47
+ function isValidTranslation ( obj : unknown ) : obj is Record < string , string > {
48
+ if ( typeof obj !== 'object' || obj === null ) return false
49
+ for ( const key in obj ) {
50
+ if ( typeof ( obj as any ) [ key ] !== 'string' ) return false
51
+ }
52
+ return true
53
+ }
34
54
35
55
const fetchOption = {
36
56
credentials : 'omit' ,
0 commit comments