intlify / vue-i18n

Vue I18n for Vue 3
https://vue-i18n.intlify.dev/
MIT License
2.15k stars 327 forks source link

breaking change: Can't use nested arrays or arrays of objects needed for Page content or Language list #885

Closed tohagan closed 2 years ago

tohagan commented 2 years ago

Reporting a bug?

In vue-i18n v8.0.0, I was able use nested arrays of objects to manage translatable content for "About" or "FAQ" pages. Just tried using in vue-i18n: 9.2.0-beta.26 and found this very important feature appears to be missing. I realise this is known limitation documented in your Migration guide.

This is a bit of a show stopper for porting from v2 Vue. Without this feature I'll have to resort to some awful data transformations to get this feature back using i18n v9 or toss this library and find/build an alternative.

IMHO arrays and arrays of objects are essential for translatable content. I use them for things like the list of Languages to select from (so language names are translated). The same issue applies for any static selection list. Frequently these static lists contain multiple locale specific field values. For offline support, lazy loading and other performance reasons they belong in vue-i18n locale files but frustratingly this is now not supported by Vue 3.

Someone else mentioned this in #75 .

Examples:

...
<script lang="ts" setup>
  import { useI18n } from 'vue-i18n';
  const { t } = useI18n();

  interface Section {
    heading: string;
    body: string[]
  }

  const names = t('heroPage.names')   //  is undefined
  const content = t('heroPage.content')  as unknown as Section[];  // is undefined
</script>

i18n/en-US/index.ts ...

export default {
  heroPage: {
    title: Super Hero Powers",
    names: ["Clark Kent", "Peter Parker"],
    content: [
      {
        heading: "Superman can ...",
        body: [
         "Flight. 'You will believe a man can fly,' the 1978 movie promised, and they were right.",
         "Super Strength. Superman's other most famous power is that he's strong.",
         "Invulnerability.",
         "Super Speed.",
         "Super Hearing.",
         "Solar Flare.",
         "Turning Diamonds Into Coal.",
         "Super Disco Dancer!"
        ]
      },
      {
        heading: "Spiderman powers ...",
        body: [
          "Superhuman Strength.",
          "Superhuman Speed.",
          "Superhuman Reflexes.",
          "Superhuman Durability.",
          "Healing Factor.",
          "'Spider-Sense' Alert.",
          "Heightened Senses.",
          "Wallcrawling. "       
        ]
      }
    ]
  }
}

Expected behavior

(1) Return nested arrays or strings or objects and (2) Permit typecasting of translatable content:

const names = t<string[]>('heroPage.names') const content = t<Section[]>('heroPage.content') ;

Reproduction

I think the example above should be sufficient to reproduce it. Buzz me if you disagree.

System Info

I'm using YARN not NPM. 

  "dependencies": {
    "@quasar/extras": "^1.0.0",
    "axios": "^0.21.1",
    "core-js": "^3.6.5",
    "firebase": "^9.6.3",
    "firebaseui": "^0.600.0",
    "pinia": "^2.0.9",
    "quasar": "^2.0.0",
    "vue-i18n": "^9.2.0-beta.26"
  },

Screenshot

No response

Additional context

No response

Validations

kazupon commented 2 years ago

Thank you for your feedback.

docs said, the translation API like $t and t function (and, composition API too) that return string only. Object and array values are no longer returned. You can workaround with using tm or $tm Please check docs! https://vue-i18n.intlify.dev/guide/migration/breaking.html#translation-api-return-value

Thanks!

PeterAlfredLee commented 2 years ago

@tohagan here is a example:

<template>
  <p v-for="name in tm('heroPage.names')" :key="name">
    {{ rt(name) }}
  </p>
</template>

<script>
import { useI18n } from 'vue-i18n'

export default {
  setup() {
    const { tm, rt } = useI18n();
    return { rt, tm }
  }
}
</script>
tohagan commented 2 years ago

const { t, tm, rt } = useI18n();

Reports that tm is undefined for "vue-i18n": "^9.2.0-beta.26"

I'm guessing this is due to that fact that I'm not passing any arguments to useI18n. I'm lazy loading translations from locale files under src/i18n/<locale>/index.ts

PeterAlfredLee commented 2 years ago

@tohagan

Reports that tm is undefined for "vue-i18n": "^9.2.0-beta.26"

Unfortunately, I could not reproduce it. Could you give us the reproduction repo ?

tohagan commented 2 years ago

My apologies ... I misread the TS error ... I just needed to use ... // eslint-disable-next-line @typescript-eslint/unbound-method

I appreciate your help :)

tohagan commented 2 years ago
<template>
  <div v-for="(section, index) in tm('heroPage.content')" :key="index">
    <div class="row text-bold q-pt-xs info-heading">
      {{ rt(section.heading) }}
    </div>
    <div class="row">
      <ul>
        <li v-for="(item, index) in section.body" :key="index" v-html="rt(item)" />
      </ul>
    </div>
  </div>
 </template>

tm('heroPage.content') TS reports ...

rt(section.heading) and rt(section.body)TS reports

I think this is because we've not given it the actual type which this would do ...

I realise that you're heavily invested in the current approach that triggers the "possibly infinite" warning from TS that I suspect is the reason it's treating tm('heroPage.content') as type never.

tohagan commented 2 years ago

Ok I it now runs - just has TS Warnings about never type.

DarkLite1 commented 6 months ago

We're having the same issue. If I understood it all correctly we have to use $tm and $rt, which works but throws an TS error: image

Translation file

  "contacts": [
        { "title": "Health & safety" },
        { "title": "IT Support" }
      ],

image

Are we still doing something wrong here?