Skip to content

Commit 28733c4

Browse files
committed
fix: type error when interpolation keys differ between languages
1 parent b349da3 commit 28733c4

File tree

1 file changed

+37
-26
lines changed

1 file changed

+37
-26
lines changed

src/i18n/i18n.ts

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,47 +14,58 @@ const updater = mitt()
1414

1515
export type Language = (typeof languages)[number]
1616

17-
export type I18NTranslations = ParseObject<
17+
export type I18NTranslations = MakeTranslations<
1818
| typeof import('./generated/cn').default
1919
| typeof import('./generated/en').default
2020
> & { essentials: I18NEssentials }
2121

22-
type I18NEssentials = ParseObject<(typeof ESSENTIALS)[Language]>
22+
type I18NEssentials = MakeTranslations<(typeof ESSENTIALS)[Language]>
2323

24-
type ParseObject<T> = T extends string
24+
type MakeTranslations<T> = MakeEndpoints<ParseValue<T>>
25+
26+
type ParseValue<T> = T extends string
2527
? ParseMessage<T, []>
2628
: T extends PluralObject
2729
? ParseMessage<T['other'], ['count']>
28-
: { [K in keyof T]: ParseObject<T[K]> }
29-
30-
type PluralObject = Record<`${number}` | 'other', string>
30+
: { [P in keyof T]: ParseValue<T[P]> }
3131

32-
type ParseMessage<T extends string, InitialKeys extends unknown[]> =
33-
InterpolationKeys<T, InitialKeys> extends infer Keys
34-
? Keys extends []
35-
? string
36-
: Keys extends unknown[]
37-
? Endpoint<Keys>
38-
: never
39-
: never
40-
41-
type Endpoint<Keys extends unknown[]> = Keys extends IndexKey[]
42-
? <T extends { [K in keyof Keys]?: ReactNode }>(
43-
...args: T
44-
) => T[number] extends string | number ? string : ReactElement
45-
: <T extends { [K in Extract<Keys[number], string>]?: ReactNode }>(
46-
...args: [T]
47-
) => ValueOf<T> extends string | number ? string : ReactElement
32+
type ParseMessage<
33+
T extends string,
34+
InitialKeys extends unknown[],
35+
Keys = InterpolationKeys<T, InitialKeys>,
36+
> = Keys extends [] ? string : Keys
4837

4938
type InterpolationKeys<
5039
Str,
5140
Keys extends unknown[],
5241
> = Str extends `${string}{{${infer Key}}}${infer End}`
53-
? InterpolationKeys<End, [...Keys, Key extends '' ? IndexKey : Key]>
42+
? InterpolationKeys<End, [...Keys, Key extends '' ? UnnamedKey : Key]>
5443
: Keys
5544

56-
declare const indexKey: unique symbol
57-
type IndexKey = typeof indexKey
45+
type PluralObject = Record<`${number}` | 'other', string>
46+
47+
type MakeEndpoints<T, K extends keyof T = keyof T> = string extends T
48+
? T
49+
: [T] extends [unknown[]]
50+
? Endpoint<T>
51+
: { [P in K]: MakeEndpoints<T[P]> }
52+
53+
type Endpoint<Keys extends unknown[]> = Keys[number] extends UnnamedKey
54+
? UnnamedInterpolation<{ [K in keyof Keys]: ReactNode }>
55+
: Interpolation<{ [K in Extract<Keys[number], string>]: ReactNode }>
56+
57+
type Interpolation<Arg> = <T extends Arg>(
58+
...args: [T]
59+
) => InterpolationResult<ValueOf<T>>
60+
61+
type UnnamedInterpolation<Arg extends unknown[]> = <T extends Arg>(
62+
...args: T
63+
) => InterpolationResult<T[number]>
64+
65+
type InterpolationResult<T> = T extends string | number ? string : ReactElement
66+
67+
declare const unnamedKey: unique symbol
68+
type UnnamedKey = typeof unnamedKey
5869

5970
export const allEssentials = Object.fromEntries(
6071
Object.entries(ESSENTIALS).map(([language, data]) => [
@@ -81,7 +92,7 @@ export const i18n = new Proxy({} as I18NTranslations, {
8192
// before the translations are loaded, in which case you should change it to i18nDefer.*
8293
throw new Error(allEssentials[currentLanguage].translations_not_loaded)
8394
}
84-
return currentTranslations[prop as keyof I18NTranslations] || prop
95+
return currentTranslations[prop] || prop
8596
},
8697
})
8798

0 commit comments

Comments
 (0)