How to share Rails i18n messages with React through webpack

Very specific title hu? This time I’m here for sharing a solution for a specific problem I encountered a couple of days ago.

There’s this existing Rails monolithic application. Team and customer decided that time had come for this app to be decoupled in two components: Rails would do its usual work as an administration and API backend, while React would be used for the frontend component. Everything related to the frontend would then be rewritten, keeping the same behaviour and visual design. But there are a lot of translations related to the user experience and that have now to be included in the javascript bundle, while they were before used by the server.

To do so, I was aiming to write a webpack plugin that could give access to the Rails yml translations. Then I discovered the Virtual Module Plugin: During webpack compilation this plugin injects a fake file containing the given text.

Working on it led me to an extension that flattens and joins the various translations, providing them to javascript source files without duplicating data: a Rails Translations Plugin. In our case we didn’t even want to share ALL translations between Rails and React, but just a frontend.yml file (that stores messages related to the user experience), so the resulting webpack.config.js is:

// app/frontend/webpack.config.js

const clientApp = path.resolve(__dirname, 'app');
const webpackConfig = {
  entry: [path.join(clientApp, 'index.js')],
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  plugins: [
    new RailsTranslationsPlugin({
      root       : clientApp,
      localesPath: path.resolve(__dirname, '../backend/config/locales'),
      pattern    : '**/frontend.yml'
    })
  ],
  module: {
    loaders: [
      {
        include : clientApp,
        test    : /\.(js|jsx|es6)$/,
        loader  : 'babel-loader'
      },
      {
        include : clientApp,
        test    : /\.json$/,
        loader  : 'json-loader'
      }
    ]
  }
};

And this allowed us to use them in ReactIntl:

// app/context.jsx
// ...
import { addLocaleData } from 'react-intl';
import translations from 'translations.json';

for (let locale of Object.keys(translations)) {
  addLocaleData(require(`react-intl/locale-data/${locale}`));
}

const locale   = navigator.language || 'en';
const messages = translations[locale];

return (
  <IntlProvider locale={locale} key={locale} messages={messages}>
    <App />
  </IntlProvider>
)

The only downside is that React Intl is not able, obviously, to work with Rails localization formats, and in fact we needed to iterate over the various locales and require its additional data needed for localization (look at the addLocaleData method in the above snippet). I’m now thinking of improving this plugin adding builtin methods that work like Rails’s I18n.t and I18n.l.

A front-end tale: Google Places Library, jQuery promises and Coffeescript

When it comes to define my working position I often need to state (and clarify) the differences between design, frontend and backend development.
Given my academic background and my interests I can easily be considered more a backend developer than a frontend. I do not have a great “esthetic sense” and despite my knowledge of HTML5 and CSS (with all its SCSS, SASS, LESS and so on) I do not have the expertise of a “real frontend developer”.
Don’t get me wrong, I studied Javascript basics and I like to keep myself updated with its community news but still, it is not (at least for now ;P) my area of expertise.

image

Nevertheless I like to solve problems independently on their nature and so if there is a “frontend problem” to tackle I’m always ready to dive in.

This is exactly what happened last week.

Continue reading “A front-end tale: Google Places Library, jQuery promises and Coffeescript”

A “desperate times call for desperate measures” hack

Last week I had to solve some tricky problems related to a Web application that needs to run locally on some tablets in kiosk mode without any Internet connection.

With the term kiosk mode I mean that the browser used to run the application, in this case Google Chrome, is wrapped in another application that is acting like a sandbox to prevent users from accessing the Operating System or any other application.

Continue reading “A “desperate times call for desperate measures” hack”

Web Procedural Map Generation – Part 2

In my previous post I created a heightmap and I started looking for a map representation system different from the tilemap.

But a tilemap, composed of square tiles, probably is not the best tool for the job: it would require a lot of tiles to obtain a nice looking result and probably we would suffer from pixeling. A better approach is to use polygons with a larger number of sides such as hexagons.
Continue reading “Web Procedural Map Generation – Part 2”

Web Procedural Map Generation – Part 1

Two weeks ago I started playing with map generation algorithms.

I wanted to come up with a map generator (and eventually a climatic environment simulator) using javascript coffeescript because it’s really easy to prototype something and my goal was to learn to do something, not to really do something (so, when talking of “map generation” there is really no need to add obstacles to the quest like OpenGL, strictly typed languages, and so on).
Continue reading “Web Procedural Map Generation – Part 1”

jQuery injection for profit and profit, cause fun is overrated

Working on a project for an important client (more on that in the next months), we came up with the need to inject jQuery on a page, making it available to your functions.

In addition to that we wanted to be sure it didn’t conflict with other jQueries the page was already using, and, if a new version of jQuery was already available, use that instead of injecting a new one.

This whole injection thing is not very complicated per se. You just have to be sure you’re not messing up with what’s already in the page.

Let’s walk through our solution to see how we implemented it. All the code you see here will be CoffeeScript, the javascript translation is really straightforward and won’t be provided 🙂

We start with an empty html document, something like this:

<!DOCTYPE html>
<head>
  <meta charset="utf-8" />
  <script src="jquery-injection.js"></script>
</head>
<body>
</body>

jQuery-injection.js is the compiled version of our coffeescript source.

The simplest way to inject jQuery would be something like this:

@Injector = 
    injectjQuery: ->
        script = document.createElement('script')
        script.src = "//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"
        head = document.getElementsByTagName('head')[0]
        
        head.appendChild(script)

We’re creating a globally available Injector object with an injectjQuery function that creates a script element, sets its src attribute and adds it to head.

This is all fine and dandy, but what if we already have jQuery? This would load jQuery twice and there would be namespace conflicts. What we want to do is check for an existing jQuery and use jQuery.noconflict() to make sure they don’t clash.

checkAndInjectjQuery: ->
    if jQuery?
      if @versionCompare(jQuery.fn.jquery, "2.0.3") < 0
        @injectjQuery(true)
      else
        @jq = window.jQuery
    else
      @injectjQuery(false)   

What’s going on in here? We check if jQuery exists, and that its version is at least 2.0.3. The version check is done via versionCompare, a coffeescript simplified port of this code I found on StackOverflow.

If jQuery exists and is sufficiently new, we just map this.jq to window.jQuery. Our local jq variable is what we’re gonna use inside our functions instead of using $.

If you paid enough attention, you will have noticed we’re calling injectjQuery with a parameter.

  injectjQuery: (saveOriginal) ->
    script = document.createElement('script')
    script.src = "http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"
    head = document.getElementsByTagName('head')[0]

    if saveOriginal
      @jq = $.noConflict(true)

    head.appendChild(script)

injectjQuery(true) will assume that there’s another jQuery in the page, so it saves it (temporarily) in this.jq, and then cleans after it, making sure it won’t be there when the new jQuery loads.

Wait, did I say “when the new jquery loads”? You can bet I did! Everything we’re doing is asyncronous, so we can’t be sure that the new jQuery is already available.

Avoiding potential issues with that requires some callback magicks. First we edit injectjQuery, adding, immediately after the last line, the following code

@checkLoaded(saveOriginal)

checkLoaded is a function that keeps calling itself until jQuery is effectively loaded.

 checkLoaded: (saveOriginal) ->
    window.setTimeout( =>
      if !jQuery?
        @checkLoaded(saveOriginal)
      else
        if saveOriginal
          temp = @jq
          @jq = $.noConflict(true)
          window.jQuery = temp
          window.$ = jQuery
        else
          @jq = jQuery

        @continueInitialization()
    , 500)

It recognizes the same saveOriginal parameter we used before, restoring the original jQuery where it belongs, and leaving us with our new jQuery, available through this.jq.

continueInitializazion is the function that should handle all of your post-jquery-loaded needs.

In this gist you’ll find the complete Injector class, with some bonus console.log calls to better understand what’s going on.

Thank you for reading up to this last paragraph, and have fun injecting jQuery where you please.