diff --git a/__tests__/useTranslation.test.js b/__tests__/useTranslation.test.js index 5c6c05e1..e2466d5e 100644 --- a/__tests__/useTranslation.test.js +++ b/__tests__/useTranslation.test.js @@ -1170,6 +1170,58 @@ describe('useTranslation', () => { const expected = 'This is a default translation with a count: 3' + const { container } = render( + + + + ) + expect(container.textContent).toBe(expected) + }) + test('should allow default object translation with interpolation', () => { + const Inner = () => { + const { t } = useTranslation() + const text = t( + 'ns:no-translation', + { count: 3 }, + { + default: { + example: 'This is a default translation with a count: {{count}}' + }, + returnObjects: true, + fallback: 'ns:no-translation2', + } + ) + return <>{text.example} + } + + const expected = 'This is a default translation with a count: 3' + + const { container } = render( + + + + ) + expect(container.textContent).toBe(expected) + }) + test('should allow default array translation with interpolation', () => { + const Inner = () => { + const { t } = useTranslation() + const text = t( + 'ns:no-translation', + { count: 3 }, + { + default: [ + 'This is a default translation with a count: {{count}}' + ], + returnObjects: true, + fallback: 'ns:no-translation2', + } + ) + return <>{text[0]} + } + + const expected = 'This is a default translation with a count: 3' + const { container } = render( diff --git a/src/Trans.tsx b/src/Trans.tsx index 893c763f..21be3c50 100644 --- a/src/Trans.tsx +++ b/src/Trans.tsx @@ -23,7 +23,7 @@ export default function Trans({ * Memoize the transformation */ const result = useMemo(() => { - const text = t(i18nKey, values, { fallback, default: defaultTrans }) + const text = t(i18nKey, values, { fallback, default: defaultTrans }) if (!components || components.length === 0) return text diff --git a/src/index.tsx b/src/index.tsx index a8116b28..efcf0311 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -5,13 +5,22 @@ export interface TranslationQuery { [name: string]: any } -export type Translate = ( + +export interface NestedStringObject { + [key:string]: string | NestedStringObject | NestedStringArray +} +type ValueOrArray = T | ValueOrArray[]; +type NestedStringArray = ValueOrArray; + +export type TranslateValue = string | NestedStringObject | NestedStringArray + +export type Translate = ( i18nKey: string | TemplateStringsArray, query?: TranslationQuery | null, options?: { returnObjects?: boolean fallback?: string | string[] - default?: string + default?: T | string ns?: string } ) => T diff --git a/src/transCore.tsx b/src/transCore.tsx index 8ff3a308..0d7ac290 100644 --- a/src/transCore.tsx +++ b/src/transCore.tsx @@ -3,6 +3,7 @@ import { I18nDictionary, LoaderConfig, LoggerProps, + TranslateValue, TranslationQuery, } from '.' import { Translate } from './index' @@ -37,6 +38,21 @@ export default function transCore({ allowEmptyStrings = true, } = config + const interpolateUnknown = (value: TranslateValue, query?: TranslationQuery | null): TranslateValue => { + if (Array.isArray(value)) { + return value.map(val => interpolateUnknown(val, query)); + } + if (value instanceof Object) { + return objectInterpolation({ + obj: value as Record, + query, + config, + lang, + }) + } + return interpolation({ text: value as string, query, config, lang }) + } + const t: Translate = (key = '', query, options) => { const k = Array.isArray(key) ? key[0] : key const { nsSeparator = ':', loggerEnvironment = 'browser' } = config @@ -81,8 +97,8 @@ export default function transCore({ } } - if (empty && options?.default && fallbacks?.length == 0) { - return interpolation({ text: options?.default, query, config, lang }) + if (empty && options?.default && !fallbacks?.length) { + return interpolateUnknown(options.default, query) } // no need to try interpolation @@ -90,18 +106,9 @@ export default function transCore({ return k } - if (value instanceof Object) { - return objectInterpolation({ - obj: value as Record, - query, - config, - lang, - }) - } - // this can return an empty string if either value was already empty // or it contained only an interpolation (e.g. "{{name}}") and the query param was empty - return interpolation({ text: value as string, query, config, lang }) + return interpolateUnknown(value, query) } return t @@ -117,7 +124,7 @@ function getDicValue( options: { returnObjects?: boolean; fallback?: string | string[] } = { returnObjects: false, } -): string | undefined | object { +): TranslateValue | undefined { const { keySeparator = '.' } = config || {} const keyParts = keySeparator ? key.split(keySeparator) : [key] @@ -141,7 +148,7 @@ function getDicValue( typeof value === 'string' || ((value as unknown) instanceof Object && options.returnObjects) ) { - return value + return value as TranslateValue } return undefined @@ -203,8 +210,6 @@ function interpolation({ const regexEnd = suffix === '' ? '' : `(?:[\\s,]+([\\w-]*))?\\s*${escapeRegex(suffix)}` return Object.keys(query).reduce((all, varKey) => { - if (typeof all !== 'string') return all - const regex = new RegExp( `${escapeRegex(prefix)}\\s*${varKey}${regexEnd}`, 'gm' @@ -232,7 +237,6 @@ function objectInterpolation({ lang?: string }): any { if (!query || Object.keys(query).length === 0) return obj - Object.keys(obj).forEach((key) => { if (obj[key] instanceof Object) objectInterpolation({ diff --git a/yarn.lock b/yarn.lock index 015edc2b..4db9041a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5537,4 +5537,4 @@ yargs@^16.2.0: require-directory "^2.1.1" string-width "^4.2.0" y18n "^5.0.5" - yargs-parser "^20.2.2" + yargs-parser "^20.2.2" \ No newline at end of file