Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for arrays and pluralization to Trans component #1179

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading