question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Feature discussion] more robust built-in support for i18n

See original GitHub issue

What problem does this feature solve?

Let’s see the truth

I suppose that we probably would like to create agnostic to any i18n library, but let’s see the truth - it’s NOT agnostic already - antd using own tiny library to solve i18n problems and LocaleProvider is part of it.

More over, because of lack of functionalities we have to create hacks here and there, extend it with getLocale() in most components that needed it and etc.

I suggest to revise it and use external solution (I still remember time when we switched to moment for dates).

There are 3 main players on the market: i18next, react-intl and polyglot. I suggest to stick with polyglot

And here is my why:

  1. it’s small and not bloated - 2.8k minified (probably we already have half of it to solve i18n problems with own implementation)

  2. interpolation and pluralization out of box (E.g.: Pagination/Transfer already potential users of pluralization)

  3. easy to use, easy to integrate, really small API to learn

  4. it’s battle-tested and supported by Airbnb

What does the proposed API look like?

How it can helps us? (just example)

  1. we will create global variable at _utils
polyglot = new Polyglot();
  1. remove own workarounds to get i18n and replace it with something like:
polyglot.t('Table.filterTitle', {_: 'default title'});
  1. all current locale files will be same as right now - without any changes

  2. to change locale for app we will use LocaleProvider as like now but with some changes(just example):

import React, { PropTypes } from 'react';
import { polyglot } from '../utils';

/**
 * i18n provider with some shorthand helpers
 */
export class I18Provider extends React.Component {
	initLocale({ locale, messages }) {
		polyglot.locale(locale);
		polyglot.clear();
		polyglot.extend(messages);
	}

	getChildContext = () => ({
		i18n: polyglot.t.bind(polyglot)
	});

	componentWillMount() {
		this.initLocale(this.props);
	}

	componentWillReceiveProps(nextProps) {
		if (nextProps.locale !== this.props.locale) {
			this.initLocale(nextProps);
		}
	}

	render() {
		return React.Children.only(this.props.children);
	}
}

I18Provider.propTypes = {
	children: PropTypes.element.isRequired,
	locale: React.PropTypes.string.isRequired,
	messages: React.PropTypes.object.isRequired,
};

I18Provider.childContextTypes = {
	i18n: PropTypes.func.isRequired,
};

/**
 * i18n wrapper for component
 * @param WrappedComponent
 * @returns {function(*, *): XML}
 */
export const injectI18n = (WrappedComponent) => {
	function getDisplayName(component) {
		return component.displayName || component.name || 'Component';
	}

	const InjectI18n = (props, context) => <WrappedComponent {...props} i18n={context.i18n} />;

	InjectI18n.contextTypes = {
		i18n: PropTypes.func.isRequired,
	};

	InjectI18n.displayName = `InjectI18n(${getDisplayName(WrappedComponent)})`;

	return InjectI18n;
};
  1. after that anyone will have built-in support for i18n. And no any magic anymore. Just 2.8Kb to bundle and functionalities that will be enough for 99% of apps.

  2. moreover, to override built-in locale we will need to do just something like this:

polyglot.extend({ 
    Table: {
        filterTitle: 'my supper title!',
    }
);

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:15 (15 by maintainers)

github_iconTop GitHub Comments

3reactions
plandemcommented, Apr 3, 2017

I can refactor code and make PR for last version that I wrote here.

1reaction
plandemcommented, Mar 31, 2017

@benjycui it’s 1, 2 and also:

  1. LocaleProvider doesn’t support pluralization, E.g. Transfer uses singular and plural words (item/items), but in Russian we have like 5 cases for plural.

  2. If I want to create own component based on antd and make it public, then I will have to solve i18n problem by self again, because antd doesn’t provide any functionalities for it.

  3. If I want to make app to support i18n, then I will have to solve this problem again.

I just ask, why end-users must solve i18n problem again and again when antd already solved it internally with own limited solution (no pluralization)?! If antd will provide public API for i18n (just improve LocaleProvider a bit), then users will not have any problems with i18n locales in own apps in 99% cases.

Let’s recap. E.g. we upgrade LocaleProvider with these methods:

export class LocaleProvider extends React.Component {
	constructor(props) {
		this.initLocale(props);
	}

	componentWillReceiveProps(nextProps) {
		if (nextProps.locale !== this.props.locale) {
			this.initLocale(nextProps);
		}
	}

	initLocale({ locale, messages }) {
		this.polyglot = new Polyglot({ locale, phrases: messages });
	}

	getChildContext = () => ({
		antLocale: {
			t: polyglot.t.bind(this.polyglot),
			extend: polyglot.extend.bind(this.polyglot),
			clear: polyglot.clear.bind(this.polyglot),
		}
	});
}

LocaleProvider.childContextTypes = {
	antLocale: PropTypes.shape({
		t: PropTypes.func,
		extend: PropTypes.func,
		clear: PropTypes.func,
	}),
};

In this case our LocaleProvide will solve all the cases that I mentioned: 1,2,3,4,5: for 1,3,4,5 - use antLocale.t, for 2 - use antLocale.extend,

For Components we can improve injectLocale too. E.g.:

export const injectLocale = (componentName, defaultLocale) => (WrappedComponent) => {
	const defaultLanguage = 'en';
	const noLocale = (id, substitutions) => Polyglot.transformPhrase(defaultLocale[id], substitutions, defaultLanguage);

	const injectLocale = (props, { antLocale }) => (
		const locale = antLocale 
			? (id, substitutions) => antLocale.t(`${componentName}.${id}`, substitutions) 
			: noLocale;

		<WrappedComponent {...props} locale={locale}/>
	);

	injectLocale.contextTypes = {
		antLocale: PropTypes.any,
	};

	injectLocale.displayName = `injectLocale(${componentName})`;
	return injectLocale;
};

and use it like this:

class Table extends React.Component {
	render(){
		const { locale } = this.props;
		<div>{locale('filterTitle')}</div>
	}
}

export default injectLocale('Table', {
    filterTitle: 'Filter Menu',
    filterConfirm: 'OK',
    filterReset: 'Reset',
    emptyText: 'No Data',
    selectAll: 'Select All',
    selectInvert: 'Select Invert',
})(Table);

From app side user can use antLocale like this:

class App extends React.Component {
    constructor(props, context) {
       super(props, context);

       //--- to override some built-in locale
       //Table.filterTitle: 'New Filter Menu'
       context.antLocale.extend({ Table: { filterTitle: 'New Filter Menu' }});

      //--- to use i18n
      //app.button.edit: 'Edit'
      console.log(context.antLocale.t('app.button.edit'));

      //--- to use pluralization
      //app.total_cars: '%{count} car |||| %{count} cars'
      console.log(context.antLocale.t('app.total_cars', { count: 5 })); 
      
      //--- to use interpolation
      //app.hello:  'Hello %{name}!'
      console.log(context.antLocale.t('app.hello', { name: 'Peter' })); 
    }
}

App.contextTypes = {
	antLocale: PropTypes.any,
};

So as you see, in 99% cases built-in LocaleProvider will be enough in that case

Read more comments on GitHub >

github_iconTop Results From Across the Web

Rails Internationalization (I18n) API - Ruby on Rails Guides
The i18n library takes a pragmatic approach to locale keys (after some discussion), including only the locale ("language") part, like :en , :pl...
Read more >
Angular i18n: internationalization & localization with examples
In this article you will learn with examples how to get started with Angular I18n using the built-in internationalization module.
Read more >
The Best JavaScript I18n Libraries | Phrase
JavaScript land is a wonderful place to be: There are many prebuilt solutions for every general problem. Internationalization (i18n) is no ...
Read more >
Next.js i18n - How To Build a Multi-Language Website with ...
A beginners tutorial on how to build from scratch a multi-language website with Next.js i18n. Including a CodePen with the full example.
Read more >
I18N | Aurelia
Under the hood it uses i18next , which is a generalized open source library with an extensive set of features. By building on...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found