Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cant set a language per request with server side rendering (SSR). #51

Open
trainiac opened this issue Sep 27, 2017 · 15 comments
Open

Cant set a language per request with server side rendering (SSR). #51

trainiac opened this issue Sep 27, 2017 · 15 comments

Comments

@trainiac
Copy link
Contributor

trainiac commented Sep 27, 2017

Hello again! And thank you for your work. I've began porting our application to SSR and am finding that vue-gettext uses a global setting for all vue instances for the current language (i.e. Vue.config.language). This is a problem for server side rendering because a different language per request may be required. One request could set the global language value which would clobber the language context for other request.

There is a nuxt.js example of how they recommend to handle i18n https://nuxtjs.org/guide/plugins/#inject-in-root-amp-context. Notice how the plugin is being intergrated. They first call a Vue.use to wire up the properties and methods that a component will need and then per request setup the language on the app root component. Then the language can be accessed in components via this.$root.language.

This I think would actually simplify the codebase quite a bit as well as you wont have to instantiate a languageMixinVm. You just explain in the "How to Use" that in the the root component they need to define a "language" property (by default "language" would be the property but should be customizable). It could be computed or just plain data.

@kemar I'd love to hear your thoughts on this.

@kemar
Copy link
Contributor

kemar commented Sep 28, 2017

Hello @trainiac :)

To put things in context, vue-gettext was developped before SSR support in Vue.js.

Vue.config.language is heavily used in unit tests and can also be used in custom filters where you can't access any vm instance. That may be a little bit of a challenge to refactor.

As much as your suggestion sounds great, I do not have the time to work on it now, nor in the coming months.

But feel free to submit a PR!

@trainiac
Copy link
Contributor Author

trainiac commented Sep 28, 2017

Totally understand. I'll fork for now and show you what I ended up doing.

Do you really need to support a v-translate directive :). Is it purely for stylistic purposes, i.e. can the translate component do everything the directive can? If so, there could be some simplicity in only offering translate component (smaller lib size, simpler docs, supports SSR :P). However, if you really did need to support v-translate maybe you could use a function in the plugin setup?

// directive.js

export default lang => ({
  bind () {
    // access to lang
  },
  update () {
    // access to lang
  }
})

Anyways, I'll keep you posted!

@kemar
Copy link
Contributor

kemar commented Sep 28, 2017

Do you really need to support a v-translate directive

Yes! Just read the doc, the directive adds support for HTML content in translations.

If you plan to submit a PR, you have to support all available features and make sure npm test passes :)

@trainiac
Copy link
Contributor Author

trainiac commented Oct 2, 2017

@kemar After having done some research I think I could submit a PR that allows vue-gettext to support server side rendering with the exception of the v-translate directive. If you are using vue-gettext only for client side rendering v-translate will still work fine.

The reason v-translate can't be supported server side is due to a shortcoming of the Vue API. Directives can either be made global or available to a specific component. However, if you have a directive that depends on a per request value (i.e. SSR) and you want to make it available to all of the components within the current request render tree (i.e. not global for all components across all requests) there is no option.

You'd need something like this.

const getTranslateDirective = lang => ( {
  bind () {
    // access to lang
  },
  update () {
    // access to lang
  }
})

const translate = getTranslateDirective(() => store.getters.lang)

new Vue({
  directives: {
    translate: {
      module: translate,
      provide: true
    }
  }
})

@trainiac
Copy link
Contributor Author

trainiac commented Oct 2, 2017

vuejs/vue#6732 Let's see what they say.

@trainiac
Copy link
Contributor Author

trainiac commented Oct 4, 2017

Well it looks like there will be no budging on that so I don't see a way to inject reactive data into a directive without passing it into the directive (v-translate-language='language') but that seems verbose to have to do that every place you want a translation. If the only difference between the translate directive and translate component is the ability to inject html, I think I might start investing some time into why it can't be done with a component. The end all goal here is to support a translate directive and component that works for client side only apps and SSR apps.

@AlexandreBonaventure
Copy link

@trainiac Is your fork is already working and usable ?

@trainiac
Copy link
Contributor Author

My fork works for my purposes. I only support the translate component. And there is a little more instruction on how to use it. If you're interested let me know and I could walk you through it via gitter.

@trainiac trainiac changed the title Cant set a language per request with Server Side Rendering (SSR). Cant set a language per request with server side rendering (SSR). Nov 17, 2017
@imcvampire
Copy link

Hi @trainiac, can you share your fork with us?

@thujone25
Copy link

@trainiac personally I use cookies for translations with SSR. Pull value from cookies -> set the global $language param on the Node side

@trainiac
Copy link
Contributor Author

It looks like Vue is gonna allow directives per instance now so when this lands in 3.0 I'll submit a PR. vuejs/rfcs#29

@brunobg
Copy link

brunobg commented Oct 14, 2019

I understand vue 3.0 will take a few months yet, and the repo fork from @trainiac is quite old and can't be merged automatically. @trainiac, do you think it's worth updating your fork for the current version, even thought v-translate won't work yet? I'll need translation for a SSR soon and I could work on that update.

@ghost
Copy link

ghost commented Oct 28, 2019

@brunobg i'm desperate, please help me!
We got this issue :(

@brunobg
Copy link

brunobg commented Oct 28, 2019

@WdgtSam never got a reply from @trainiac, so I don't know if his repo fork would still work if ported to the current version.

@brunobg
Copy link

brunobg commented Feb 3, 2020

To those looking for this, here's how I did it. This is not a pure server side solution, but it works well enough for my purposes. This solution is used in https://github.com/Corollarium/lajevr/.

  1. Install vue-gettext as usual.

  2. Create a plugins/translation.js file to load vue-gettext. The code at my repo tries to guess the best language as well, but here's the basics:

import Vue from 'vue';
import GetTextPlugin from 'vue-gettext';
import translations from '../components/translations.json';

export default ({ app }) => {
  Vue.use(
    GetTextPlugin,
    {
      availableLanguages: {
        en_US: 'English',
        pt_BR: 'Português'
      },
      defaultLanguage: 'en_US',
      translations,
      silent: true
    }
  );
};
  1. Load the plugin in your nuxt.config.js:
  plugins: [
    { src: '~/plugins/translations.js', mode: 'client' }
  ],
  1. The language picker won't have access to $language when compiled, but it can use it at mount time:
<template>
  <div>
    <select
      v-model="current"
      @change="changed()"
      name="language"
    >
      <option v-for="(language, key) in available" :value="key">
        {{ language }}
      </option>
    </select>
  </div>
</template>

<script>
export default {
  data () {
    return {
      current: 'en_US',
      available: {}
    };
  },

  mounted () {
    this.current = this.$language.current;
    this.available = this.$language.available;
  },

  methods: {
    changed () {
      this.$language.current = this.current;
    }
  }
};
</script>

Everything else works as documented. The only drawbacks are that all translations are compiled in the JS and the strings are replaced at the client side. To the end user there's no difference, and on the other hand, if you change the language no reload from server is required.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants