Skip to content

Commit

Permalink
feat: add support for arrays and pluralization to Trans component (#1179
Browse files Browse the repository at this point in the history
)
  • Loading branch information
acidfernando authored Jan 12, 2024
1 parent 88daa77 commit c215acf
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 9 deletions.
69 changes: 69 additions & 0 deletions __tests__/Trans.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,75 @@ describe('Trans', () => {
expect(container.textContent).toContain(expected)
})

test('should work with arrays', () => {
const i18nKey = 'ns:parent.child'
const expectedFirstElement = '<strong>First</strong> element 42'
const expectedSecondElement = '<strong>Second</strong> element 42'
const withSingular = {
parent: {
child: [
'<0>First</0> element {{num}}',
'<0>Second</0> element {{num}}',
],
},
}
const { container } = render(
<TestEnglish
returnObjects
namespaces={{ ns: withSingular }}
i18nKey={i18nKey}
values={{ num: 42 }}
components={[<strong />]}
/>
)
expect(container.innerHTML).toContain(expectedFirstElement)
expect(container.innerHTML).toContain(expectedSecondElement)
})

test('should work with arrays and singulars', () => {
const i18nKey = 'ns:withsingular'
const expected = '<strong>The number</strong> is one'
const withSingular = {
withsingular_0: ['<0>The number</0> is ZERO!'],
withsingular_one: ['<0>The number</0> is one'],
withsingular_other: ['<0>The number</0> is plural'],
}

const { container } = render(
<TestEnglish
returnObjects
namespaces={{ ns: withSingular }}
i18nKey={i18nKey}
values={{ count: 1 }}
components={[<strong />]}
/>
)

expect(container.innerHTML).toContain(expected)
})

test('should work with arrays and plurals', () => {
const i18nKey = 'ns:withsingular'
const expected = '<strong>The number</strong> is plural'
const withSingular = {
withsingular: ['<0>First</0> is not zero'],
withsingular_0: ['<0>The number</0> is ZERO!'],
withsingular_other: ['<0>The number</0> is plural'],
}

const { container } = render(
<TestEnglish
returnObjects
namespaces={{ ns: withSingular }}
i18nKey={i18nKey}
values={{ count: 2 }}
components={[<strong />]}
/>
)

expect(container.innerHTML).toContain(expected)
})

test('should work with nested keys and custom keySeparator', () => {
const i18nKey = 'ns:parent_child'
const expected = 'The number is 42'
Expand Down
13 changes: 11 additions & 2 deletions src/Trans.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,27 @@ export default function Trans({
fallback,
defaultTrans,
ns,
returnObjects,
}: TransProps): any {
const { t, lang } = useTranslation(ns)

/**
* Memoize the transformation
*/
const result = useMemo(() => {
const text = t<string>(i18nKey, values, { fallback, default: defaultTrans })
const text = t<string>(i18nKey, values, {
fallback,
default: defaultTrans,
returnObjects,
})

if (!text) return text

if (!components || components.length === 0) return text
if (!components || components.length === 0)
return Array.isArray(text) ? text.map((item) => item) : text

if (Array.isArray(text))
return text.map((item) => formatElements(item, components))

return formatElements(text, components)
}, [i18nKey, values, components, lang]) as string
Expand Down
1 change: 1 addition & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface TransProps {
fallback?: string | string[]
defaultTrans?: string
ns?: string
returnObjects?: boolean
}

export type PageValue = string[] | ((context: object) => string[])
Expand Down
30 changes: 23 additions & 7 deletions src/transCore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,14 @@ export default function transCore({
)

const dic = (namespace && allNamespaces[namespace]) || {}
const keyWithPlural = plural(pluralRules, dic, i18nKey, config, query)
const keyWithPlural = plural(
pluralRules,
dic,
i18nKey,
config,
query,
options
)
const dicValue = getDicValue(dic, keyWithPlural, config, options)
const value =
typeof dicValue === 'object'
Expand Down Expand Up @@ -157,11 +164,14 @@ function getDicValue(

if (
typeof value === 'string' ||
((value as unknown) instanceof Object && options.returnObjects)
((value as unknown) instanceof Object &&
options.returnObjects &&
Object.keys(value).length > 0)
) {
return value
}

if (Array.isArray(value) && options.returnObjects) return value
return undefined
}

Expand All @@ -173,23 +183,29 @@ function plural(
dic: I18nDictionary,
key: string,
config: I18nConfig,
query?: TranslationQuery | null
query?: TranslationQuery | null,
options?: {
returnObjects?: boolean
fallback?: string | string[]
}
): string {
if (!query || typeof query.count !== 'number') return key

const numKey = `${key}_${query.count}`
if (getDicValue(dic, numKey, config) !== undefined) return numKey
if (getDicValue(dic, numKey, config, options) !== undefined) return numKey

const pluralKey = `${key}_${pluralRules.select(query.count)}`
if (getDicValue(dic, pluralKey, config) !== undefined) {
if (getDicValue(dic, pluralKey, config, options) !== undefined) {
return pluralKey
}

const nestedNumKey = `${key}.${query.count}`
if (getDicValue(dic, nestedNumKey, config) !== undefined) return nestedNumKey
if (getDicValue(dic, nestedNumKey, config, options) !== undefined)
return nestedNumKey

const nestedKey = `${key}.${pluralRules.select(query.count)}`
if (getDicValue(dic, nestedKey, config) !== undefined) return nestedKey
if (getDicValue(dic, nestedKey, config, options) !== undefined)
return nestedKey

return key
}
Expand Down

0 comments on commit c215acf

Please sign in to comment.