fnando / i18n-js

It's a small library to provide the I18n translations on the Javascript. It comes with Rails support.
MIT License
3.77k stars 520 forks source link

Locale not switching ( Always en ) #581

Closed TheHussein closed 4 years ago

TheHussein commented 4 years ago

Hello, so I have my Rails/Vue application where I have en.yml and ar.yml in my config/locales, I import them in my config/application.rb like this

I18n.default_locale = :en
I18n.available_locales = [:en, :ar]
config.i18n.fallbacks = true

in my application.js.erb I have

//= require i18n
//= require i18n.js
//= require i18n/translations

Now I have a vue mixin that I import and use that looks like this

export default {
  methods: {
    t: (...args) => I18n.t(...args),
    currentLocale: () => I18n.currentLocale(),
  },
};

My problem is that the locale set in I18n-js is always "en", if I console.log(I18n.locales.get()), it's always an array of "en".

in my application_controller.rb I have

  def set_locale
    I18n.locale = params[:locale] || I18n.default_locale
  end

  def default_url_options
    { locale: I18n.locale }
  end

I have even tried I18n.locale = "ar" here but it was still "en" in i18n-js

I know the params locale is set properly because other erb files using rails i18N translate fine. Only the vue ones using i18n-js seem to always be en.

PikachuEXE commented 4 years ago

I have never used vue so I can't speak for that part

The config you set in config/application.rb is for ruby side If you want to propagate those config onto I18n object on JS side, you must do it yourself

My app's JS got a piece of code like this:

import(
  /* webpackMode: "eager" */
  "./../../../../assets/javascripts/plugins/i18n-js/index.ts.erb"
)
.then(({default: I18n}) => {
  // region assign existing locale
  try {
    if (typeof window.I18n.defaultLocale === "string") {
      I18n.defaultLocale = window.I18n.defaultLocale
    }
  } catch (e) {
    // Do nothing
  }
  try {
    if (typeof window.I18n.locale === "string") {
      I18n.locale = window.I18n.locale
    }
  } catch (e) {
    // Do nothing
  }
  window.I18n = I18n
})

And this in layout

  :javascript
    (function(I18n) {
      I18n.defaultLocale = "#{I18n.config.default_locale}";
      I18n.locale = "#{valid_external_locale_sym_from_params}";
    }(this.I18n = {}))
TheHussein commented 4 years ago

Wow documentation mentions none of this I'm surprised no one else had my issue before.

For the code in your layout I added it to my application.js pack which I render as a javascript pack tag on my layout, I got the following error:

application.js:26 Uncaught TypeError: Cannot set property 'I18n' of undefined
    at Module../app/javascript/packs/application.js (application.js:26)

For your JS code I'm not even sure where to place that and even if I definitely don't have that plugin you're importing, I'm using the asset pipeline approach which just said to install the gem add a couple lines to application.js.erb and voila, I just need it to start reading my ruby locales.

TheHussein commented 4 years ago

Added the following to my layout and all checked out in case anyone stumbles across the same. thing is that webpacker = you're not using asset pipeline even if you are otherwise in your rails app.

    <%= javascript_include_tag "i18n" %>
    <script type="text/javascript">
    I18n.defaultLocale = "<%= I18n.default_locale %>";
    I18n.locale = "<%= I18n.locale %>";
    </script>
JohnRDOrazio commented 2 years ago

I haven't been having much luck with any of the methods provided. But considering that in my app, locale is set from the URL, here's the solution I've come up with for now, using Rails 7 with @hotwire/stimulus:

in config/i18n-js.yml

translations:
  - file: "app/javascript/src/translations.js"
    prefix: "import I18n from 'i18n-js';\n"
    suffix: "window.I18n = I18n;\nconst currentPathLocale = location.pathname.substring(1, 3);\nif( ['en', 'it'].includes( currentPathLocale ) ) {\n    window.I18n.locale = currentPathLocale;\n}"
export_i18n_js: false

This will make sure that window.I18n.locale is set after window.I18n and before any of the stimulus controllers kick in.

Only downside to this approach is that the enabled languages are hardcoded with an array in the javascript that is produced, and would have to be manually updated any time any new languages are enabled in the rails app...