Giter VIP home page Giter VIP logo

redux-polyglot's Introduction

redux-polyglot

Codeship Status for Tiqa/redux-polyglot

Toolset (actions, reducer, middleware, enhancer, selectors) to help use Polyglot with Redux.

Installation

    npm install --save redux-polyglot

Setup

First of all, you need the polyglot reducer in your rootReducer :

import { createStore, combineReducers } from 'redux';
import { polyglotReducer } from 'redux-polyglot';

const rootReducer = combineReducers({
    ...yourReducers,
    polyglot: polyglotReducer,
});
const store = createStore(rootReducer, {});

Usage

Set the language

without middleware

You can use redux-polyglot without his middleware, for this you need the setLanguage() action creator :

  • setLanguage :: (String, Object) -> Action

Example:

import { setLanguage } from 'redux-polyglot';

store.dispatch(setLanguage('en', { yolo: 'yolo' }));

second parameter should be polyglot phrases (see polyglot documentation)

note: if language phrases already exists, this will overwrite the corresponding object state.

with middleware

The createPolyglotMiddleware() function allow you to automatically update language and phrases by listening to specific action(s).

The middleware catches specific action(s), and find the locale in the payload, and then [asynchronously] load the polyglot phrases (with Promise).

It takes 3 parameters and return a middleware :

  • 1 - actionToCatch :: String | Array<String>
    • the type(s) of the action to catch
  • 2 - getLocale :: Object -> String
    • a function that take the catched action as parameter and return new language.
  • 3 - getPhrases :: String -> Promise Object
    • a function that take the language (as provided by getLocale) and return a Promise of Object ( Object should be polyglot phrases )

the middleware will catch actionToCatch; note: when a matching action is dispatched, it will return this promise called so you can await on it (pretty useful on SSR)

import { createStore, combineReducers, applyMiddleware } from 'redux';

const polyglotMiddleware = createPolyglotMiddleware(
    'ACTION_TO_CATCH',
    action => action.payload.locale,
    locale => new Promise(resolve => {
        // perform async here
        resolve({
            hello: 'bonjour',
        });
    }),
)

const store = createStore(rootReducer, {}, applyMiddleware(polyglotMiddleware));

you can catch more than one action passing an array of action types:

const polyglotMiddleware = createPolyglotMiddleware(
    ['FIRST_ACTION_TO_CATCH', 'SECOND_ACTION_TO_CATCH'],
    getLocale,
    getPhrases,
)

note: if language has not changed, nothing happens.

Translation

with getP() selector

You can use the getP(state) selector.

It returns an object with 4 functions inside :

  • t: String -> String : translation (the original polyglot t function)
  • tc: String -> String : translation capitalized
  • tt: String -> String : translation titleized
  • tu: String -> String : translation upper-cased
  • tm: (String -> String) -> String -> String : translation using a custom morphism

(see polyglot documentation)

there is an optional parameter to getP(). this is allow you to automatically 'aim' a scope in your phrases object using polyglotScope property.

for example :

store.dispatch(setLanguage('en', {
    some: { nested: { data: { hello: 'hello' } } }
}));
const p = getP(store.getState(), { polyglotScope: 'some.nested.data' });
console.log(p.tc('hello')) // => will return 'Hello'

Getting current locale

getLocale(state) selector returns current language.

If you use React

You can use connect() from react-redux, and the getP() selector, to get the p prop in your component.

Proptype:

p: PropTypes.shape({
    t: PropTypes.func.isRequired,
    tc: PropTypes.func.isRequired,
    tu: PropTypes.func.isRequired,
    tm: PropTypes.func.isRequired,
}),
translate() enhancer

props.p can be also be provided easily to a component with the translate enhancer :

import translate from 'redux-polyglot/translate';
const DummyComponentWithPProps = translate(DummyComponent);

you can select a polyglotScope with translate('scope', Component)

// all this lines return an enhanced Dummy component
translate(Dummy);
translate('catalog', Dummy); // with polyglotScope
translate()(Dummy); // curried
translate('catalog')(Dummy); // curried with polyglotScope.
translate({ polyglotScope : 'some.nested.data', ownPhrases: 'some.nested.data.hello': 'Hi !', ... })(Dummy); // curried with object configuration.
get locale in a component

You can use the getLocale() selector inside a mapStateToProps from react-redux.

Proptype: locale: PropTypes.string,

Overwrite phrases

In some case, you should be able to replace some default phrases by others phrases.

For doing this, you have to define an object which contains your overwrited phrases. This object is composed of : { 'some.nested.data': 'phrase', ... } where key is the target path you want to replace and value ... the new value.

with getP() selector

Simply add ownPhrases property and set the new configuration like above to overwrite :

store.dispatch(setLanguage('en', {
    some: { nested: { data: { hello: 'hello' } } }
}));
const p = getP(store.getState(), {
    polyglotScope: 'some.nested.data',
    ownPhrases: { 'some.nested.data.hello': 'Hi !' }
});
console.log(p.tc('hello')) // => will return 'Hi !'
with translate() enhancer

Instead passing only string as parameter : translate('catalog', Dummy), pass a plain object which contains polyglotScope and ownPhrases properties :

translate({
    polyglotScope : 'some.nested.data',
    ownPhrases: { 'some.nested.data.catalog': 'Cars' }
}, Dummy);
console.log(p.tc('catalog')) // => will return 'Cars'

Use polyglot options

if you want to use onMissingKey, allowMissing or warn polyglot options, you can use the createGetP factory selector to create a custom getP.

usage :

import { createGetP } from 'redux-polyglot';

const options = {
  allowMissing: true,
}

export const getP = createGetP(options);

Please note you cannot use translate hoc with a custom getP selector.

Team

These folks keep the project moving and are resources for help:

redux-polyglot's People

Contributors

guillaumearm avatar jalilarfaoui avatar jvincent42 avatar nemosupremo avatar rjerue avatar satazor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

redux-polyglot's Issues

publish on npm

depending on #3, #4, #21 and #22 .

- write a prepublish/postpublish script in package.json.
- change the 'main' property.
- create appropriate .npmignore file.
- add repository field.

  • then publish a new version on npmjs.

hold all phrases in the redux state.

Change the reducer, for keep the corresponding phrases in the redux state.
exemple :

  polyglot: {
    locale: ‘fr’,
    phrases: {
      fr: { hello: 'salut' },
      en: { hello: "hi" },
    },
  }

export all utils

export utils like is/toUpper/path/adujst in src/private/utils.js

build a single packed bundle

need #37.

  • build a single redux-polyglot.js
  • make a minified version :
    • add minified.js at the root
    • build a redux-polyglot.min.js
    • update doc

translate can take an array of string

today, translate() enhancer can select a polyglotScope giving a string.

why not give the possibility to provide many scopes in an array ?

  • all scopes are merged into one.
  • all duplicate keys will be overwritten.

Add p.append and p.prepend translator combinators

I think we could add append and prepend combinators for enhanced wording.

  • p.t is a translator
  • p.append is a combinator
  • p.append('...').t is a combined translator

examples :

p.append(' ...').tc('home.common.title');
p.prepend('-> ').tc('home.common.title');

p.prepend('-> ').append(' ...').tc('home.common.title')

In the future, we can imagine export more combinators in a new addons/redux-polyglot-combinators.

I think we should open another issue for talking about replace p.tm() by a combinator.
please see #62

Pluralization not working

I'm trying to use the Polyglot plural syntax but it doesn't seem to be working as expected... I have the following string in my bundle:

"NumberOfNetworksReporting": "%{count} network is reporting |||| %{count} networks are reporting"

If I provide an object with interpolation values, it ends up printing the whole string with both forms, including the pipe characters:

<p>{props.p.tu('NumberOfNetworksReporting', { count: 0 })}</p>
<p>{props.p.tu('NumberOfNetworksReporting', { count: 1 })}</p>
<p>{props.p.tu('NumberOfNetworksReporting', { count: 2 })}</p>

Result:
0 NETWORK IS REPORTING |||| 0 NETWORKS ARE REPORTING
1 NETWORK IS REPORTING |||| 1 NETWORKS ARE REPORTING
2 NETWORK IS REPORTING |||| 2 NETWORKS ARE REPORTING

If I use Polyglot's alternate syntax (pass a number as the second argument), it appears to be doing the proper plural phrase selection, but it doesn't substitute the actual value:

<p>{props.p.tu('NumberOfNetworksReporting', 0)}</p>
<p>{props.p.tu('NumberOfNetworksReporting', 1)}</p>
<p>{props.p.tu('NumberOfNetworksReporting', 2)}</p>

Result:
%{COUNT} NETWORKS ARE REPORTING
%{COUNT} NETWORK IS REPORTING
%{COUNT} NETWORKS ARE REPORTING

Any suggestions?
Rick

setLanguage() - v1.2 changements

We need to think about actions and the purpose of polyglotMiddleware.

I think redux-polyglot should work with the middleware ONLY.
so actions creators too.

some changes :

  • make setLanguage() a private function (don't expose it)
  • add setLocale() action creator : middleware will catch this new action, and call getPhrases() with the given locale
    • reducer should be impacted by SET_LANGUAGE, not SET_LOCALE.
  • catch SET_LOCALE in middleware and bypass the getLocale step.

that's all folks.

change translate() parameter

call translate(DummyComponent) should be forbidden.

should be call like this :

  • translate()(Dummy)
  • translate('')(Dummy)
  • translate('', Dummy)

add an example.

find a way to show a simple example for show cases with React. (maybe using JsFiddle/JsBin)

getLocale()/getPhrases() can return a falsy value.

for v1.0

need #16

this will stop the progression :

  • if getLocale() or getPhrases() return or resolve a falsy value, should return next(action)
  • add 'previousLocale' as second parameter of getLocale() to determine if the locale have been changed.

about this behavior :

  • need tests
  • need doc

getLocale/getPhrases callback can be synchronous or asynchronous.

for v1.2

today, getLocale() and getPhrases() callback signatures are:

getLocale :: Action -> String
getPhrases :: String -> Promise of Object

after a nice PR, this would be:

getLocale :: Action -> (String | Promise of String)
getPhrases :: String -> (Object | Promise of Object)

careful to be backward compatible.

peer dependencies and versions

I think we have to discuss about redux-polyglot peer dependencies.

In #91, @satazor asked why prop-types should be a peer dep, This is a very good question and we should think about it.

There are some points to cover :

  • Which dependencies should be peer
  • Which versions of this dependencies works together ?
  • Which versions of this dependencies works with redux-polyglot ?

In addition, it's can be a very good idea to test this, for example :

  • run redux-polyglot unit test suites with react@15, react@14 and so on.

Refs :

Performance concerns related to translate()

Hello!

I've been using the translate() enhancer for quite some time and, after some experiments, I concluded that all components using translate are being re-rendered after any dispatch(action).

All my components extend PureComponent which goes well with redux because it avoids re-rendering components unless props have changed. The issue here is that the p object changes every time because of this line: https://github.com/Tiqa/redux-polyglot/blob/master/src/selectors.js#L52. A new object is being created in mapStateToProps function which causes p ref to change and, consequently, causes all translated components to re-render.

The user-land solution here is to implement a custom shouldComponentUpdate function that does a shallow compare for all properties except for p, which needs to be deeply compared. While it works, it feels awkward to delegate that responsibility to devs.

Am I missing something?

new development strategy.

due to #37, we need to be careful about staging.

react-redux-polyglot use redux-polyglot as main dependency, so i think we need to work on a devbranch and follow this 3 principles :

  • this branch must be as clean as master, so npm run prepublish must be ok.
  • all master merges should be tagged by a new version. (on the master branch only)
  • all change must be validated by react-redux-polyglot unit testing before merge on master ( maybe find for an automation )

todo before close this issue :

  • create a dev branch [DONE]
  • explain this principles in redux-polyglot CONTRIBUTING.md [DONE]
  • pre-release react-redux-polyglot 0.0.1
  • write documentation for this when it's done.

note: please see react-redux-polyglot/issues/3

add optional parameter to translate enhancer

translate enhancer could take a string named 'polyglotScope' to default browse in the phrases when use a t function :

const TranslatedComponent = translate(DummyComponent, { polyglotScope: 'catalog.misc' })

but could be this :

const TranslatedComponent = translate(DummyComponent, 'catalog.misc')

if you have a phrases object like :

{
  "catalog": {
    "misc": {
      "hello": "hi"
    }
  }
}

you just need to do a p.t('hello')

Add ability to pass withRef: true to connect()

I need to be able to create a ref for an enhanced component but I can't do it since connect needs to be called with withRef. Unfortunately there's no way to do it currently. Thoughts?

Support react-redux 5.x

Currently 4.x is entered as a peer dependency. This does not really hurt, except that shrinkwrap does not like it if you're using react-redux 5 otherwise.

redux-polyglot 1.0.0

due to #77 and other issues, I think it's time to plan to do some breaking changes.

My first idea is to change the actual translate component enhancer in order to support all react-redux features.

In addition, I think that using a function named `translate' to connect a component to a redux store is a bit confusing

I propose a mapStateToProps enhancer which can be named withPolyglot :

connect(
  withPolyglot('my_scope')(mapStateToProps),
  mapDispatchToProps,
  mergeProps,
  options,
)(Component);

// example without scope
withPolyglot()(mapStateToProps);

// example without mapStateToProps;
withPolyglot('myScope')()

// example without scope and mapStateToProps
withPolyglot()();

Edit:

The usual case will be something like that :

const mapStateToProps = withPolyglot()(state => ({
  a: getA(state),
  b: getB(state),
}))

export default connect(mapStateToProps)(Component);

hold all phrases in the middleware.

for v1.0

from #8,
need close #16 before,

we want all phrases and his corresponding locales stored inside the middleware :

  • add a fourth optional parameter to createPolyglotMiddleware() :
const defaultOptions = {
  cache: false,
};
  • add a @@polyglot/SET_OPTIONS actions for dynamically set the cache mode.
  • add a setCache() action creator.

Make p a functor

conforming to #61, replace p.tmby morph combinator.

example :

const replace = (searchValue, newValue) => content => (
  content.replace(searchValue, newValue)
);

const replaceDollarsByEuros = replace(/\$/g, '€');
p.map(replaceDollarsByEuros).tc('home.common.title');

NPM warning with React 15.5.4

I am currently using React v15.5.4. Versions 15.5.0 to 15.5.3 are all deprecated due to a critical issue. However, using [email protected] causes the following warning because it has react '15.5.0' (not '^15.5.0') as a peer dependency:

[email protected] requires a peer of [email protected] but none was installed.

I believe [email protected] is the first version to import PropTypes from prop-types and not from react so this seems to be the correct (and currently only) version to use with [email protected].

So far, redux-polyglot seems to be working correctly with [email protected] but I do not know if there was a particular reason for locking in the single peer dependency version.

Monorepo

Restructure the repo so that is contains 2 subfolders : redux-polyglot and react-redux-polyglot

Add setPolyglotOptions function

from #67

I'm thinking to add a function setPolyglotOptions to redux-polyglot.
even if you can create your own getP, I want to mutate getP polyglot options.

so you can import the basic getP function and use setPolyglotOptions once at the start of your app.
This is the easy way to change polyglot options.

createGetP is the pure alternative, very useful for unit testing.

example :

import { setPolyglotOptions } from 'redux-polyglot';

const options = { allowMissing: true };
dispatch(setPolyglotOptions(options));

translate enhancer can take a mapPhrasesToProps function in parameter

As we discussed with @JalilArfaoui :

/*
const phrases = {
  catalog: {
    product: {
      counter: '%{smart_count} product |||| %{smart_count} products',
    },
  },
}
*/
const mapPhrasesToProps = (p, ownProps) => ({
    nbProducts: p.t('counter', ownProps.nbProducts), // nbProducts ownProps will be overwritten
});
const TranslatedDummy = translate(mapPhrasesToProps, { polyglotScope: 'catalog.product' })(DummyComponent);
// <TranslatedDummy nbProducts={42} />
// DummyComponent will receive a new nbProducts props as a string : '42 products'

/*
  DummyComponent.propTypes.nbProducts === string
  TranslatedComponent.propTypes.nbProducts === number
*/

remove ramda from dependencies.

remove ramda dependency.

just recode :

  • R.path
  • R.adjust
  • R.toUpper
  • R.join

don't forget to edit middleware.js and use typeof instead of R.is

note: you can edit spy() function from middleware.spec.js, now he doesn't need his constructor mutation.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.