diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useTranslation/info.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useTranslation/info.mdx
index 353578203b7..909a0a283df 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useTranslation/info.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/Form/useTranslation/info.mdx
@@ -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>
@@ -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',
},
}
@@ -84,6 +86,9 @@ const MyComponent = () => {
myKey: 'myValue',
})
+ // Render line-breaks
+ const jsxOutput = t.renderMessage(t.Nested.stringWithLinebreaks)
+
return <>MyComponent>
}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx
index bb73d810b72..7803658e379 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/extensions/forms/getting-started.mdx
@@ -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',
},
}
@@ -660,6 +661,9 @@ const MyComponent = () => {
myKey: 'myValue',
})
+ // Render line-breaks
+ const jsxOutput = t.renderMessage(t.Nested.stringWithLinebreaks)
+
return <>MyComponent>
}
diff --git a/packages/dnb-design-system-portal/src/docs/uilib/usage/customisation/localization.mdx b/packages/dnb-design-system-portal/src/docs/uilib/usage/customisation/localization.mdx
index 486d1676cab..0c52ef1e19c 100644
--- a/packages/dnb-design-system-portal/src/docs/uilib/usage/customisation/localization.mdx
+++ b/packages/dnb-design-system-portal/src/docs/uilib/usage/customisation/localization.mdx
@@ -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',
},
}
@@ -173,6 +174,9 @@ const MyComponent = () => {
myKey: 'myValue',
})
+ // Render line-breaks
+ const jsxOutput = t.renderMessage(t.Nested.stringWithLinebreaks)
+
return <>MyComponent>
}
diff --git a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useTranslation.test.tsx b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useTranslation.test.tsx
index 7ede2e76bfb..5f244349017 100644
--- a/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useTranslation.test.tsx
+++ b/packages/dnb-eufemia/src/extensions/forms/hooks/__tests__/useTranslation.test.tsx
@@ -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)
})
@@ -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)
@@ -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)
})
diff --git a/packages/dnb-eufemia/src/shared/__tests__/useTranslation.test.tsx b/packages/dnb-eufemia/src/shared/__tests__/useTranslation.test.tsx
index a38c062a59a..8baf23036a1 100644
--- a/packages/dnb-eufemia/src/shared/__tests__/useTranslation.test.tsx
+++ b/packages/dnb-eufemia/src/shared/__tests__/useTranslation.test.tsx
@@ -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),
})
)
})
@@ -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),
})
)
@@ -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),
})
)
})
@@ -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([
+
+ Hello
+
+ ,
+
+ World
+
+ ,
+ ])
+ })
+
+ it('should support multiple line-breaks', () => {
+ const { result } = renderHook(() => useTranslation())
+
+ expect(result.current.renderMessage('A{br}B{br}C')).toEqual([
+
+ A
+
+ ,
+
+ B
+
+ ,
+
+ C
+
+ ,
+ ])
+ })
+ })
})
diff --git a/packages/dnb-eufemia/src/shared/useTranslation.tsx b/packages/dnb-eufemia/src/shared/useTranslation.tsx
index e3004009b30..da505b6dc68 100644
--- a/packages/dnb-eufemia/src/shared/useTranslation.tsx
+++ b/packages/dnb-eufemia/src/shared/useTranslation.tsx
@@ -1,4 +1,4 @@
-import { useContext, useMemo } from 'react'
+import React, { Fragment, useContext, useMemo } from 'react'
import Context, {
Translation,
TranslationLocale,
@@ -45,6 +45,7 @@ export type CombineWithExternalTranslationsArgs = {
}
export type FormatMessage = {
formatMessage: typeof formatMessage
+ renderMessage: typeof renderMessage
}
export type CombineWithExternalTranslationsReturn = Translation &
TranslationCustomLocales &
@@ -78,6 +79,9 @@ export function combineWithExternalTranslations({
return formatMessage(id, args, combined)
}
+ // Support line-breaks
+ combined.renderMessage = renderMessage
+
return combined
}
@@ -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
+): string | React.ReactNode {
+ let element = text
+
+ if (typeof text === 'string') {
+ element = text.split('{br}')
+ }
+
+ if (Array.isArray(element)) {
+ return element.map((item, index) => (
+
+ {item}
+
+
+ ))
}
- return str
+ return text
}