@snack-uikit/locale
Advanced tools
Comparing version 0.3.0 to 0.3.1-preview-bd4095bc.0
@@ -1,3 +0,4 @@ | ||
import { LocaleDictionary, LocaleLang } from '../../types'; | ||
import { DottedTranslationKey, LocaleDictionary, LocaleLang } from '../../types'; | ||
type LocaleComponentName = keyof LocaleDictionary; | ||
type GetLocaleText<T extends keyof LocaleDictionary | undefined = undefined> = (key: DottedTranslationKey<T>) => string; | ||
/** | ||
@@ -7,4 +8,10 @@ * Inner hook to use translations | ||
*/ | ||
export declare function useLocale(): [LocaleDictionary, LocaleLang]; | ||
export declare function useLocale<C extends LocaleComponentName = LocaleComponentName>(componentName: C): [LocaleDictionary[C], LocaleLang]; | ||
export declare function useLocale(): { | ||
t: GetLocaleText; | ||
lang: LocaleLang; | ||
}; | ||
export declare function useLocale<C extends LocaleComponentName = LocaleComponentName>(componentName: C): { | ||
t: GetLocaleText<C>; | ||
lang: LocaleLang; | ||
}; | ||
export {}; |
@@ -1,17 +0,30 @@ | ||
import { useContext, useMemo } from 'react'; | ||
import { DEFAULT_LANG, LocaleContext } from './LocaleProvider'; | ||
import { useCallback, useContext, useMemo } from 'react'; | ||
import { LocaleContext } from './LocaleProvider'; | ||
export function useLocale(componentName) { | ||
const { locales: ctxLocales, lang } = useContext(LocaleContext); | ||
const { localesByLang, lang } = useContext(LocaleContext); | ||
const locales = useMemo(() => { | ||
let localesObj = ctxLocales[lang]; | ||
if (!localesObj) { | ||
console.warn(`Snack-uikit: localization for lang ${lang} was not found. Make sure you are using correct lang or passed proper locales to LocaleProvider. For now default language (${DEFAULT_LANG}) will be used`); | ||
localesObj = ctxLocales[DEFAULT_LANG]; | ||
} | ||
if (!componentName) { | ||
return localesObj; | ||
return localesByLang; | ||
} | ||
return (localesObj[componentName] || {}); | ||
}, [componentName, lang, ctxLocales]); | ||
return [locales, lang]; | ||
return (localesByLang[componentName] || {}); | ||
}, [componentName, localesByLang]); | ||
const getLocaleText = useCallback(key => { | ||
let translation = ''; | ||
const complexKey = key.split('.'); | ||
if (complexKey.length === 1) { | ||
translation = locales[key]; | ||
} | ||
else { | ||
translation = complexKey.reduce((acc, cur) => acc[cur], locales); | ||
} | ||
if (!(translation === null || translation === void 0 ? void 0 : translation.length)) { | ||
console.warn(`Snack-uikit: the '${key}' key is not found in the current locale '${lang}'.`); | ||
return key; | ||
} | ||
return translation; | ||
}, [lang, locales]); | ||
return { | ||
t: getLocaleText, | ||
lang, | ||
}; | ||
} |
import { ReactNode } from 'react'; | ||
import { LocaleLang, OverrideLocales } from '../../types'; | ||
import { LocaleDictionary, LocaleLang, OverrideLocales } from '../../types'; | ||
export declare const DEFAULT_LANG = "en_GB"; | ||
@@ -7,2 +7,3 @@ export type LocaleContextType = { | ||
locales: OverrideLocales; | ||
localesByLang: LocaleDictionary; | ||
}; | ||
@@ -9,0 +10,0 @@ export declare const LocaleContext: import("react").Context<LocaleContextType>; |
@@ -9,4 +9,5 @@ import { jsx as _jsx } from "react/jsx-runtime"; | ||
locales: LOCALES, | ||
localesByLang: LOCALES.en_GB, | ||
}); | ||
export function LocaleProvider({ lang: langProp = DEFAULT_LANG, locales: localesProp, children }) { | ||
export function LocaleProvider({ lang: langProp, locales: localesProp, children }) { | ||
const locales = useMemo(() => { | ||
@@ -19,3 +20,11 @@ if (localesProp) { | ||
const lang = useMemo(() => langProp.replace('-', '_'), [langProp]); | ||
return _jsx(LocaleContext.Provider, { value: { lang, locales }, children: children }); | ||
const localesByLang = useMemo(() => { | ||
let localesObj = locales[lang]; | ||
if (!localesObj) { | ||
console.warn(`Snack-uikit: localization for lang ${lang} was not found. Make sure you are using correct lang or passed proper locales to LocaleProvider. For now default language (${DEFAULT_LANG}) will be used`); | ||
localesObj = locales[DEFAULT_LANG]; | ||
} | ||
return localesObj; | ||
}, [lang, locales]); | ||
return _jsx(LocaleContext.Provider, { value: { lang, locales, localesByLang }, children: children }); | ||
} |
@@ -1,27 +0,2 @@ | ||
export declare const ru_RU: { | ||
Table: { | ||
searchPlaceholder: string; | ||
noData: { | ||
title: string; | ||
}; | ||
noResults: { | ||
title: string; | ||
description: string; | ||
}; | ||
errorData: { | ||
title: string; | ||
description: string; | ||
}; | ||
rowsOptionsLabel: string; | ||
export: string; | ||
}; | ||
Chips: { | ||
clearAllButton: string; | ||
}; | ||
Fields: { | ||
limitTooltip: { | ||
max: string; | ||
min: string; | ||
}; | ||
}; | ||
}; | ||
import { en_GB } from './en_GB'; | ||
export declare const ru_RU: typeof en_GB; |
import { LOCALES } from './locales'; | ||
import { PartialDeep } from './typeUtils'; | ||
import { Join, PartialDeep, PathsToStringProps } from './typeUtils'; | ||
export type KnownLocaleLang = keyof typeof LOCALES; | ||
export type LocaleLang = KnownLocaleLang | string; | ||
export type LocaleDictionary = typeof LOCALES.en_GB; | ||
export type OverrideLocales = PartialDeep<Record<KnownLocaleLang, LocaleDictionary>> & Record<string, LocaleDictionary>; | ||
export type OverrideLocales = PartialDeep<Record<LocaleLang, LocaleDictionary>>; | ||
export type DottedTranslationKey<C extends keyof LocaleDictionary | undefined = undefined> = C extends string ? Join<PathsToStringProps<LocaleDictionary[C]>, '.'> : Join<PathsToStringProps<LocaleDictionary>, '.'>; |
@@ -26,2 +26,6 @@ type Primitive = null | undefined | string | number | boolean | symbol | bigint; | ||
}; | ||
export type PathsToStringProps<T> = T extends string ? [] : { | ||
[K in Extract<keyof T, string>]: [K, ...PathsToStringProps<T[K]>]; | ||
}[Extract<keyof T, string>]; | ||
export type Join<T extends string[], D extends string> = T extends [] ? never : T extends [infer F] ? F : T extends [infer F, ...infer R] ? F extends string ? `${F}${D}${Join<Extract<R, string[]>, D>}` : never : string; | ||
export {}; |
@@ -7,3 +7,3 @@ { | ||
"title": "Locale", | ||
"version": "0.3.0", | ||
"version": "0.3.1-preview-bd4095bc.0", | ||
"sideEffects": [ | ||
@@ -41,3 +41,3 @@ "*.css", | ||
}, | ||
"gitHead": "3a1dd5498600db7f34a4681a70254b81337ddeb4" | ||
"gitHead": "53065276382788f1cae880a1303f6f5a47cdd750" | ||
} |
@@ -57,3 +57,3 @@ # Locale | ||
| lang* | `string` | - | | | ||
| locales | `OverrideLocales` | - | | | ||
| locales | `PartialObjectDeep<Record<string, { firstLevelTrans: string; Table: { searchPlaceholder: string; noData: { title: string; }; noResults: { title: string; description: string; }; errorData: { title: string; description: string; }; rowsOptionsLabel: string; export: string; }; Chips: { ...; }; Fields: { ...; }; }>>` | - | | | ||
## useLocale | ||
@@ -60,0 +60,0 @@ `helper` |
@@ -1,8 +0,12 @@ | ||
import { useContext, useMemo } from 'react'; | ||
import { useCallback, useContext, useMemo } from 'react'; | ||
import { KnownLocaleLang, LocaleDictionary, LocaleLang } from '../../types'; | ||
import { DEFAULT_LANG, LocaleContext, LocaleContextType } from './LocaleProvider'; | ||
import { DottedTranslationKey, LocaleDictionary, LocaleLang } from '../../types'; | ||
import { LocaleContext, LocaleContextType } from './LocaleProvider'; | ||
type ValueOf<T> = T[keyof T]; | ||
type LocaleComponentName = keyof LocaleDictionary; | ||
type GetLocaleText<T extends keyof LocaleDictionary | undefined = undefined> = (key: DottedTranslationKey<T>) => string; | ||
/** | ||
@@ -12,29 +16,48 @@ * Inner hook to use translations | ||
*/ | ||
export function useLocale(): [LocaleDictionary, LocaleLang]; | ||
export function useLocale(): { t: GetLocaleText; lang: LocaleLang }; | ||
export function useLocale<C extends LocaleComponentName = LocaleComponentName>( | ||
componentName: C, | ||
): [LocaleDictionary[C], LocaleLang]; | ||
): { t: GetLocaleText<C>; lang: LocaleLang }; | ||
export function useLocale<C extends LocaleComponentName = LocaleComponentName>(componentName?: C) { | ||
const { locales: ctxLocales, lang } = useContext<LocaleContextType>(LocaleContext); | ||
const { localesByLang, lang } = useContext<LocaleContextType>(LocaleContext); | ||
const locales = useMemo(() => { | ||
let localesObj = ctxLocales[lang as KnownLocaleLang]; | ||
if (!componentName) { | ||
return localesByLang; | ||
} | ||
if (!localesObj) { | ||
console.warn( | ||
`Snack-uikit: localization for lang ${lang} was not found. Make sure you are using correct lang or passed proper locales to LocaleProvider. For now default language (${DEFAULT_LANG}) will be used`, | ||
); | ||
return (localesByLang[componentName] || {}) as LocaleDictionary[C]; | ||
}, [componentName, localesByLang]); | ||
localesObj = ctxLocales[DEFAULT_LANG] as LocaleDictionary; | ||
} | ||
const getLocaleText: GetLocaleText<C> = useCallback( | ||
key => { | ||
let translation = ''; | ||
if (!componentName) { | ||
return localesObj as LocaleDictionary; | ||
} | ||
const complexKey = key.split('.'); | ||
return (localesObj[componentName] || {}) as LocaleDictionary[C]; | ||
}, [componentName, lang, ctxLocales]); | ||
if (complexKey.length === 1) { | ||
translation = locales[key as keyof typeof locales] as unknown as string; | ||
} else { | ||
translation = complexKey.reduce<LocaleDictionary | ValueOf<LocaleDictionary> | string>( | ||
(acc, cur) => acc[cur as keyof typeof acc], | ||
locales, | ||
) as string; | ||
} | ||
return [locales, lang]; | ||
if (!translation?.length) { | ||
console.warn(`Snack-uikit: the '${key}' key is not found in the current locale '${lang}'.`); | ||
return key; | ||
} | ||
return translation; | ||
}, | ||
[lang, locales], | ||
); | ||
return { | ||
t: getLocaleText, | ||
lang, | ||
}; | ||
} |
@@ -1,2 +0,4 @@ | ||
export const ru_RU = { | ||
import { en_GB } from './en_GB'; | ||
export const ru_RU: typeof en_GB = { | ||
Table: { | ||
@@ -3,0 +5,0 @@ searchPlaceholder: 'Поиск', |
import { LOCALES } from './locales'; | ||
import { PartialDeep } from './typeUtils'; | ||
import { Join, PartialDeep, PathsToStringProps } from './typeUtils'; | ||
@@ -10,2 +10,8 @@ export type KnownLocaleLang = keyof typeof LOCALES; | ||
export type OverrideLocales = PartialDeep<Record<KnownLocaleLang, LocaleDictionary>> & Record<string, LocaleDictionary>; | ||
// TODO: temporary changed type to fix typings mismatch for "locale" prop in LocaleProvider | ||
// export type OverrideLocales = PartialDeep<Record<KnownLocaleLang, LocaleDictionary>> | Record<string, LocaleDictionary>; | ||
export type OverrideLocales = PartialDeep<Record<LocaleLang, LocaleDictionary>>; | ||
export type DottedTranslationKey<C extends keyof LocaleDictionary | undefined = undefined> = C extends string | ||
? Join<PathsToStringProps<LocaleDictionary[C]>, '.'> | ||
: Join<PathsToStringProps<LocaleDictionary>, '.'>; |
@@ -49,1 +49,17 @@ type Primitive = null | undefined | string | number | boolean | symbol | bigint; | ||
}; | ||
export type PathsToStringProps<T> = T extends string | ||
? [] | ||
: { | ||
[K in Extract<keyof T, string>]: [K, ...PathsToStringProps<T[K]>]; | ||
}[Extract<keyof T, string>]; | ||
export type Join<T extends string[], D extends string> = T extends [] | ||
? never | ||
: T extends [infer F] | ||
? F | ||
: T extends [infer F, ...infer R] | ||
? F extends string | ||
? `${F}${D}${Join<Extract<R, string[]>, D>}` | ||
: never | ||
: string; |
Sorry, the diff of this file is not supported yet
34044
509