multiline text using vuejs i18n
Asked Answered
D

9

17

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 ?

Doud answered 8/12, 2019 at 10:32 Comment(4)
you can try v-html or v-text for this purposeGentes
Thanks, that's for the rendering, but how can I define multiple line text within the i18n tag. In case of 'text' variable would be multiline ?Doud
@Doud Did you find out something in the end?Unduly
I wrote the value in a single line, not realy easy, but didn't find anythinh else.Doud
Q
20

#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

Quincentenary answered 17/8, 2021 at 7:52 Comment(0)
S
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>
Swiercz answered 23/1, 2021 at 17:21 Comment(0)
U
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.

Udale answered 10/3, 2020 at 12:16 Comment(3)
Not working for me, I got an error unexpected token ' JSON at position xx. I'm using Nuxt, don't know if it's differentUnduly
I use this in nuxt too, so your error is probably related to something else.Udale
@BrunoMartins probably a bit late, but I think the issue there was using a normal single quote character ' as apposed to a backtick character `Phoney
F
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>
Foible answered 30/12, 2019 at 10:34 Comment(4)
thanks, this is what I did, but for large text writing on a single line is not realy practical.Doud
v-html can lead to XSS attacks! Don't use it unless you're sure there's no other way to solve it!Organism
Isn't v-html only a problem when you are inserting user content? How would it change if you are using your own text? vuejs.org/v2/guide/security.html#Injecting-HTMLViburnum
Don't use it, if you want to use it in this way you need to sanitize your HTML first!Quincentenary
S
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> [email protected] </template>
  </i18n-t>
Sellers answered 29/6, 2021 at 14:47 Comment(0)
B
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

Bureau answered 18/1, 2022 at 12:30 Comment(0)
P
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>
Pointer answered 18/2, 2021 at 8:38 Comment(0)
S
0

I am using Vue 2.x, and Vuetify

If you use v-card-text to render it, You can simply use a class: text-pre-wrap

  <v-card-text v-show="!!message" class="text-pre-wrap">
    {{ message }}
  </v-card-text>

Now this message can contain new line \n and it will split into new lines

Slover answered 29/2 at 7:21 Comment(0)
M
0

Question is asking to solve it from the json. to do it in this way is the v-html must be used in the .vue file, ex. paragraph.vue:

<div v-html="$t('translatable_text')" />

now the json language filesare able to use the key with html format, ex. en.json:

{
  "translatable_text": "<h1>Title<h1> <p>Some text <br> and breaklines <br> to make it view <br> like a paragraph.<p>"
}
Monstrance answered 23/7 at 20:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.