15

I'm using i18n single file component to have translation support on my application. To do so, I'm using the tag as following

<i18n>
{
  "fr": {
    "text": "blabla in french 
             blabla
             bla"
  },
  "en": {
    "text": "blabla in english
             bla"
  }
}
</i18n>

But I have multiple lines text with html formating, how can I use language handling for long html text ?

Freestyle09
  • 4,894
  • 8
  • 52
  • 83
user1595929
  • 1,284
  • 3
  • 13
  • 23

7 Answers7

15

#1. You can use backticks:

i18n file:

{
  text: `Content
  With some 
  Break lines`
}

#2. You can use combination of js and css

{
  text: 'Content \n With some \n Break lines'
}

css:

.class {
   white-space: pre-line
}

#3. You can use HTML and v-html (but becareful because without sanitizing your HTML you lead to XSS attacks!)

{
  text: 'Content <br /> With some <br /> Break lines'
}

template:

<div v-html="yourI18nRule" />

Learn more about sanitizing HTML here:

https://www.npmjs.com/package/sanitize-html

Freestyle09
  • 4,894
  • 8
  • 52
  • 83
11

Found a pretty cool solution here. It is possible to achieve this with Interpolation. In this example, the {0} placeholder will be replaced with what you put into the <i18n> tag.

en.json

{
  "footer": "Built with Vue and Vue I18n{0}Powered by an excessive amount of coffee"
}

Footer.vue

<template>
  <i18n path="footer" tag="p" class="footer">
    <br />
  </i18n>
</template>
Luc
  • 365
  • 4
  • 22
7

You could always use backticks:

<i18n>
{
  "fr": {
    "text": `blabla in french 
             blabla
             bla`
  },
  "en": {
    "text": `blabla in english
             bla`
  }
}
</i18n>

You will get some (harmless) warning about something concerning POJO strings though.

Daniel Schreij
  • 773
  • 1
  • 10
  • 26
  • Not working for me, I got an error `unexpected token ' JSON at position xx`. I'm using Nuxt, don't know if it's different – Bruno Martins Jul 01 '20 at 13:11
  • I use this in nuxt too, so your error is probably related to something else. – Daniel Schreij Oct 13 '20 at 09:02
  • @BrunoMartins probably a bit late, but I think the issue there was using a normal single quote character `'` as apposed to a backtick character `\`` – thorne51 Apr 14 '21 at 08:41
4

Try implementing the below lines of code, as it worked for me:

<i18n>
{
    "fr": {
        "text": "blabla in french <br /> blabla <br /> bla"
    },
    "en": {
        "text": "blabla in english <br /> bla"
    }
}
</i18n>



<span v-html="$t('text')"></span>
Dhara Parmar
  • 296
  • 1
  • 8
  • thanks, this is what I did, but for large text writing on a single line is not realy practical. – user1595929 Jan 13 '20 at 18:22
  • 2
    `v-html` can lead to XSS attacks! Don't use it unless you're sure there's no other way to solve it! – Fusseldieb Jul 24 '20 at 12:37
  • Isn't v-html only a problem when you are inserting user content? How would it change if you are using your own text? https://vuejs.org/v2/guide/security.html#Injecting-HTML – Brettins Jul 07 '21 at 21:41
  • Don't use it, if you want to use it in this way you need to sanitize your HTML first! – Freestyle09 Aug 17 '21 at 07:46
2

You can set-up a placeholder and use it more than once in your translation. In this exmmple I'm using {br}, and I also have a placeholder for an email address.

 Body:
    "Email was sent to {EmailAddress}{br}{br}Please enter the validation code below.{br}{br}If you have not received this email, please check your spam folder.",

And then in the vue component I put this

  <i18n-t tag="h3" keypath="Body">
    <template v-slot:br><br /></template>
    <template v-slot:EmailAddress> yo@mama.com </template>
  </i18n-t>
GeekyMonkey
  • 12,478
  • 6
  • 33
  • 39
1

None of the "common" solutions to the problem work well for me:

  • The css-based solution (white-space: pre-line) requires adding the style to each translated element where a line break is needed. It clutters up the code and personally I'd like to have a clear separation between the text and the styles.
  • As mentioned above, the v-html-based solution might be dangerous, especially if you have multiple people working on the locale messages.
  • I use global .json files for storing the locale messages and .yaml format is not an option for me.

That being said, I don't think there is a perfect solution to the problem and maybe this is where the vue-i18n can be improved. But for my use case I found a pretty good solution: I created a custom wrapper around component, that allows me to:

  1. move the text into the new line by adding a {br} placeholder (inspired by @GeekyMonkey)
  2. create styled paragraphs from the list of locale messages by passing the 'paragraph-class' prop to the custom wrapper. It's important for me since I don't just want to move the text to the new line, sometimes I want the paragraphs to have the same customizable distance between them

I18nCustom.vue:

<template>
    <div>
        <i18n
            v-bind="$attrs"
            v-on="$listeners"
            :class="paragraphClass"
            v-for="idx in (Array.isArray(paragraphs) ? paragraphs : [paragraphs]).length"
            :key="`${path}.${idx - 1}`"
            :path="Array.isArray(paragraphs) ? `${path}.${idx - 1}` : path"
        >
            <template v-for="(_, name) in $slots" v-slot:[name]>
                <slot :name="name"/>
            </template>
            <template #br>
                <br>
            </template>
        </i18n>
    </div>
</template>

<script>
export default {
    data: () => ({
        paragraphs: null
    }),
    props: {
        path: {
            type: String,
            required: true
        },
        paragraphClass: {
            type: String,
            default: ''
        },
    },
    mounted() {
        this.paragraphs = this.$t(this.path)
    }
};
</script>

How to use it: You can use the wrapper in the same way you would you the element: all the props and slots are supported

en.json

{
    "paragraphsNewLine": "line1{br}line2{br}line3{slot-example}{br}",
    "slotExample": "slot",
    "styledParagraphs": [
        "paragraph1",
        "paragraph2",
        "paragraph3"
    ],
}

In the components:

<!-- new line example -->
new line example:
<i18n-custom
    tag="p"
    path="paragraphsNewLine"
>
    <template #slot-example>
        <b>
            {{ $t('slotExample') }}
        </b>
    </template>
</i18n-custom>

<!-- styled paragraphs example -->
styled paragraphs example:
<i18n-custom
    tag="p"
    path="styledParagraphs"
    paragraph-class="mb-3"
/>

The results: the results

Milena
  • 11
  • 2
0

Have your tried yaml format? You only have to install 'yaml-loader' and modify your vue.config.js file as described in the documentation. Then, you can do something like that:

<i18n>
  fr:
    text: |
      blabla in french
      blabla
      bla
  en:
    text: |
      blabla in english
      bla
</i18n>
Dharman
  • 30,962
  • 25
  • 85
  • 135