Improve: i18n type gymnastics (#116)

Co-authored-by: Dreamacro <8615343+Dreamacro@users.noreply.github.com>
This commit is contained in:
橘年 2023-01-12 16:10:55 +08:00 committed by GitHub
parent a29c1121f1
commit c298eae5bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 24 additions and 4 deletions

View File

@ -1,3 +1,7 @@
import { IsEqual } from 'type-fest'
import Infer from '@lib/type'
import en_US from './en_US'
import zh_CN from './zh_CN'
@ -8,6 +12,16 @@ export const Language = {
export type Lang = keyof typeof Language
type US = typeof Language.en_US
type CN = typeof Language.zh_CN
// type guard for US and CN
type TrueGuard<T extends true> = T
// eslint-disable-next-line @typescript-eslint/no-unused-vars
type _equalGuard = TrueGuard<IsEqual<Infer<US>, Infer<CN>>>
export type LocalizedType = US
export const locales = Object.keys(Language)
export function getDefaultLanguage (): Lang {

5
src/lib/type.ts Normal file
View File

@ -0,0 +1,5 @@
type InferRecursion<K extends string, T> = T extends object ? { [P in keyof T]: P extends string ? InferRecursion<`${K}.${P}`, T[P]> : K }[keyof T] : K
type Infer<T> = Extract<{ [K in keyof T]: K extends string ? InferRecursion<K, T[K]> : unknown }[keyof T], string>
export default Infer

View File

@ -10,13 +10,14 @@ import { useCallback, useEffect, useMemo, useRef } from 'react'
import useSWR from 'swr'
import { Get } from 'type-fest'
import { Language, locales, Lang, getDefaultLanguage } from '@i18n'
import { Language, locales, Lang, getDefaultLanguage, LocalizedType } from '@i18n'
import { partition } from '@lib/helper'
import { useWarpImmerSetter, WritableDraft } from '@lib/jotai'
import { isClashX, jsBridge } from '@lib/jsBridge'
import { Snapshot } from '@lib/request'
import * as API from '@lib/request'
import { StreamReader } from '@lib/streamer'
import Infer from '@lib/type'
import * as Models from '@models'
import { Log } from '@models/Log'
@ -31,9 +32,9 @@ export function useI18n () {
const lang = useMemo(() => defaultLang ?? getDefaultLanguage(), [defaultLang])
const translation = useCallback(
function <Namespace extends keyof typeof Language['en_US']>(namespace: Namespace) {
function t<Path extends string> (path: Path) {
return get(Language[lang][namespace], path) as unknown as Get<typeof Language['en_US'][Namespace], Path>
function <Namespace extends keyof LocalizedType>(namespace: Namespace) {
function t<Path extends Infer<LocalizedType[Namespace]>> (path: Path) {
return get(Language[lang][namespace], path) as unknown as Get<LocalizedType[Namespace], Path>
}
return { t }
},