react-localize
Advanced tools
Comparing version 1.0.0 to 1.1.0
@@ -6,3 +6,3 @@ 'use strict'; | ||
}); | ||
exports.Text = undefined; | ||
exports.LocalizationConnector = exports.LocalizationWrapper = exports.Text = undefined; | ||
@@ -17,5 +17,15 @@ var _Localization = require('./Localization'); | ||
var _LocalizationWrapper = require('./LocalizationWrapper'); | ||
var _LocalizationWrapper2 = _interopRequireDefault(_LocalizationWrapper); | ||
var _LocalizationConnector = require('./LocalizationConnector'); | ||
var _LocalizationConnector2 = _interopRequireDefault(_LocalizationConnector); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
exports.default = _Localization2.default; | ||
exports.Text = _Text2.default; | ||
exports.Text = _Text2.default; | ||
exports.LocalizationWrapper = _LocalizationWrapper2.default; | ||
exports.LocalizationConnector = _LocalizationConnector2.default; |
@@ -7,6 +7,10 @@ 'use strict'; | ||
var _get = require('lodash/get'); | ||
var _localizeFormatter = require('./util/localize-formatter'); | ||
var _get2 = _interopRequireDefault(_get); | ||
var _localizeFormatter2 = _interopRequireDefault(_localizeFormatter); | ||
var _lodash = require('lodash.get'); | ||
var _lodash2 = _interopRequireDefault(_lodash); | ||
var _react = require('react'); | ||
@@ -16,8 +20,5 @@ | ||
var _util = require('util'); | ||
var _util2 = _interopRequireDefault(_util); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var bool = _react.PropTypes.bool; | ||
var func = _react.PropTypes.func; | ||
@@ -32,3 +33,6 @@ var objectOf = _react.PropTypes.objectOf; | ||
propTypes: { | ||
messages: objectOf(string).isRequired | ||
messages: objectOf(string).isRequired, | ||
localize: func, | ||
_localizeDebug: bool, | ||
xLocale: bool | ||
}, | ||
@@ -38,3 +42,6 @@ | ||
return { | ||
messages: {} | ||
messages: {}, | ||
localize: _localizeFormatter2.default, | ||
_localizeDebug: false, | ||
xLocale: false | ||
}; | ||
@@ -45,3 +52,4 @@ }, | ||
childContextTypes: { | ||
localize: func | ||
localize: func, | ||
_localizeDebug: bool | ||
}, | ||
@@ -51,19 +59,19 @@ | ||
return { | ||
localize: this.localize | ||
localize: this.localize, | ||
_localizeDebug: this.props.debug | ||
}; | ||
}, | ||
localize: function localize(key) { | ||
var messages = this.props.messages; | ||
var values = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1]; | ||
var _props = this.props; | ||
var messages = _props.messages; | ||
var xLocale = _props.xLocale; | ||
var string = (0, _get2.default)(messages, key, key); | ||
for (var _len = arguments.length, values = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | ||
values[_key - 1] = arguments[_key]; | ||
if (xLocale) { | ||
return 'XXXXXX'; | ||
} | ||
if (values && string) { | ||
string = _util2.default.format.apply(_util2.default, [string].concat(values)); | ||
} | ||
return string; | ||
var message = (0, _lodash2.default)(messages, key, null); | ||
return this.props.localize(message, key, values); | ||
}, | ||
@@ -75,2 +83,3 @@ render: function render() { | ||
exports.default = Localization; | ||
exports.default = Localization; | ||
module.exports = exports['default']; |
@@ -13,7 +13,6 @@ 'use strict'; | ||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } | ||
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } | ||
var array = _react.PropTypes.array; | ||
var bool = _react.PropTypes.bool; | ||
var func = _react.PropTypes.func; | ||
@@ -25,8 +24,8 @@ var string = _react.PropTypes.string; | ||
var message = props.message; | ||
var children = props.children; | ||
var values = props.values; | ||
var rest = _objectWithoutProperties(props, ['message', 'children', 'values']); | ||
var rest = _objectWithoutProperties(props, ['message', 'values']); | ||
var localize = context.localize; | ||
var _localizeDebug = context._localizeDebug; | ||
@@ -37,14 +36,14 @@ | ||
if (typeof localize === 'function') { | ||
localized = localize.apply(undefined, [message].concat(_toConsumableArray(values))); | ||
localized = localize(message, values); | ||
} | ||
if (typeof children === 'function') { | ||
return children.apply(undefined, [localized, message].concat(_toConsumableArray(values))); | ||
} else { | ||
return _react2.default.createElement( | ||
'span', | ||
rest, | ||
localized | ||
); | ||
if (_localizeDebug) { | ||
rest['data-original-message'] = message; | ||
} | ||
return _react2.default.createElement( | ||
'span', | ||
rest, | ||
localized | ||
); | ||
}; | ||
@@ -55,3 +54,2 @@ | ||
Text.propTypes = { | ||
children: func, | ||
message: string.isRequired, | ||
@@ -66,5 +64,7 @@ values: array | ||
Text.contextTypes = { | ||
localize: func.isRequired | ||
localize: func.isRequired, | ||
_localizeDebug: bool | ||
}; | ||
exports.default = Text; | ||
exports.default = Text; | ||
module.exports = exports['default']; |
@@ -1,4 +0,6 @@ | ||
require('babel/register'); | ||
require('babel-register'); | ||
const build = require('./tools/build').build; | ||
const test = require('./tools/test').test; | ||
const watchTest = require('./tools/test').watchTest; | ||
const githooks = require('./tools/githooks'); | ||
@@ -10,1 +12,3 @@ const gulp = require('gulp'); | ||
gulp.task('build', build); | ||
gulp.task('test', test); | ||
gulp.task('watchTest', watchTest); |
{ | ||
"name": "react-localize", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "A simple context wrapper and text localization component for localizing strings", | ||
"main": "build/index.js", | ||
"scripts": { | ||
"build": "gulp build", | ||
"githooks": "gulp githooks", | ||
"precommit": "npm run test", | ||
"test": "echo \"Error: no test specified\" && exit 0" | ||
"prepublish": "in-publish && npm t && npm run build || not-in-publish", | ||
"test": "gulp test" | ||
}, | ||
@@ -26,20 +28,22 @@ "repository": { | ||
"dependencies": { | ||
"lodash": "4.8.1", | ||
"react": "0.14.8" | ||
"in-publish": "2.0.0", | ||
"lodash.get": "^4.0.0", | ||
"util": "0.10.3" | ||
}, | ||
"peerDependencies": { | ||
"react": "^0.14.6 || 15.x" | ||
}, | ||
"devDependencies": { | ||
"babel": "5.8.34", | ||
"babel-preset-es2015": "6.6.0", | ||
"babel-preset-react": "6.5.0", | ||
"babel-preset-stage-0": "6.5.0", | ||
"babelify": "6.3.0", | ||
"browserify": "13.0.0", | ||
"envify": "3.4.0", | ||
"gulp": "git://github.com/gulpjs/gulp.git#aaed69d1fd73750227e803035b2a75d0f49e7de0", | ||
"babel-preset-netflix-dea": "1.0.0-alpha.2", | ||
"babel-register": "6.7.2", | ||
"chai": "^3.5.0", | ||
"gulp": "git://github.com/gulpjs/gulp.git#4ed9a4a3275559c73a396eff7e1fde3824951ebb", | ||
"gulp-babel": "6.1.2", | ||
"gulp-mocha": "^2.2.0", | ||
"gulp-symlink": "2.1.4", | ||
"lodash": "4.7.0", | ||
"vinyl-source-stream": "1.1.0", | ||
"watchify": "3.7.0" | ||
"gulp-util": "^3.0.7", | ||
"mocha": "^2.5.3", | ||
"react": "^0.14.6 || 15.x", | ||
"react-dom": "^0.14.6 || 15.x" | ||
} | ||
} |
133
README.md
# react-localize | ||
A simple context wrapper and text localization component for localizing strings. | ||
## You know this part: | ||
[![Travis build status](http://img.shields.io/travis/sprjr/react-localize.svg?style=flat)](https://travis-ci.org/sprjr/react-localize/) | ||
## Getting Started, Quickly: | ||
### Install it from [npm, Inc.](http://www.npmjs.org): | ||
`npm i react-localize` | ||
### Use it in your React App (with <Text /> helper): | ||
## You can use it like this: | ||
``` | ||
```js | ||
import Localization, { Text } from 'react-localize'; | ||
<Localization messages={{ | ||
const localizationBundle = { | ||
'app.button.Submit': 'Submit', | ||
@@ -17,34 +20,116 @@ foo: { | ||
} | ||
}}> | ||
<AnyParent> | ||
}; | ||
<Localization messages={localizationBundle}> | ||
<AnyParentComponent> | ||
<Text message="prop.Val" style={{ color: 'blue' }} /> | ||
<Text message="app.button.Submit" data-magic="pretty neat" /> | ||
<Text message="foo.bar" values={['Foophen', 32]} style={{ color: 'red' }} /> | ||
</AnyParent> | ||
</AnyParentComponent> | ||
</Localization> | ||
// outputs (respectively): | ||
// <span style="color: blue">prop.Val</span> | ||
// <span data-magic="pretty neat">Submit</span> | ||
// <span style="color: red">Hey Foophen, you must be 32 old?</span> | ||
``` | ||
## You can expect the above to output: (respectively) | ||
### Use it in your React App (with context.localize() method): | ||
```js | ||
// app.js | ||
<Localization messages={myBundle}> | ||
<YourComponent /> | ||
</Localization> | ||
// YourComponent.jsx | ||
import TabsMaybe from 'react-bootstrap-tabs-i-guess'; | ||
export default YourComponent = (props, context) => { | ||
const tabsConfig = props.tabsArray.map((tab) => { | ||
return { | ||
id: tab.id, | ||
message: context.localize(tab.label) | ||
onClick: () => props.onTabClick(tab.id) | ||
}; | ||
}); | ||
return <TabsMaybe tabs={tabConfig} /> | ||
}; | ||
``` | ||
<span style="color: blue">prop.Val</span> | ||
<span data-magic="pretty neat">Submit</span> | ||
<span style="color: red">Hey Foophen, you must be 32 old?</span> | ||
## More Info & Usage: | ||
`<Localization />` exposes a `localize(key, values)` function that is passed through [ReactJS Context](https://facebook.github.io/react/docs/context.html) to all children in the render tree it wraps. It takes a `messages` property that should be formatted like `{ 'mykey.path.to.Value': 'Value' }` or `{ myKey: { path: { to: { Value: 'Value' } } } }`. | ||
The `<Text />` component is just a wrapper intended to help you out when you don't need or want to wire your component up to `contextTypes` and process things yourself. All it's really doing it helping you call `localize(key, values)`. By default it returns a span with all the other props you pass this component. Because this renders a `<span>` it's not always useful, for example when localizing `<input placeholder='something' />`. | ||
### LocalizationWrapper | ||
If you need to do non-JSX localization and you just want the string back, you must wire your component up to context in order to pick up the formatter function. We provide an HOC wrapper to help with this, like so: | ||
```js | ||
import { LocalizationWrapper } from 'react-localize'; | ||
const MyComponent = React.createClass({ | ||
// stuff | ||
render() { | ||
return <input placeholder={this.context.localize('foo')} />; | ||
} | ||
}); | ||
export default LocalizationWrapper(MyComponent); | ||
``` | ||
## Also you can defer rendering for custom output: | ||
This is just a convenience HOC for delcaring `contextTypes` on your component, which you're welcome to do if you don't like this pattern. | ||
### LocalizationConnector | ||
There's also an HOC wrapper to quickly provide childContextTypes for a given component. Let's redo the first example above using this pattern: | ||
```js | ||
// app.js | ||
import { LocalizationWrapper } from 'react-localize'; | ||
import MyApp from './app.js'; | ||
const localizationBundle = { | ||
'app.button.Submit': 'Submit', | ||
foo: { | ||
bar: 'Hey %s, you must be %d old?' | ||
} | ||
}; | ||
export default LocalizationWrapper(MyApp, localizationBundle) | ||
``` | ||
<Text message='some.key'> | ||
{(localized, message, ...values) => { | ||
// message & ...values are optional, and are really just whatever you sent in on props | ||
return <input type="text" placholder={localized} /> | ||
}} | ||
</Text> | ||
This is just a convenience HOC for declaring `childContextTypes` for your app, the same way `<Localization><MyApp /></Localization>` does. | ||
## Available Props | ||
### xLocale | ||
If you pass `<Localization xLocale={true} />` this short circuits `context.localize()` calls to always return `XXXXXX`. This can be useful for viewing your UI and identifying which parts of the application are not using localization. | ||
### debug | ||
Utilizing `<Localization debug={true} />` expands the `<Text />` helper to include an HTML attribute `data-original-message`, which will be set to the `message` prop given. This can be useful in areas like Chrome DevTools where you want to see what key an element is using to localize things without having to swap back to your code. | ||
### localize | ||
`context.localize(key, values)` simply tries to look `messageBundle[key]` up using `lodash/get`. It then calls `this.props.localize(message, key, values);` so that the string can be formatted, you can utilize the default formatter function we provide, or you can override this to suit your needs. If the key is not found on your bundle, by default you'll receive the key back. | ||
The default `localize()` format function behaves similar to `printf` formatted strings. You can read more about how that works on [util.format](https://nodejs.org/api/util.html#util_util_format_format) as well. Here's some quick examples: | ||
```js | ||
util.format('Why hello, %s!', 'Foophen'); | ||
// 'Why hello, Foophen!' | ||
util.format('There are %d things you have to do today', 5) | ||
// There are 5 things you have to do today | ||
``` | ||
(that's so you can use something other than span, or put localized text in attributes, mostly) | ||
## Testing | ||
React is in package.json's dev and peer dependencies because React is required for running the tests. You'll need to build before the tests will work as they're run against the built files. We're using Puny to test because tests for react-localize should be simple & fast. | ||
## More Stuff | ||
I think you might need to browserify `build/index.js` because of `util` dependency. I have the `dist/` folder but I'm not sure how I feel about all that or what it should do yet. | ||
`¯\_(ツ)_/¯` | ||
```sh | ||
npm run build | ||
npm t | ||
``` |
import Localization from './Localization'; | ||
import { default as Text } from './Text'; | ||
import LocalizationWrapper from './LocalizationWrapper'; | ||
import LocalizationConnector from './LocalizationConnector'; | ||
export default Localization; | ||
export { Text }; | ||
export { Text, LocalizationWrapper, LocalizationConnector }; |
@@ -1,9 +0,12 @@ | ||
import get from 'lodash/get'; | ||
import defaultLocalizer from './util/localize-formatter'; | ||
import get from 'lodash.get'; | ||
import React, { PropTypes } from 'react'; | ||
import util from 'util'; | ||
const { func, objectOf, string } = PropTypes; | ||
const { bool, func, objectOf, string } = PropTypes; | ||
const Localization = React.createClass({ | ||
propTypes: { | ||
messages: objectOf(string).isRequired | ||
messages: objectOf(string).isRequired, | ||
localize: func, | ||
_localizeDebug: bool, | ||
xLocale: bool | ||
}, | ||
@@ -13,3 +16,6 @@ | ||
return { | ||
messages: {} | ||
messages: {}, | ||
localize: defaultLocalizer, | ||
_localizeDebug: false, | ||
xLocale: false | ||
}; | ||
@@ -19,3 +25,4 @@ }, | ||
childContextTypes: { | ||
localize: func | ||
localize: func, | ||
_localizeDebug: bool | ||
}, | ||
@@ -25,15 +32,16 @@ | ||
return { | ||
localize: this.localize | ||
localize: this.localize, | ||
_localizeDebug: this.props.debug | ||
}; | ||
}, | ||
localize(key, ...values) { | ||
const { messages } = this.props; | ||
let string = get(messages, key, key); | ||
localize(key, values=[]) { | ||
const { messages, xLocale } = this.props; | ||
if (values && string) { | ||
string = util.format(string, ...values); | ||
if (xLocale) { | ||
return 'XXXXXX'; | ||
} | ||
return string; | ||
const message = get(messages, key, null); | ||
return this.props.localize(message, key, values); | ||
}, | ||
@@ -40,0 +48,0 @@ |
@@ -8,3 +8,3 @@ /* | ||
* <AnyParent> | ||
* <Text key='localized.strings.HelloWord' style={{ color: 'blue' }} /> | ||
* <Text message='localized.strings.HelloWord' style={{ color: 'blue' }} /> | ||
* </AnyParent> | ||
@@ -17,7 +17,7 @@ * </Localization> | ||
import React, { PropTypes } from 'react'; | ||
const { array, func, string } = PropTypes; | ||
const { array, bool, func, string } = PropTypes; | ||
const Text = (props, context) => { | ||
const { message, children, values, ...rest } = props; | ||
const { localize } = context; | ||
const { message, values, ...rest } = props; | ||
const { localize, _localizeDebug } = context; | ||
@@ -27,11 +27,10 @@ let localized = message; | ||
if (typeof localize === 'function') { | ||
localized = localize(message, ...values); | ||
localized = localize(message, values); | ||
} | ||
// allow for optional alternative API using render-prop pattern | ||
if (typeof children === 'function') { | ||
return children(localized, message, ...values); | ||
} else { | ||
return (<span {...rest}>{localized}</span>); | ||
if (_localizeDebug) { | ||
rest['data-original-message'] = message; | ||
} | ||
return (<span {...rest}>{localized}</span>); | ||
}; | ||
@@ -42,3 +41,2 @@ | ||
Text.propTypes = { | ||
children: func, | ||
message: string.isRequired, | ||
@@ -53,5 +51,6 @@ values: array | ||
Text.contextTypes = { | ||
localize: func.isRequired | ||
localize: func.isRequired, | ||
_localizeDebug: bool | ||
}; | ||
export default Text; |
# Done is Better than Perfect™ | ||
(but we should do these, eventually) | ||
## (but we should do these, eventually) | ||
- Tests. (because then we'll know if we break something) | ||
- better usage documentation | ||
- Fix up lint / style and update githooks | ||
- Add custom retrieval hook for `<Localization />` for folks who want to deviate from the current data model pattern. | ||
- Improve built in `localize()` key lookup functionality |
@@ -1,20 +0,7 @@ | ||
import assign from 'lodash/assign'; | ||
import babelify from 'babelify'; | ||
import browserify from 'browserify'; | ||
import envify from 'envify/custom'; | ||
import gulp from 'gulp'; | ||
import babel from 'gulp-babel'; | ||
import source from 'vinyl-source-stream'; | ||
import gulp from 'gulp'; | ||
//------------------------------------------------------------------------------ | ||
// Build Client Assets | ||
// Build ES5 Assets | ||
//------------------------------------------------------------------------------ | ||
export function dist() { | ||
return browserify('./src/index.js') | ||
.transform(babelify) | ||
.bundle() | ||
.pipe(source('index.js')) | ||
.pipe(gulp.dest('dist')); | ||
}; | ||
export function build() { | ||
@@ -24,3 +11,3 @@ return gulp.src('./src/**/*.js*') | ||
comments: false, | ||
presets: ['es2015', 'react', 'stage-0'], | ||
presets: ['netflix-dea'], | ||
sourceMaps: true | ||
@@ -27,0 +14,0 @@ })) |
@@ -1,3 +0,2 @@ | ||
import gulp from 'gulp'; | ||
import symlink from 'gulp-symlink'; | ||
import gulp, { symlink } from 'gulp'; | ||
@@ -8,6 +7,7 @@ //------------------------------------------------------------------------------ | ||
function githooks() { | ||
return gulp.src('./tools/pre-commit') | ||
.pipe(symlink('.git/hooks/pre-commit', { force: true })); | ||
return gulp.src('./tools/hooks/pre-commit') | ||
//`(gulp|vinylFS).symlink` injests folders only | ||
.pipe(symlink('.git/hooks', { overwrite : true })); | ||
} | ||
export default githooks; |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
11
27
1
135
0
31990
4
597
1
+ Addedin-publish@2.0.0
+ Addedlodash.get@^4.0.0
+ Addedutil@0.10.3
+ Addedcreate-react-class@15.7.0(transitive)
+ Addedencoding@0.1.13(transitive)
+ Addedfbjs@0.8.18(transitive)
+ Addediconv-lite@0.6.3(transitive)
+ Addedin-publish@2.0.0(transitive)
+ Addedinherits@2.0.1(transitive)
+ Addedis-stream@1.1.0(transitive)
+ Addedisomorphic-fetch@2.2.1(transitive)
+ Addedlodash.get@4.4.2(transitive)
+ Addednode-fetch@1.7.3(transitive)
+ Addedobject-assign@4.1.1(transitive)
+ Addedprop-types@15.8.1(transitive)
+ Addedreact@15.7.0(transitive)
+ Addedreact-is@16.13.1(transitive)
+ Addedsetimmediate@1.0.5(transitive)
+ Addedutil@0.10.3(transitive)
+ Addedwhatwg-fetch@3.6.20(transitive)
- Removedlodash@4.8.1
- Removedreact@0.14.8
- Removedacorn@5.7.4(transitive)
- Removedamdefine@1.0.1(transitive)
- Removedast-types@0.9.6(transitive)
- Removedbalanced-match@1.0.2(transitive)
- Removedbase62@1.2.8(transitive)
- Removedbrace-expansion@1.1.11(transitive)
- Removedcommander@2.20.3(transitive)
- Removedcommoner@0.10.8(transitive)
- Removedconcat-map@0.0.1(transitive)
- Removeddefined@1.0.1(transitive)
- Removeddetective@4.7.1(transitive)
- Removedenvify@3.4.1(transitive)
- Removedesprima@3.1.3(transitive)
- Removedesprima-fb@15001.1.0-dev-harmony-fb(transitive)
- Removedfbjs@0.6.1(transitive)
- Removedglob@5.0.15(transitive)
- Removedgraceful-fs@4.2.11(transitive)
- Removediconv-lite@0.4.24(transitive)
- Removedinflight@1.0.6(transitive)
- Removedinherits@2.0.4(transitive)
- Removedjstransform@11.0.3(transitive)
- Removedlodash@4.8.1(transitive)
- Removedminimatch@3.1.2(transitive)
- Removedminimist@1.2.8(transitive)
- Removedmkdirp@0.5.6(transitive)
- Removedobject-assign@2.1.1(transitive)
- Removedonce@1.4.0(transitive)
- Removedpath-is-absolute@1.0.1(transitive)
- Removedprivate@0.1.8(transitive)
- Removedq@1.5.1(transitive)
- Removedreact@0.14.8(transitive)
- Removedrecast@0.11.23(transitive)
- Removedsource-map@0.4.40.5.7(transitive)
- Removedthrough@2.3.8(transitive)
- Removedwhatwg-fetch@0.9.0(transitive)
- Removedwrappy@1.0.2(transitive)