3

I have added to my project react-rails gem and I want to use for translated components.

I cannot put in the precompiled assets erb templates, but still I am trying to create components, make them available in all the project and then use them in some partial with some translation.

Working Scenario

# app/view/example/_react_component.coffee.erb
DOM = React.DOM

FormInput = React.createClass
  displayName: "FormInput"
  render: ->
    DOM.div
      className: 'row control-group'
      DOM.div
        className: 'form-group col-xs-12 floating-label-form-group controls'
        DOM.label
          htmlFor: @props.id
          @props.label
        DOM.input
          id: @props.id
          className: 'form-control'
          placeholder: @props.placeholder
          type: @props.type
        DOM.p
          className: 'help-block text-danger'

formInput = React.createFactory(FormInput)

window.ValidationFormInput = React.createClass
  displayName: "ValidationFormInput"

  getInitialState: ->
    { }

  render: ->
    formInput
      id: "<%= t('validation_form.id') %>"
      label: "<%= t('validation_form.label') %>"
      placeholder: "<%= t('validation_form.placeholder') %>"
      type: 'text'

validationFormInput = React.createFactory(ValidationFormInput)

# app/view/example/index.html.erb
<%= react_component('ValidationFormInput', {}, {class: "container"}) %>

Desired Scenario (not working)

# app/assets/javascripts/components/form_input.coffee
DOM = React.DOM

FormInput = React.createClass
  displayName: "FormInput"
  render: ->
    DOM.div
      className: 'row control-group'
      DOM.div
        className: 'form-group col-xs-12 floating-label-form-group controls'
        DOM.label
          htmlFor: @props.id
          @props.label
        DOM.input
          id: @props.id
          className: 'form-control'
          placeholder: @props.placeholder
          type: @props.type
        DOM.p
          className: 'help-block text-danger'

formInput = React.createFactory(FormInput)

# app/view/example/_react_component.coffee.erb
window.ValidationFormInput = React.createClass
  displayName: "ValidationFormInput"

  getInitialState: ->
    { }

  render: ->
    formInput
      id: "<%= t('validation_form.id') %>"
      label: "<%= t('validation_form.label') %>"
      placeholder: "<%= t('validation_form.placeholder') %>"
      type: 'text'

validationFormInput = React.createFactory(ValidationFormInput)

# app/view/example/index.html.erb
<%= react_component('ValidationFormInput', {}, {class: "container"}) %>

I guess that the issue is related to the scope of the definition of my component, but I cannot figure out how to make the component available for any partial.

Thank you in advance

Edit

In order to make the translations available, I found the gem I18n-js. After installing, I can easily run a rake task to create a js version of my config/locales/* translations

mabe02
  • 2,676
  • 2
  • 20
  • 35

1 Answers1

3

Excellent question.

There are a few ways to do this.

1- Usually, this is not just a question about how to pass data from Rails to React but rather how to generally pass data to Javascript. You can store the data in a meta in the header and access it from Javascript. This way you can still have your JS compressed and fast. (Instead of js.erb etc)

2- Passing all the translations to the react component. Basically, you can pass arguments to the react component, one of which is the translations. If it's a few translations, it's fine but if your list grows, the load would be heavy on your page.

3- Make your own Javascript translator. Here's a CoffeeScript example that I have created; make sure to add it in your assets' list before the other files. In my code, I'm pulling the locale from meta (as you can see in the code). Feel free to edit this.

class Translator
  @en = {
    internet_connection_lost: "Your internet connection has been lost"
    attempting_to_reconnect: "Attempting to reconnect!"
    failed_to_reconnect: "Failed to reconnect..."
    connection_success: "Connected"
    reconnecting: "Reconnecting..."
    bid_received: "Bid received. New balance $$bid_balance$$"
  }

  @ar = {
    internet_connection_lost: "لقد فقدت الاتصال بالإنترنت"
    attempting_to_reconnect: "نحاول إعادة الاتصال!"
    failed_to_reconnect: "لم تنجح إعادة الاتصال بالشبكة..."
    connection_success: "متصل بشبكة الإنترنت"
    reconnecting: "إعادة الاتصال جارية..."
    bid_received: "تم تلقي العرض. رصيد جديد $$bid_balance$$"
  }

  @get_translations: (locale) ->
    switch (locale)
      when 'en'
        @en
      when 'ar'
        @ar

  @translate: (val, interpolation) ->
    # get locale from meta
    locale = $("meta[name='locale']").attr("content") || "en"
    translation = Translator.get_translations(locale)[val]

    if interpolation
      console.log "#{JSON.stringify(interpolation)}"

    for k,v of interpolation
      console.log "#{translation} : #{k} : #{v}"
      translation = translation.replace(k, v)

    return translation

window.Translator = Translator

And this is how you can use the Translator

  message = Translator.translate(
    "bid_received", { "$$bid_balance$$": 10 }
  )
Abdo
  • 13,549
  • 10
  • 79
  • 98
  • You are welcome; I hope this is what you are looking for :-) – Abdo Jun 09 '16 at 13:03
  • Actually I was wondering if there is a simple way to write a `Translator` class that takes automatically all the translation in `config/locales/*` ? – mabe02 Jun 09 '16 at 13:04
  • The meta solution can do this. Your translations are just YAML files; all you need to do is have a route that can have that converted to JSON and inject it as a script. My issue with this solution is that it would do this every time and the text is not compressed. Also, you'll have to fix my interpolator though to accept `%something` =) – Abdo Jun 09 '16 at 13:13
  • For the translations I actually found this [I18n.js gem](https://github.com/fnando/i18n-js) which does what I was expecting! Thank you again for your help – mabe02 Jun 09 '16 at 14:44