Skip to content

Commit

Permalink
Add options object for t function and returnObjects option (#158)
Browse files Browse the repository at this point in the history
* Add options object for t function and returnObjects option for array/object locales

* Added simple interpolation for objects
  • Loading branch information
bickmaev5 authored May 23, 2020
1 parent 18f7d29 commit 0072c13
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 4 deletions.
118 changes: 118 additions & 0 deletions __tests__/useTranslation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -338,4 +338,122 @@ describe('useTranslation', () => {
expect(container.textContent).toContain(expected)
})
})

describe('options', () => {
test('should work with returnObjects option and Array locale', () => {
const Inner = () => {
const { t } = useTranslation()
const items = t('ns:template-array', {}, { returnObjects: true })
return <>{items.map((i) => `${i.title} `)}</>
}

const expected = 'Title 1 Title 2 Title 3'
const templateString = {
'template-array': [
{ title: 'Title 1' },
{ title: 'Title 2' },
{ title: 'Title 3' },
],
}

const { container } = render(
<I18nProvider lang="en" namespaces={{ ns: templateString }}>
<Inner />
</I18nProvider>
)
expect(container.textContent).toContain(expected)
})

test('should work with returnObjects option and Object locale', () => {
const Inner = () => {
const { t } = useTranslation()
const { title, description } = t(
'ns:template-object',
{},
{ returnObjects: true }
)
return <>{`${title} ${description}`}</>
}

const expected = 'Title 1 Description 1'
const templateString = {
'template-object': {
title: 'Title 1',
description: 'Description 1',
},
}

const { container } = render(
<I18nProvider lang="en" namespaces={{ ns: templateString }}>
<Inner />
</I18nProvider>
)
expect(container.textContent).toContain(expected)
})
test('should work with returnObjects option and Object locale with interpolation', () => {
const Inner = () => {
const { t } = useTranslation()
const { title, description } = t(
'ns:template-object-interpolation',
{ count: 2, something: 'of title' },
{ returnObjects: true }
)
return <>{`${title} ${description}`}</>
}

const expected = 'Title 2 Description of title'
const templateString = {
'template-object-interpolation': {
title: 'Title {{count}}',
description: 'Description {{something}}',
},
}

const { container } = render(
<I18nProvider lang="en" namespaces={{ ns: templateString }}>
<Inner />
</I18nProvider>
)
expect(container.textContent).toContain(expected)
})
test('should work with returnObjects option and Object locale with interpolation and highly nested object', () => {
const Inner = () => {
const { t } = useTranslation()
const {
title,
description,
template: {
parent: { child },
},
} = t(
'ns:template-object-interpolation',
{ count: 2, something: 'of title', childTitle: '4' },
{ returnObjects: true }
)
return <>{`${title} ${description} ${child.title}`}</>
}

const expected = 'Title 2 Description of title Child title 4'
const templateString = {
'template-object-interpolation': {
title: 'Title {{count}}',
description: 'Description {{something}}',
template: {
parent: {
child: {
title: 'Child title {{childTitle}}',
},
},
},
},
}

const { container } = render(
<I18nProvider lang="en" namespaces={{ ns: templateString }}>
<Inner />
</I18nProvider>
)
expect(container.textContent).toContain(expected)
})
})
})
27 changes: 23 additions & 4 deletions src/I18nProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ const NsContext = createContext({})
/**
* Get value from key (allow nested keys as parent.children)
*/
function getDicValue(dic, key = '') {
function getDicValue(dic, key = '', options = { returnObjects: false }) {
const value = key.split('.').reduce((val, key) => val[key] || {}, dic)
return typeof value === 'string' ? value : undefined

if (
typeof value === 'string' ||
(value instanceof Object && options.returnObjects)
) {
return value
}
}

/**
Expand Down Expand Up @@ -41,6 +47,15 @@ function interpolation(text, query) {
}, text)
}

function objectInterpolation(obj, query) {
if (!query || Object.keys(query).length === 0) return obj
Object.keys(obj).forEach((key) => {
if (obj[key] instanceof Object) objectInterpolation(obj[key], query)
if (typeof obj[key] === 'string') obj[key] = interpolation(obj[key], query)
})
return obj
}

export default function I18nProvider({
lang,
namespaces = {},
Expand All @@ -52,12 +67,16 @@ export default function I18nProvider({

setInternals({ ...internals, lang })

function t(key = '', query) {
function t(key = '', query, options) {
const k = Array.isArray(key) ? key[0] : key
const [namespace, i18nKey] = k.split(':')
const dic = allNamespaces[namespace] || {}
const keyWithPlural = plural(dic, i18nKey, query)
const value = getDicValue(dic, keyWithPlural)
const value = getDicValue(dic, keyWithPlural, options)

if (value instanceof Object) {
return objectInterpolation(value, query)
}

return interpolation(value, query) || k
}
Expand Down

0 comments on commit 0072c13

Please sign in to comment.