Last active
December 17, 2022 19:40
-
-
Save tomoasleep/5f4143edc7dcbe79abd5b7438a5e601a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Returns R if T is a function, otherwise returns Fallback | |
type IsFunction<T, R, Fallback = T> = T extends (...args: any[]) => any ? R : Fallback | |
// Returns R if T is an object, otherwise returns Fallback | |
type IsObject<T, R, Fallback = T> = IsFunction<T, Fallback, (T extends object ? R : Fallback)> | |
// "a.b.c" => "b.c" | |
type Tail<S> = S extends `${string}.${infer T}`? Tail<T> : S; | |
// typeof Object.values(T) | |
type Value<T> = T[keyof T] | |
// {a: {b: 1, c: 2}} => {"a.b": {b: 1, c: 2}, "a.c": {b: 1, c: 2}} | |
type FlattenStepOne<T> = { | |
[K in keyof T as K extends string ? (IsObject<T[K], `${K}.${keyof T[K] & string}`, K>) : K]: | |
IsObject<T[K], {[key in keyof T[K]]: T[K][key]}> | |
}; | |
// {"a.b": {b: 1, c: 2}, "a.c": {b: 1, c: 2}} => {"a.b": 1, "a.c": 2} | |
type FlattenStepTwo<T> = {[a in keyof T]: IsObject<T[a], Value<{[M in keyof T[a] as M extends Tail<a> ? M : never]: T[a][M] }>>} | |
// {a: {b: 1, c: {d: 1}}} => {"a.b": 1, "a.c": {d: 1}} | |
type FlattenOneLevel<T> = FlattenStepTwo<FlattenStepOne<T>> | |
// {a: {b: 1, c: {d: 1}}} => {"a.b": 1, "a.b.c.d": 1} | |
type Flatten<T> = T extends FlattenOneLevel<T> ? T: Flatten<FlattenOneLevel<T>> | |
const dictionaryJA = { | |
editor: { | |
placeholders: { | |
title: "タイトル", | |
tag: "タグを入力 (例: Ruby)" | |
}, | |
slideMode: "スライドモード", | |
submit: ({ group } : { group: string }) => `${group} に投稿`, | |
}, | |
} as const | |
const dictionaries = { ja: dictionaryJA } as const | |
type I18nParam<F> = F extends ((...args: any) => any) ? Parameters<F> : [] | |
type Dictionary = { [K in string]: Dictionary | string | ((...args: any) => string) } | |
function translate<D extends Dictionary, K extends keyof Flatten<D> & string, P extends I18nParam<Flatten<D>[K]>>(dictionaries: D, key: K, ...params: P): string { | |
const flattenedDictionary = flattenDictionaries(dictionaries) | |
const value = flattenedDictionary[key] | |
switch (typeof value) { | |
case 'function': | |
if (params.length >= value.length) { | |
return value.apply(null, params) | |
} else { | |
throw `parameter missing: ${key}` | |
} | |
case 'string': | |
return value | |
default: | |
throw `translation missing: ${key}` | |
} | |
} | |
// {a: {b: "text", c: {d: (key) => `${key} text` }}} => {"a.b": "text", "a.b.c.d": (key) => `${key} text` } | |
function flattenDictionaries<D extends Dictionary>(dictionaries: D): Flatten<D>{ | |
return Object.keys(dictionaries).reduce((dict: Dictionary, key) => { | |
const value = dictionaries[key] | |
if (typeof value === 'object') { | |
Object.entries(flattenDictionaries(value)).forEach(([k, v]) => { | |
dict[`${key}.${k}`] = v | |
}) | |
} else { | |
dict[key] = value | |
} | |
return dict | |
}, {}) as Flatten<D> | |
} | |
console.log(translate(dictionaries, "ja.editor.placeholders.tag")); | |
console.log(translate(dictionaries, "ja.editor.submit", { group: "Qiita" })); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment