Skip to content

Commit

Permalink
feat: add renderMessage to useTranslation hook for line-break suppo…
Browse files Browse the repository at this point in the history
…rt (#4125)
  • Loading branch information
tujoworker authored Oct 16, 2024
1 parent dada40a commit 5b07bec
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ render(
In addition to all internal translations, you also get;

- `formatMessage` - a function you can use to get a specific translation based on a key (flattened object with dot-notation).
- `renderMessage` - a function you can use to render a string with line-breaks. It converts `{br}` to a JSX line-break.

```tsx
import { Form } from '@dnb/eufemia/extensions/forms'

function MyComponent() {
const { formatMessage } = Form.useTranslation()
const { formatMessage, renderMessage } = Form.useTranslation()
const errorRequired = formatMessage('Field.errorRequired')

return <>MyComponent</>
Expand Down Expand Up @@ -62,7 +63,8 @@ const myTranslations = {
},

// Flat translations
'Nested.stringWithArgs': 'My custom string with an argument: {myKey}',
'Nested.stringWithLinebreaks':
'My custom string with a {br}line-break',
},
}

Expand All @@ -84,6 +86,9 @@ const MyComponent = () => {
myKey: 'myValue',
})

// Render line-breaks
const jsxOutput = t.renderMessage(t.Nested.stringWithLinebreaks)

return <>MyComponent</>
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,8 @@ const myTranslations = {
},

// Flat translations
'Nested.stringWithArgs': 'My custom string with an argument: {myKey}',
'Nested.stringWithLinebreaks':
'My custom string with a {br}line-break',
},
}

Expand All @@ -660,6 +661,9 @@ const MyComponent = () => {
myKey: 'myValue',
})

// Render line-breaks
const jsxOutput = t.renderMessage(t.Nested.stringWithLinebreaks)

return <>MyComponent</>
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ const myTranslations = {
},

// Flat translations
'Nested.stringWithArgs': 'My custom string with an argument: {myKey}',
'Nested.stringWithLinebreaks':
'My custom string with a {br}line-break',
},
}

Expand All @@ -173,6 +174,9 @@ const MyComponent = () => {
myKey: 'myValue',
})

// Render line-breaks
const jsxOutput = t.renderMessage(t.Nested.stringWithLinebreaks)

return <>MyComponent</>
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ describe('Form.useTranslation', () => {
const nb = {}
extendDeep(nb, forms_nbNO['nb-NO'], global_nbNO['nb-NO'])
nb['formatMessage'] = expect.any(Function)
nb['renderMessage'] = expect.any(Function)

expect(result.current).toEqual(nb)
})
Expand All @@ -38,6 +39,7 @@ describe('Form.useTranslation', () => {
const gb = {}
extendDeep(gb, forms_enGB['en-GB'], global_enGB['en-GB'])
gb['formatMessage'] = expect.any(Function)
gb['renderMessage'] = expect.any(Function)

expect(resultGB.current).toEqual(gb)

Expand All @@ -50,6 +52,7 @@ describe('Form.useTranslation', () => {
const nb = {}
extendDeep(nb, forms_nbNO['nb-NO'], global_nbNO['nb-NO'])
nb['formatMessage'] = expect.any(Function)
nb['renderMessage'] = expect.any(Function)

expect(resultNO.current).toEqual(nb)
})
Expand Down
39 changes: 39 additions & 0 deletions packages/dnb-eufemia/src/shared/__tests__/useTranslation.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ describe('useTranslation without an ID', () => {
expect(result.current).toEqual(
Object.assign({}, nbNO[defaultLocale], {
formatMessage: expect.any(Function),
renderMessage: expect.any(Function),
})
)
})
Expand All @@ -33,6 +34,7 @@ describe('useTranslation without an ID', () => {
expect(resultGB.current).toEqual(
Object.assign({}, enGB['en-GB'], {
formatMessage: expect.any(Function),
renderMessage: expect.any(Function),
})
)

Expand All @@ -45,6 +47,7 @@ describe('useTranslation without an ID', () => {
expect(resultNO.current).toEqual(
Object.assign({}, nbNO['nb-NO'], {
formatMessage: expect.any(Function),
renderMessage: expect.any(Function),
})
)
})
Expand Down Expand Up @@ -477,4 +480,40 @@ describe('useTranslation with an ID', () => {
)
})
})

describe('renderMessage', () => {
it('should render with JSX line-breaks', () => {
const { result } = renderHook(() => useTranslation())

expect(result.current.renderMessage('Hello{br}World')).toEqual([
<React.Fragment key="0">
Hello
<br />
</React.Fragment>,
<React.Fragment key="1">
World
<br />
</React.Fragment>,
])
})

it('should support multiple line-breaks', () => {
const { result } = renderHook(() => useTranslation())

expect(result.current.renderMessage('A{br}B{br}C')).toEqual([
<React.Fragment key="0">
A
<br />
</React.Fragment>,
<React.Fragment key="1">
B
<br />
</React.Fragment>,
<React.Fragment key="2">
C
<br />
</React.Fragment>,
])
})
})
})
35 changes: 32 additions & 3 deletions packages/dnb-eufemia/src/shared/useTranslation.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useContext, useMemo } from 'react'
import React, { Fragment, useContext, useMemo } from 'react'
import Context, {
Translation,
TranslationLocale,
Expand Down Expand Up @@ -45,6 +45,7 @@ export type CombineWithExternalTranslationsArgs = {
}
export type FormatMessage = {
formatMessage: typeof formatMessage
renderMessage: typeof renderMessage
}
export type CombineWithExternalTranslationsReturn = Translation &
TranslationCustomLocales &
Expand Down Expand Up @@ -78,6 +79,9 @@ export function combineWithExternalTranslations({
return formatMessage(id, args, combined)
}

// Support line-breaks
combined.renderMessage = renderMessage

return combined
}

Expand Down Expand Up @@ -114,11 +118,36 @@ export function formatMessage(
str = id(messages)
}

if (str) {
if (typeof str === 'string') {
for (const t in args) {
str = str.replace(new RegExp(`{${t}}`, 'g'), args[t])
}

if (str.includes('{br}')) {
return renderMessage(str)
}
}

return str ?? id
}

export function renderMessage(
text: string | Array<React.ReactNode>
): string | React.ReactNode {
let element = text

if (typeof text === 'string') {
element = text.split('{br}')
}

if (Array.isArray(element)) {
return element.map((item, index) => (
<Fragment key={index}>
{item}
<br />
</Fragment>
))
}

return str
return text
}

0 comments on commit 5b07bec

Please sign in to comment.