question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Formatting is broken when switching language

See original GitHub issue

vue & vue-i18n version

Vue version: 2.6.11 Vue-i18n version: 8.18.2

Reproduction Link

https://codesandbox.io/s/elastic-shockley-wjw0z

Steps to reproduce

When using a formatter like the one provided by the official documentation, and switching locale, the formatter is not aware of this change.

(INFO: I tried this version of the formatter but it seems broken : TypeError: fn(...).map is not a function)

So I added this method to the formatter:

// Formatter.js
changeLocale(locale) {
  this._locale = locale;
  this._formatter = new MessageFormat(locale);
}

Then I can use this method when switching locale:

// App.vue
switchLocale(locale) {
  this.$i18n.locale = locale;
  this.$i18n.formatter.changeLocale(locale);
},

I don’t know if it’s the proper way to do this, but it solves partially my problem. Maybe there is another way to achieve this?

BUT, there’s still a bug, formatted messages doesn’t update when the translation string is exactly the same in both languages! Example:

const translations = {
  fr: {
    test: {
      number: {
        first: "Mon nombre est {myNumber, number, integer}",
        second: "{myNumber, number, integer}" // exact same string in 'en' and 'fr'
      },
      date: {
        first: "Ma date est {myDate, date, medium}",
        second: "{myDate, date, medium}" // exact same string in 'en' and 'fr'
      }
    }
  },
  en: {
    test: {
      number: {
        first: "My number is {myNumber, number, integer}",
        second: "{myNumber, number, integer}" // exact same string in 'en' and 'fr'
      },
      date: {
        first: "My date is {myDate, date, medium}",
        second: "{myDate, date, medium}" // exact same string in 'en' and 'fr'
      }
    }
  }
};

What is Expected?

When I use translation strings in a template and I switch locale, I expect the strings to be formatted in the chosen locale.

Number first

  • template: {{ $t('test.number.first', { myNumber: 2440 }) }}
  • expected output:
    • en: My number is 2,440 ✔️
    • fr: Mon nombre est 2 440 ✔️

Number second

  • template: {{ $t('test.number.second', { myNumber: 2440 }) }}
  • expected output:
    • en: 2,440 ✔️
    • fr: 2 440

Date first

  • template: {{ $t('test.date.first', { myDate: '2020-08-28 13:47:38+02:00' }) }}
  • expected output:
    • en: My date is Aug 28, 2020 ✔️
    • fr: Ma date est 28 août 2020 ✔️

Date second

  • template: {{ $t('test.date.second', { myDate: '2020-08-28 13:47:38+02:00' }) }}
  • expected output:
    • en: Aug 28, 2020 ✔️
    • fr: 28 août 2020

What is actually happening?

Number second

  • actual output:
    • fr: 2,440

Date second

  • actual output:
    • fr: Aug 28, 2020

As I said before, the concerned translation strings are those that are exactly the same in ‘en’ and ‘fr’, so I guess it could be a cache issue?

Any idea how to solve this?

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:1
  • Comments:11

github_iconTop GitHub Comments

1reaction
Azjargal13commented, Sep 5, 2020

@kazupon @Pochwar

The current issue is solved by doing below⬇️.

Question 1: injecting method into formatter

@Pochwar with help of changeLocale(locale) method that you’ve added we can fix format by adding space in the messages field.

like this: image

Previously it was written like

fr: {
        test: {
            number: {
                first: "Mon nombre est {myNumber, number, integer}",
                second: "{myNumber, number, integer}"
            },
            date: {
                first: "Ma date est {myDate, date, medium}",
                second: "{myDate, date, medium}"
            }
        }
    },

Please check this in your env and let me know.

Question 2

About setting up the formatter config correctly, it looks that current formatter config could not know when the locale has changed/updated from vue component. Once the Formatter class constructor is set its locale as default value, in this case it is ‘en’, after language switch btn pressed it could not update locale accordingly.

However this.$i18n.formatter.changeLocale(locale) is called when button pressed and switchLocale() is called, in this case formatter can get updated.

So I am looking into config now. Once I get the idea, will update you shortly.

0reactions
PetrCimalacommented, Oct 8, 2021

Hi @Pochwar and @Azjargal13,

I guess I found this issue too late. Maybe you have figure out the solution yet. Anyway, just to be sure, here it is.

You were right about the cache. It is a problem. You have to realize that messages serve as keys in the instantiated cache object. On the contrary, values represent MessageFormat configs. So, when you interpolate the key “second” for French, this key already exists. In this case, the key for English and French is the same, so the code has no reason to update the MessageFormat config because of this check.

let fn = this.caches[message];
if (!fn) {
  // code ...
}

If we know this, the solution is pretty straightforward. We need to define a cache, which is capable to hold separate locale objects. Then we need to refactor the code to work with these locale objects. Moreover, I decided to cache localised instances of MessageFormat. Below, you have the result.

// Formatter.js
import MessageFormat from 'messageformat';

export default class MessageFormatFormatter {
  constructor(options = {}) {
    this._locale = options.locale;
    this._cacheFormatters = Object.create(null);
    if (!this._cacheFormatters[this._locale]) {
      this._cacheFormatters[this._locale] = new MessageFormat(this._locale);
    }
    this._cacheMessages = Object.create(null);
  }

  interpolate(message, values) {
    if (!this._cacheMessages[this._locale]) {
      this._cacheMessages[this._locale] = Object.create(null);
    }
    // console.log('­LOCALE CACHE [Messages]: ', this._cacheMessages); // For testing purposes
    // console.log('­LOCALE CACHE [Formatters]: ', this._cacheFormatters); // For testing purposes

    let fn = this._cacheMessages[this._locale][message];
    if (!fn) {
      fn = this._cacheFormatters[this._locale].compile(message, this._locale);
      this._cacheMessages[this._locale][message] = fn;
    }
    return [fn(values)];
  }

  changeLocale(locale) {
    this._locale = locale;
    if (!this._cacheFormatters[this._locale]) {
      this._cacheFormatters[this._locale] = new MessageFormat(this._locale);
    }
  }
}

I tested the code out, and it works as you need.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Excel formatting and features that are not transferred to other ...
Column widths and most number formats are saved, but all other formats are lost. Page setup settings and manual page breaks are lost....
Read more >
Adding translation to language breaks formatting and widgets
When we go into any of the languages (see language translation.png) on any page/post/template and make a change to a language we are...
Read more >
Format a document for another language in Pages on iPad
Change a document's language and formatting​​ in the toolbar, tap Document Options, then tap Language & Region. Tap Language and choose a new...
Read more >
Code formatting | CLion Documentation - JetBrains
Do not keep line breaks: reformat line breaks according to the code style settings. This option overrides the Keep when reformatting | Line...
Read more >
How to change the language of a single document in Excel?
If the spreadsheet is created by someone in a foreign language and I just want to open (maybe adjust) and print it, the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found