Skip to content
This repository has been archived by the owner on Nov 15, 2019. It is now read-only.

Support template strings #6

Open
MrOrz opened this issue Apr 3, 2015 · 14 comments
Open

Support template strings #6

MrOrz opened this issue Apr 3, 2015 · 14 comments

Comments

@MrOrz
Copy link

MrOrz commented Apr 3, 2015

i18n-webpack-plugin is extremely powerful because it eliminates the table-lookup process by replacing the whole __() function calls on compile time.

However, when we are dealing with strings with variables in it, we often need to include some sort of runtime string replacement that works like sprintf(). I think it would be more powerful if it supports ES6 template strings, which converts the following:

// code.js
var count = 10;
console.log(__(`${count} unread posts`));

into something like this:

// code.bundle.js
var count = 10;
console.log(`${count} 封未讀訊息`);

given the following language file:

// zh-TW.json
{
  "${count} unread posts": "${count} 封未讀訊息"
}

The translated template strings can be processed by loaders like babel-loader after __() replacement.

@lime
Copy link

lime commented Apr 7, 2015

I'm currently facing this same problem. Although for me, it extends to other common i18n tasks as well, such as pluralization, selecting a different locale at runtime, and so on.

Since well above 90% of all the keys I have can be statically replaced on compile time, I would like to stick to i18n-webpack-plugin by default. For those few runtime translations / interpolations etc, I was thinking of creating a compile-time function similar to __(), which would be replaced by a call to some client-side i18n library. That way I could offload all the i18n features to an existing library that does it well.

__('some.key')
// compiles to
"dinosaur"

___('other.key', { count: 4 })
// compiles to
I18n.t('other.key', { count: 4 })
// evaluates to
"4 dinosaurs"

Another benefit of this is that I can choose a client-side i18n library that uses the same interpolation syntax as I have on the server-side, or whatever niche feature I need.

Not sure whether this is something that would fit into i18n-webpack-plugin or should be its own plugin.

@sokra
Copy link
Member

sokra commented Apr 7, 2015

Good idea. babel-loader runs before the i18n-webpack-plugin, so we need to be able to replace something like __("" + count + " unread posts") with __("" + count + " 封未讀訊息")

@MrOrz
Copy link
Author

MrOrz commented Apr 7, 2015

@lime thanks for the reply. There is an open issue addressing the pluralization ( #1 ), in which you might ve interested :)

As for me, I am currently using GNU Gettext along with I18n-webpack-plugin. Therefore I always use natural language as keys inside __(…) calls. If template string keys were supported, pluralization may be a trivial task as well:

// code.js
var count = 10;
console.log(count === 1 ? __(`${count} unread post`) : __(`${count} unread posts`));

@lime
Copy link

lime commented Apr 7, 2015

@MrOrz pluralization, like interpolation, is a trivial task on its own. :) My thought was that instead of adding rudimentary support for one common i18n feature after another into the core of this plugin, we could enable users to use existing client-side i18n libraries.

Of course, those who only need e.g. interpolation could use a different replacement snippet, the same way we currently can customize what localization function to use.

E.g. instead of the I18n.t('text') I use, you could configure the plugin to replace with an ES6 template string, an inline function call like function(c){ return "" + c + " unread posts"; }(unread_count) or whatever. The template string solution could even be the default for all I care, but it would still provide flexibility for those that need it. :)

@MrOrz
Copy link
Author

MrOrz commented Apr 8, 2015

@lime I get what you mean now :) Instead of trying to be a swiss army knife, we can delegate some work to existing solutions that does a better job; since i18n-webpack-plugin is good at replacing function calls, it is capable of doing such things.

I would like to second the idea that this is suitable for a new plugin on its own. In https://github.com/webpack/i18n-webpack-plugin/blob/master/index.js most parts are i18n related (L33 - L64), and the rest are like a boilerplate for a function-call-swapping plugin. Thus, it makes sense to do function call swapping in a separate plugin.

@priyajeet
Copy link

Currently I am working around this issue by using the webpack string replace loader as follows:

preLoaders: [
                { 
                    test: js,
                    loader: StringReplacePlugin.replace({
                        replacements: [
                            {
                                pattern: /\_\_\(`(.*?)\`\)/ig,
                                replacement: function (match, p1) {
                                    return '`' + languageJSON[p1] + '`';
                                }
                            }
                        ]
                    })
                }
            ]

where languageJSON is the json bundle having key / value pairs for localization. Have to use the above stuff as a preLoader because babel needs to run to convert ES6 template strings to ES5 compliant code. The regex basically searches for all occurrences of __(````) with the ES6 template string coming from the JSON bundle. Webpack i18n plugin continues to replace __('')

@priyajeet
Copy link

Another thing that should be done is to run this plugin as a loader (quite like the StringReplacePlugin allows), so that it can be run before the babel loader. Then the i18n plugin could do the replacements and still have ES5 compatibility.

@MrOrz
Copy link
Author

MrOrz commented Jan 4, 2017

StringReplacePlugin in preLoaders works exactly I expected. Thank you so much @priyajeet for sharing this technique! I am closing this because my need specified in the original post is fully fulfilled.

p.s. StringReplacePlugin requires adding itself in plugins as well, just like ExtractTextPlugin. If you forget to do so you will run into cryptic errors during compilation.

@MrOrz MrOrz closed this as completed Jan 4, 2017
@ronkorving
Copy link

I think the most elegant solution is to use ES6 template strings "the right way" :)

That is:

const str = __`foo ${bar}`;

Notice how __ is not written as a function call. This technique is called tagged template literals. It gives you full power over variables.

Read more at MDN: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals

@MrOrz
Copy link
Author

MrOrz commented Jan 4, 2017

@ronkorving Thanks for sharing. I know about template literals, but could not come up with a good use with i18n-webpack-plugin.

@AlexMost
Copy link

AlexMost commented Jan 5, 2017

currently working right at the same thing - https://github.com/AlexMost/babel-plugin-c-3po. Still in progress, will try to have first working solution in a couple of weeks. It's a babel plugin that can be used for extraction translations and for replacing them at compile time. This will work nicely with webpack. Here is a demo project - https://github.com/AlexMost/c-3po-demo

@aikar
Copy link

aikar commented Feb 8, 2017

Can this be reopened. Just because there is a workaround doesn't mean the problem is solved.

This plugin should just natively support it.

@MrOrz
Copy link
Author

MrOrz commented Feb 10, 2017

Sure, I will open this and leave the decision of closing the issue to the repo owner.

@MrOrz MrOrz reopened this Feb 10, 2017
@AlexMost
Copy link

check out this tutorial about localization with webpack - https://c-3po.js.org/localization-with-webpack-and-c-3po.html.

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

No branches or pull requests

9 participants