diff --git a/.babelrc b/.babelrc index 0932411..e1553c6 100644 --- a/.babelrc +++ b/.babelrc @@ -1,4 +1,11 @@ { - "presets": ["es2015", "react"], - "plugins": ["transform-react-jsx"] + "presets": ["es2015"], + "plugins": [ + [ + "transform-react-jsx", + { + "pragma": "h" + } + ] + ] } diff --git a/ComponentInterpolator.js b/ComponentInterpolator.js index d6ec36e..32787c9 100644 --- a/ComponentInterpolator.js +++ b/ComponentInterpolator.js @@ -1,25 +1,33 @@ -var React = require('react'); +var log; +try { + // use ulog when available + log = require('ulog')('preact-i18nliner:ComponentInterpolator'); +} catch (e) { + + /* satisfy eslint */ +} + var invariant = require('invariant'); -var { string, object } = React.PropTypes; +var Component = require('preact').Component; +var h = require('preact').h; +var clone = require('extend').bind(true); var WRAPPER_PATTERN = /(\*+)/; var PLACEHOLDER_PATTERN = /(%\{.*?\})/; -var toArray = function(children) { - if (children instanceof Array) return children.slice(); - if (!children) return []; - return [children]; -}; - // Replace a "$1" text descendant in this tree with the newDescendants var injectNewDescendants = function(element, newDescendants, props, ensureInjected) { + if (log) { + log.debug(log.name + ': injectNewDescendants', element, newDescendants, props); + } + newDescendants.injectedCount = newDescendants.injectedCount || 0; props = props || {}; - var children = toArray(element.props.children); + var children = element.children; for (var i = 0; i < children.length; i++) { var child = children[i]; - children[i] = child.type ? injectNewDescendants(child, newDescendants) : child; + children[i] = child.nodeName ? injectNewDescendants(child, newDescendants) : child; } var injectIndex = getInjectIndex(children); @@ -28,14 +36,17 @@ var injectNewDescendants = function(element, newDescendants, props, ensureInject newDescendants.injectedCount++; } - props.children = children.length ? children : null; + props.children = children.length ? children : []; if (ensureInjected) { invariant(newDescendants.injectedCount === 1, 'wrappers must have a single "$1" text descendant'); } - return React.cloneElement(element, props); + return clone(element, props); }; var getInjectIndex = function(children, containerName) { + if (log) { + log.debug(log.name + ': getInjectIndex', children, containerName); + } var child, index = -1; for (var i = 0; i < children.length; i++) { child = children[i]; @@ -47,26 +58,28 @@ var getInjectIndex = function(children, containerName) { return index; }; -var ComponentInterpolator = React.createClass({ - propTypes: { - string: string.isRequired, - wrappers: object - }, - +class ComponentInterpolator extends Component { inferChildren() { + if (log) { + log.debug(log.name + ': inferChildren'); + } + var tokens = (this.props.string || '').split(WRAPPER_PATTERN); this.keyCounter = 0; var inferredChildren = this.interpolateAllComponents(tokens); - var currentChildren = toArray(this.props.children); + var currentChildren = this.props.children; var index = getInjectIndex(currentChildren, ''); invariant(index >= 0, ' must have a "$1" text child"'); currentChildren.splice.apply(currentChildren, [index, 1].concat(inferredChildren)); return currentChildren; - }, + } interpolateAllComponents(tokens, eof) { + if (log) { + log.debug(log.name + ': interpolateAllComponents', tokens, eof); + } var token, child; var children = []; var wrappers = this.props.wrappers || {}; @@ -92,9 +105,12 @@ var ComponentInterpolator = React.createClass({ } } return children; - }, + } interpolatePlaceholders(string) { + if (log) { + log.debug(log.name + ': interpolatePlaceholders', string, this.props); + } var token, child; var tokens = string.split(PLACEHOLDER_PATTERN); var children = []; @@ -107,19 +123,22 @@ var ComponentInterpolator = React.createClass({ ` expected '${token}' placeholder value, none found` ); child = this.props[token]; - child = child && child.type ? React.cloneElement(child, {key: this.keyCounter++}) : child; + child = child && child.nodeName ? clone(child, {key: this.keyCounter++}) : child; children.push(child); } else { children.push(token); } } return children; - }, + } render() { - return React.createElement('span', {}, this.inferChildren()); + return h('span', {}, this.inferChildren()); } -}); +} module.exports = ComponentInterpolator; +if (log) { + log.log('Initialized ' + log.name); +} diff --git a/LICENSE b/LICENSE index 6f98a0f..de9aec6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Jon Jensen +Copyright (c) 2016 Stijn de Witt & Jon Jensen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index 34ee559..a033ad2 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ -# react-i18nliner +# preact-i18nliner -[](http://travis-ci.org/jenseng/react-i18nliner) +[](http://travis-ci.org/Download/preact-i18nliner) -react-i18nliner brings [I18nliner](https://github.com/jenseng/i18nliner-js) -to React via the [html `translate` attribute](http://www.w3.org/International/questions/qa-translate-flag). I18n doesn't get any easier than this. +preact-i18nliner brings [I18nliner](https://github.com/jenseng/i18nliner-js) +to [Preact](https://preactjs.com/) via the [html `translate` attribute](http://www.w3.org/International/questions/qa-translate-flag). I18n doesn't get any easier than this. ## TL;DR -react-i18nliner lets you do this: +preact-i18nliner lets you do this: ```html

@@ -29,19 +29,24 @@ fallback if a translation is missing or hasn't happened yet. Best of all, you don't need to maintain separate translation files anymore; I18nliner will do it for you. +## What is this? +This project is a port of [react-i18nliner](https://github.com/jenseng/react-i18nliner) by +[Jon Jensen](https://github.com/jenseng) to [Preact](https://preactjs.com), a 3kB alternative +to React. + ## How does it work? -react-i18nliner [preprocesses](https://github.com/jenseng/react-i18nliner/blob/master/preprocess.js) +preact-i18nliner [preprocesses](https://github.com/download/preact-i18nliner/blob/master/preprocess.js) your JSX, transforming it into something truly localizable. It infers -[placeholders for expressions](https://github.com/jenseng/react-i18nliner/blob/57f813bc3ef6769be7aab47eb42fd4d081e1a498/__tests__/preprocess.test.js#L21) -and [wrappers for elements/components](https://github.com/jenseng/react-i18nliner/blob/57f813bc3ef6769be7aab47eb42fd4d081e1a498/__tests__/preprocess.test.js#L17), -and separates the localizable string. [At runtime](https://github.com/jenseng/react-i18nliner/blob/master/ComponentInterpolator.js), -it localizes the string, interpolating the [wrappers](https://github.com/jenseng/react-i18nliner/blob/57f813bc3ef6769be7aab47eb42fd4d081e1a498/__tests__/ComponentInterpolator.test.js#L28) -and [placeholders](https://github.com/jenseng/react-i18nliner/blob/57f813bc3ef6769be7aab47eb42fd4d081e1a498/__tests__/ComponentInterpolator.test.js#L42) into the correct locations. +[placeholders for expressions](https://github.com/download/preact-i18nliner/blob/57f813bc3ef6769be7aab47eb42fd4d081e1a498/__tests__/preprocess.test.js#L21) +and [wrappers for elements/components](https://github.com/download/preact-i18nliner/blob/57f813bc3ef6769be7aab47eb42fd4d081e1a498/__tests__/preprocess.test.js#L17), +and separates the localizable string. [At runtime](https://github.com/download/preact-i18nliner/blob/master/ComponentInterpolator.js), +it localizes the string, interpolating the [wrappers](https://github.com/download/preact-i18nliner/blob/57f813bc3ef6769be7aab47eb42fd4d081e1a498/__tests__/ComponentInterpolator.test.js#L28) +and [placeholders](https://github.com/download/preact-i18nliner/blob/57f813bc3ef6769be7aab47eb42fd4d081e1a498/__tests__/ComponentInterpolator.test.js#L42) into the correct locations. Localizable strings are detected both from the text nodes, as well as from [translatable attributes](http://www.w3.org/TR/html5/dom.html#the-translate-attribute) within the `translate="yes"` element. -react-i18nliner enhances I18nliner, so that it can extract any of these +preact-i18nliner enhances I18nliner, so that it can extract any of these `translate="yes"` strings from your codebase (in addition to regular `I18n.t` calls). Once you get everything translated, just stick it on `I18n.translations` and everything will Just Work™. @@ -114,10 +119,10 @@ within a translated element. Get i18n-js and i18nliner installed [per these instructions](https://github.com/jenseng/i18nliner-js#installation). -### 2. add react-i18nliner +### 2. add preact-i18nliner ```bash -npm install react-i18nliner --save +npm install preact-i18nliner --save ``` And make sure your `.i18nrc` file has: @@ -125,7 +130,7 @@ And make sure your `.i18nrc` file has: ```json { "plugins": [ - "react-i18nliner" + "preact-i18nliner" ] } ``` @@ -133,20 +138,20 @@ And make sure your `.i18nrc` file has: This will ensure that when you export strings for translation, all of your new `translate="yes"` stuff will get picked up. -### 3. preprocess all your js files with react-i18nliner +### 3. preprocess all your js files with preact-i18nliner How you hook up the preprocessor will depend on how you bundle your assets: #### webpack -Add [this loader](https://github.com/jenseng/react-i18nliner/blob/master/webpack-loader.js) +Add [this loader](https://github.com/download/preact-i18nliner/blob/master/webpack-loader.js) to your config, e.g. ```js { module: { loaders: [ - { test: /\.js$/, loader: "react-i18nliner/webpack-loader" } + { test: /\.js$/, loader: "preact-i18nliner/webpack-loader" } ... ], }, @@ -154,16 +159,17 @@ to your config, e.g. } ``` -Check out [this example app](https://github.com/jenseng/react-i18nliner/tree/master/examples/webpack) +**TODO: example not ported over yet** +Check out [this example app](https://github.com/download/preact-i18nliner/tree/master/examples/webpack) to see how everything is wired together. #### browserify -Use [this transform](https://github.com/jenseng/react-i18nliner/blob/master/browserify-transform.js), +Use [this transform](https://github.com/download/preact-i18nliner/blob/master/browserify-transform.js), e.g. ```bash -$ browserify -t react-i18nliner/browserify-transform app.js > bundle.js +$ browserify -t preact-i18nliner/browserify-transform app.js > bundle.js ``` #### something else? @@ -174,14 +180,14 @@ you use ember-cli, sprockets, grunt concat, etc., it's relatively painless to add a little glue code that runs preprocess on each source file. -### 4. add the react-i18nliner runtime extensions to i18n-js +### 4. add the preact-i18nliner runtime extensions to i18n-js Assuming you have a cjs-style app, do something like this: ```js var I18n = require("./path/to/cjs'd/i18n"); require("i18nliner/dist/lib/extensions/i18n_js")(I18n); -require("react-i18nliner/dist/extensions/i18n_js")(I18n); +require("preact-i18nliner/dist/extensions/i18n_js")(I18n); ``` If you're using AMD/`