FormattedMessage component for vue-intl #3961
-
Are there any plans to add a |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 1 reply
-
Did anybody manage to use rich text formatting with Vue before? I tried to use the imperative intl APIs, but didn't succeed. Either it just outputs the html tags as strings, or ends up with a type error ( Here's the code: // App.vue
<script setup>
import { h } from 'vue';
const TranslateLink = (content) => h('a', { href: '#', target: '_blank', }, () => content );
</script>
<template>
<p>
{{ $t('app.richTextMessage', { a: (content) => `<a href="#">${content}</a>` } ) }}
</p>
<p>{{ $t('app.richTextMessage', { a: TranslateLink }) }}</p>
</template> { "app.richTextMessage": "Please accept <a>terms and conditions</a> first" } |
Beta Was this translation helpful? Give feedback.
-
I gave it a try and implemented my own // App.vue
<script setup>
import { h } from 'vue';
import { useIntl } from 'vue-intl';
const FormattedMessage = (props, context) => {
const { id } = props;
const intl = useIntl();
const slotNames = Object.keys(context.slots);
const message = intl.formatMessage(
{ id },
slotNames.reduce((values, name) => {
values[name] = (content) => {
return context.slots[name]({
content: content
.map((item) => {
return typeof item === 'string' ? item : undefined;
})
.filter(Boolean)
.join(''),
});
};
return values;
}, {})
);
return h('div', null, message);
};
</script>
<template>
<FormattedMessage id="app.richTextMessage">
<template #a="{ content }">
<a href="#">{{ content }}</a>
</template>
<template #b="{ content }">
<strong>{{ content }}</strong>
</template>
</FormattedMessage>
</template> {
"app.richTextMessage": "Please accept <a>terms <b>and</b> conditions</a> first"
} |
Beta Was this translation helpful? Give feedback.
-
So finally I got it working. I think this could be incorporated into the The trick was to use another component to output the content given to the scoped slots: <script setup>
import { h, ref } from "vue";
import { useIntl } from "vue-intl";
const FormattedMessage = (props, context) => {
const { id, tag = "div", values = {} } = props;
const intl = useIntl();
const slotNames = Object.keys(context.slots);
const message = intl.formatMessage(
{ id },
{
...values,
...slotNames.reduce((slots, name) => {
slots[name] = (content) => context.slots[name]({ modelValue: content });
return slots;
}, {}),
}
);
return h(tag, message);
};
const FormattedMessageContent = (props) => {
return props.modelValue || props["model-value"];
};
</script>
<template>
<FormattedMessage id="app.richTextMessage">
<template #a="{ modelValue }">
<a href="#"><FormattedMessageContent :model-value="modelValue" /></a>
</template>
<template #b="{ modelValue }">
<strong><FormattedMessageContent :model-value="modelValue" /></strong>
</template>
<template #br>
<br />
</template>
</FormattedMessage>
</template>
<style scoped></style> {
"app.richTextMessage": "Please accept <a>terms <b>and<br></br></b> conditions</a> first"
} As you can see in Vue one would use the scoped slots syntax to replace the tags in the translation files with actual markup. Here's the updated example: https://stackblitz.com/edit/vitejs-vite-xgm1xu?file=src/App.vue UPDATE: Added possibility to pass values to FormattedMessage as prop. |
Beta Was this translation helpful? Give feedback.
So finally I got it working. I think this could be incorporated into the
vue-intl
package. It allows to apply rich text formatting the same way as inreact-intl
(at least in translation files).The trick was to use another component to output the content given to the scoped slots: