Polyconseil / vue-gettext

Translate your Vue.js applications with gettext.
MIT License
278 stars 55 forks source link

How to run gettext function in custom js module? #76

Open pantuchy opened 6 years ago

pantuchy commented 6 years ago

Hello. Please advise, how to run translation functions like (gettext, ngettext and etc. ) in custom js file? For example Vue-Gettext is installed and initiated in main.js file, everything is working fine, but if I want to use gettext functions in router.js file, functions are not working, I have tried:

Vue.$gettext('Some text') this.$gettext('Some text') Vue.prototype.$gettext('Some text')

Nothing works. In Router file I am setting titles for each page, and I wish, that they will be available for translating. Please help. Thank you.

p4t5h3 commented 6 years ago

Have you tried router.app.$gettext('Some text')? The app property on the router instance is the Vue instance on which the global gettext methods are available.

sandroden commented 5 years ago

This is interesting but I have a similar need for vuex and for a pure js module (messages used by vuelidate-error-extractor). Do you have a solution for these situations?

escapedcat commented 5 years ago

We're doing this:
gettext.js:

import Vue from 'vue'

export const interpolate = Vue.prototype.$gettextInterpolate
export default Vue.prototype.$gettext

And in a js-file:

import gettext from '@helper/gettext'

export default {
  mainNav: {
    animals: {
      itemText: gettext('AnimalsText'),
      routeName: 'Animals',
    },
   ...

Does this help?

sandroden commented 5 years ago

I tried it, I created gettext.js and then I imported it in the store and I get the error: cv.js?c148:113 Uncaught TypeError: Object(...) is not a function.

escapedcat commented 5 years ago

Did you add the plugin to your Vue instance? The idea assumes that you integrated vue-gettext into your Vue-project already. If yes we need to check more detailed. Maybe you can create a test-repo and share it.

sandroden commented 5 years ago

Yes, I integrated vue-gettext with following conf:


Vue.use(GetTextPlugin, {
  translations: translations,
  availableLanguages: {
    en_GB: 'British English',
    it_IT: 'Italiano',
    pl: 'Polski',
    pt: 'Português',
    pt_BR: 'Português brasileiro',
  },
  defaultLanguage: navigator.language.replace('-', '_'),
  silent: false,
}

And is working perfectly. Honestly -due to my poor knowledge of js, my background is Python...- I'm, uncertain of what happens when you write Vue.use(...): any instance of Vue will get that plugin from Vue.prototype? that means that if it works I can get also $language in the same way? Could you even use Vue.prototype.$gettext directly? Anyhow I'll come back later with a complete codesandbox example.

sandroden commented 5 years ago

I prepared this codesandbox that shows the error I'm getting. In main.js I tried to change the import order so that plugin is loaded before App is read, but the error doesn't change.

Any hint is appreciated.

kemar commented 5 years ago

@sandroden as its name implies, this plugin is thightly coupled to a Vue.js application. All components of an app are sharing the same Vue instance (in your codesandbox it's the one in main.js).

When you're importing Vue in your gettext.js file, you're creating another instance of Vue that is not aware of the plugin without $gettext in its prototype. That's why your codesandbox shows an error.

As suggested by others, you can find some ways to share the Vue instance and somewhat use the plugin in custom js modules, but you will loose the reactivity that is provided by the component and the directive.

All use cases supported by the plugin are covered in the documentation.

sandroden commented 5 years ago

My codesandbox sample was in fact the attempt to implement @escapedcat 's suggestion. Reactivity may not be an issue in mutations and actions at least. I just realized that store has an instance of vue as store._vm, that can be used at least to translate messages from within an action. The case that is left unresolved to me is a message list used in a separate file. As an example I use vuelidate-error-extractor to display validation error messages.

PetrovskYYY commented 4 years ago

There is another easy working way to do this: you can push this.$gettext object into a vuex store as a state variable and then use it there.

duduklein commented 4 years ago

Any solution to this? how could we use gettext in a completely separate js file?

pantuchy commented 4 years ago

Any solution to this? how could we use gettext in a completely separate js file?

I recommend to use i18n for Vue projects. It also has Nuxt Js module

duduklein commented 4 years ago

Well, I'm actually switching from i18n. gettext is the standard way of handling translations in linux and has automatic extraction of string. I don't really understand why the vue community opted for a different approach.

Besides lacking the automatic extraction of string, i18n is more verbose, since we have to write message.key everywhere and it makes more difficult to non-programmers translators to translate the files. AFAIK, there is no software like poedit that allows a non technical person to translate the strings.

brunobg commented 4 years ago

This might be helpful to people here: https://github.com/Polyconseil/vue-gettext/issues/51#issuecomment-581567628

imrim12 commented 1 year ago

There's another way of doing this by using a "lazy-string" technique JavaScript will try to convert objects into string before rendering them to the UI (You can often see something like [Object object]) Taking advantage of that, we can make a custom object, and then override the toString() method with the gettext function, so it only runs when rendered

class LazyString {
  constructor(msgid, locale) {
    this.toString = translate.gettext.bind(translate, msgid, locale)
  }
}

const gettextLazy = (msgid, locale) => new LazyString(msgid, locale)

In any files, you can just use it

const YOUR_CONST = {
  HELLO: gettextLazy("Hello World!")
}

And that should work with your components

<template>
  <div>{{ YOUR_CONST.HELLO }}</div>
</template>

This should also work for SSR too, if you use Nuxt, please make sure the language is set properly before the page rendered, there're hooks that run after plugin initialization and before rendering, you should set your language in those hooks from your cookies, user's settings or whatever, good luck