Socket
Socket
Sign inDemoInstall

terra-i18n

Package Overview
Dependencies
Maintainers
10
Versions
113
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

terra-i18n - npm Package Compare versions

Comparing version 2.2.0 to 2.3.0

bin/aggregate-translations.js

15

CHANGELOG.md

@@ -7,2 +7,17 @@ Changelog

2.3.0 - (March 30, 2018)
------------------
### Added
* Implement locale fallback strategy in translations and locale loaders. Fallback strategy will be first try the regional locale, then try the base locale, then try 'en' english base locale, and lastly throw an error if 'en' is not provided.
* Aggregate-translations pre-build tool to aggregate translations and build dynamic intlLoader and translationLoader modules that are configured for the specified locales. This tool is offered as a CLI script and as a setup function.
### Changed
* Open up the i18nLoader restrictions such that non-terra-supported locales will load.
### Fixed
* Only load the specified aggregated-translation locales.
### Added
* Exported react-intl's injectIntl and intlShape
2.2.0 - (March 6, 2018)

@@ -9,0 +24,0 @@ ------------------

1

docs/DEPENDENCIES.md

@@ -15,3 +15,2 @@ # Dependency Information

| babel-jest | ^19.0.0 | -- | [Babel](https://github.com/babel/babel) [jest](https://github.com/facebook/jest) plugin |
| terra-props-table | ^2.1.0 | ^16.2.0 | terra-props-table |

@@ -18,0 +17,0 @@ ## peerDependencies

# Terra I18n
The terra-i18n component provides the internationalization to the React component. Terra supports the following locales: 'de' 'es' 'en' 'en-US' 'en-GB', 'fi-FI', 'fr', 'pt'. All locales related files are loading on demand.
The terra-i18n package provides internationalization for React components by loading translations and locale data on demand and providing the translated messages the the component. It does this by utilizing the [`react-intl`](https://github.com/yahoo/react-intl) dependency to provide the formatted translation messages to the supplied React children. To enable this behavior, terra-i18n provides the `i18nLoader` and `I18nProvider` components.
### i18nLoader
The `i18nLoader` component guarantees that the intl polyfill, locale data and translation messages are loaded before the translation-needing component is rendered. This loader should be utilized only once within an application, because all internationalization information is loaded into memory to remove the need to dynamically load locale data on the server.
Note: the `i18nLoader` state object for the callback must containing the following keys to work properly:
- `areTranslationsLoaded` - boolean
- `locale` - string
- `messages` - key-value pairs such that the key is the message name and the value is the translation message
### I18nProvider
The `I18nProvider` component configures the react-intl's `IntlProvider` and supplies it with the translation-needing components such that the i18n context is accessible. Usually, one `I18nProvider` will wrap an application's root component such that the entire application is within the same configured i18n context, however it is possible to render nested `I1n8nProvider` components to provide different, or modified i18n context.
### Supported Locales
| Locale ID | Locale |
|-|-|
| ar | Arabic |
| en | English |
| en-US | English - United States |
| en-GB | English - Great Britain |
| es | Spanish |
| es-US | Spanish - United States |
| es-ES | Spanish - Spain (Traditional) |
| de | German |
| fi-FI | Finnish - Finland |
| fr | French |
| fr-FR | French - France|
| pt | Portuguese |
| pt-BR | Portuguese - Brazil|
#### Non-Supported Locales
It is possible to add and load non-supported locales with `terra-i18n`, however one must ensure the locales are supported by `react-intl`, otherwise no locale-date will exist and loading the intl data will result in an error. Once confirmed that the locale is supported by `react-intl`, one is responsible for including the appropriate translations messages for each terra component used in your application, otherwise the translations will fail and `react-intl` will display the message name as the fallback.
### Locale Fallback
The `i18nLoader` loads internationalized information from the translationLoader and intlLoader modules, both which utilize a locale fallback strategy. This implemented fallback strategy when loading a locale is:
1. Try the regional locale (if applicable)
2. Try the base locale
3. Try the 'en' base locale
4. Throw an error if 'en' is not provided
**Note:** This fallback strategy is only applied when an aggregated translation file does not exist. For example if the 'es' locale is loaded and the translation for `Terra.button.close` is missing, `react-intl` will display the `Terra.button.close` message name, not the english translation, because locale data was only loaded for 'es'. The `terra-i18n` package does not support loading multiple locales at once. This ensures an application will never have a mix of 'es' and 'en' translations.
### Aggregating Translations
To successfully render an internationalized component, all translation information must be provided as a single file for each locale. The terra-i18n package provides the `aggregate-translations` pre-build tool to assist with creating the translation, intl loader and translation loader files that are configured for the specified locales. This tool is offered as a CLI script and as a setup function.
See the [aggregate-translations documentation](https://github.com/cerner/terra-core/blob/master/packages/terra-i18n/docs/AggregateTranslations.md).
## Getting Started
- Install with [npmjs](https://www.npmjs.com):
It is recommended to use the `terra-base` component implementation; `terra-base` will handle locale changes, manage the locale loading state and receive customized translation messages from an application and pass them into the `I18nProvider`. See [`terra-base documentation`](https://github.com/cerner/terra-core/tree/master/packages/terra-base) to get started.
However, terra-i18n can be installed with [npmjs](https://www.npmjs.com):
- `npm install terra-i18n`

@@ -13,2 +67,5 @@ - `yarn add terra-i18n`

The best example is the `terra-base` [implementation](https://github.com/cerner/terra-core/blob/master/packages/terra-base/src/Base.jsx)
. Which can be summarized in the following example as:
```jsx

@@ -30,2 +87,3 @@ import React from 'react';

componentDidMount() {
// Load the internationalization information
i18nLoader(this.state.locale, this.setState, this);

@@ -35,4 +93,4 @@ }

render() {
// Do not render components or the provider until translations and other
// dependencies are loaded.
// Do not render components or the provider until translations and locale
// data are loaded.
if (!this.state.areTranslationsLoaded) {

@@ -42,2 +100,3 @@ return null;

// Render the internationalized components
return (

@@ -52,12 +111,7 @@ <I18nProvider locale={this.state.locale} messages={this.state.messages}>

Note that the state of the object needs to contain keys as follows for the i18nLoader callback to work properly:
## Creating Internationalized Components
```js
{
areTranslationsLoaded: false,
locale: props.locale,
messages: {},
}
```
Once an application is setup to support internationalization, the next step is to create internationalized components and provide the translation messages. Use [this documentation](https://github.com/cerner/terra-core/wiki/InternationalizeGuide) as a guide for integrating internationalization into an application.
## Component Features

@@ -67,1 +121,3 @@ * [Cross-Browser Support](https://github.com/cerner/terra-core/wiki/Component-Features#cross-browser-support)

* [Localization Support](https://github.com/cerner/terra-core/wiki/Component-Features#localization-support)
* [react_on_rails Compatible](https://github.com/shakacode/react_on_rails/blob/8cb06ed35cb5c2c453bcc193282b4c091574c1b7/docs/basics/i18n.md#how-to-add-i18n)
* [CND Compatible](https://github.com/webpack/docs/wiki/configuration#outputpublicpath)
'use strict';
var _reactIntl = require('react-intl');
var _I18nProvider = require('./I18nProvider');

@@ -15,3 +17,5 @@

I18nProvider: _I18nProvider2.default,
i18nLoader: _i18nLoader2.default
i18nLoader: _i18nLoader2.default,
injectIntl: _reactIntl.injectIntl,
intlShape: _reactIntl.intlShape
};

@@ -7,5 +7,5 @@ 'use strict';

var _translationLoaders = require('./translationLoaders');
var _translationsLoaders = require('./translationsLoaders');
var _translationLoaders2 = _interopRequireDefault(_translationLoaders);
var _translationsLoaders2 = _interopRequireDefault(_translationsLoaders);

@@ -22,4 +22,4 @@ var _i18nSupportedLocales = require('./i18nSupportedLocales');

var permitParams = function permitParams(locale, callback) {
if (_i18nSupportedLocales2.default.indexOf(locale) < 0) {
throw new Error(locale + ' is not supported, supported locales:' + _i18nSupportedLocales2.default);
if (process.env.NODE_ENV !== 'production' && _i18nSupportedLocales2.default.indexOf(locale) < 0) {
console.warn(locale + ' is not a supported locale, supported locales include: ' + _i18nSupportedLocales2.default.join(', ') + '.');
}

@@ -36,8 +36,8 @@ if (typeof callback !== 'function') {

require('intl');
_intlLoaders2.default[locale]();
_translationLoaders2.default[locale](callback, scope);
(0, _intlLoaders2.default)(locale);
(0, _translationsLoaders2.default)(locale, callback, scope);
}, 'intl-polyfill');
} else {
_translationLoaders2.default[locale](callback, scope);
(0, _translationsLoaders2.default)(locale, callback, scope);
}
};
'use strict';
var loadArIntl = function loadArIntl() {
return require.ensure([], function (require) {
return require('intl/locale-data/jsonp/ar.js');
}, 'ar-intl-local');
};
var _intlLoaders = require('intlLoaders');
var loadEnIntl = function loadEnIntl() {
return require.ensure([], function (require) {
return require('intl/locale-data/jsonp/en.js');
}, 'en-intl-local');
};
var _intlLoaders2 = _interopRequireDefault(_intlLoaders);
var loadEnGBIntl = function loadEnGBIntl() {
return require.ensure([], function (require) {
return require('intl/locale-data/jsonp/en-GB.js');
}, 'en-GB-intl-local');
};
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var loadEnUSIntl = function loadEnUSIntl() {
return require.ensure([], function (require) {
return require('intl/locale-data/jsonp/en-US.js');
}, 'en-US-intl-local');
};
var loadFallbackIntl = function loadFallbackIntl(localeContext) {
try {
_intlLoaders2.default['en']();
if (process.env.NODE_ENV !== 'production') {
console.warn('Locale data was not supplied for the ' + localeContext + '. Using en data as the fallback locale data.');
}
} catch (e) {
throw new Error('Locale data was not supplied for the ' + localeContext + ', or the en fallback locale.');
}
}; /* eslint-disable */
var loadDeIntl = function loadDeIntl() {
return require.ensure([], function (require) {
return require('intl/locale-data/jsonp/de.js');
}, 'de-intl-local');
};
var loadPtIntl = function loadPtIntl() {
return require.ensure([], function (require) {
return require('intl/locale-data/jsonp/pt.js');
}, 'pt-intl-local');
var loadIntl = function loadIntl(locale) {
var fallbackLocale = locale.split('-').length > 1 ? locale.split('-')[0] : false;
try {
_intlLoaders2.default[locale]();
} catch (e) {
if (fallbackLocale) {
try {
_intlLoaders2.default[fallbackLocale]();
if (process.env.NODE_ENV !== 'production') {
console.warn('Locale data was not supplied for the ' + locale + ' locale. Using ' + fallbackLocale + ' data as the fallback locale data.');
}
} catch (e) {
var localeContext = locale + ' or ' + fallbackLocale + ' locales';
loadFallbackIntl(localeContext);
}
} else {
var _localeContext = locale + ' locale';
loadFallbackIntl(_localeContext);
}
}
};
var loadPtBRIntl = function loadPtBRIntl() {
return require.ensure([], function (require) {
return require('intl/locale-data/jsonp/pt-BR.js');
}, 'pt-BR-intl-local');
};
var loadFrIntl = function loadFrIntl() {
return require.ensure([], function (require) {
return require('intl/locale-data/jsonp/fr.js');
}, 'fr-intl-local');
};
var loadFrFRIntl = function loadFrFRIntl() {
return require.ensure([], function (require) {
return require('intl/locale-data/jsonp/fr-FR.js');
}, 'fr-FR-intl-local');
};
var loadEsIntl = function loadEsIntl() {
return require.ensure([], function (require) {
return require('intl/locale-data/jsonp/es.js');
}, 'es-intl-local');
};
var loadEsUSIntl = function loadEsUSIntl() {
return require.ensure([], function (require) {
return require('intl/locale-data/jsonp/es-US.js');
}, 'es-US-intl-local');
};
var loadEsESIntl = function loadEsESIntl() {
return require.ensure([], function (require) {
return require('intl/locale-data/jsonp/es-ES.js');
}, 'es-ES-intl-local');
};
var loadFiFIIntl = function loadFiFIIntl() {
return require.ensure([], function (require) {
return require('intl/locale-data/jsonp/fi-FI.js');
}, 'fi-FI-intl-local');
};
var intlLoaders = {
ar: loadArIntl,
en: loadEnIntl,
'en-GB': loadEnGBIntl,
'en-US': loadEnUSIntl,
de: loadDeIntl,
pt: loadPtIntl,
'pt-BR': loadPtBRIntl,
fr: loadFrIntl,
'fr-FR': loadFrFRIntl,
es: loadEsIntl,
'es-US': loadEsUSIntl,
'es-ES': loadEsESIntl,
'fi-FI': loadFiFIIntl
};
module.exports = intlLoaders;
module.exports = loadIntl;
{
"name": "terra-i18n",
"main": "lib/I18n.js",
"version": "2.2.0",
"description": "The terra-i18n component provides the internationalization to the React component. Terra supports the following locales: 'de' 'es' 'en' 'en-US' 'en-GB', 'fi-FI', 'fr', 'pt'. All locales related files are loading on demand.",
"version": "2.3.0",
"description": "The terra-i18n package provides on-demand internationalization of React components.",
"repository": {

@@ -24,4 +24,8 @@ "type": "git",

"devDependencies": {
"babel-jest": "^19.0.0"
"babel-jest": "^19.0.0",
"memory-fs": "^0.4.1"
},
"bin": {
"aggregate-translations": "./bin/aggregate-translations.js"
},
"peerDependencies": {

@@ -33,3 +37,7 @@ "react": "^16.2.0",

"classnames": "^2.2.5",
"commander": "^2.9.0",
"fs-extra": "^5.0.0",
"glob": "^7.1.1",
"intl": "^1.2.5",
"lodash.startcase": "^4.4.0",
"prop-types": "^15.5.8",

@@ -44,6 +52,7 @@ "react-intl": "^2.4.0"

"lint:js": "eslint --ext .js,.jsx . --ignore-path ../../.eslintignore",
"test": "npm run test:jest && npm run test:nightwatch",
"postinstall": "node scripts/post-install/write-default-loaders.js",
"test": "npm run test:jest && npm run test:wdio",
"test:jest": "jest ./tests/jest/* --config ../../jestconfig.json",
"test:nightwatch": "nightwatch -c ../../nightwatch.conf.js"
"test:wdio": "wdio ../../wdio.conf.js"
}
}

@@ -7,3 +7,3 @@ # Terra I18n

The terra-i18n component provides the internationalization to the React component. Terra supports the following locales: 'de' 'es' 'en' 'en-US' 'en-GB', 'fi-FI', 'fr', 'pt'. All locales related files are loading on demand.
The terra-i18n package provides on-demand internationalization of React components. Additionally, it provides an `aggregate-translations` pre-build tool to aggregate translations and build dynamic intlLoader and translationLoader modules.

@@ -16,10 +16,12 @@ - [Getting Started](#getting-started)

You don't need to install this package directly. Please follow [this internationalization section](../../README.md#internationalizationi18n) to set up. `Base` will install this package for you.
- Install from [npmjs](https://www.npmjs.com): `npm install terra-i18n`
You can check [terra-i18n wiki page](https://github.com/cerner/terra-core/wiki/terra-i18n-Guide) for more information and [FAQ](https://github.com/cerner/terra-core/wiki/terra-i18n-Guide#faq).
It is recommended to use the `terra-base` component implementation; `terra-base` will handle locale changes, manage the locale loading state and receive customized translation messages from an application and pass them into the `I18nProvider`. See [`terra-base documentation`](https://github.com/cerner/terra-core/tree/master/packages/terra-base) to get started.
However, terra-i18n can be installed with [npmjs](https://www.npmjs.com):
- `npm install terra-i18n`
- `yarn add terra-i18n`
## Documentation
Documentation for this component is spilt into individual files.
One file for the main component and one file for each component modifier.
Documentation for this component is spilt into multiple files.

@@ -26,0 +28,0 @@ See the [documentation](docs/).

@@ -0,1 +1,2 @@

import { injectIntl, intlShape } from 'react-intl';
import I18nProvider from './I18nProvider';

@@ -7,2 +8,4 @@ import i18nLoader from './i18nLoader';

i18nLoader,
injectIntl,
intlShape,
};
/* eslint-disable */
import intlLoaders from './intlLoaders';
import translationLoaders from './translationLoaders';
import loadIntl from './intlLoaders';
import loadTranslations from './translationsLoaders';

@@ -10,4 +10,4 @@ import supportedLocales from './i18nSupportedLocales';

const permitParams = (locale, callback) => {
if (supportedLocales.indexOf(locale) < 0) {
throw new Error(`${locale} is not supported, supported locales:${supportedLocales}`);
if (process.env.NODE_ENV !== 'production' && supportedLocales.indexOf(locale) < 0) {
console.warn(`${locale} is not a supported locale, supported locales include: ${supportedLocales.join(', ')}.`);
}

@@ -24,8 +24,8 @@ if (typeof (callback) !== 'function') {

require('intl');
intlLoaders[locale]();
translationLoaders[locale](callback, scope);
loadIntl(locale);
loadTranslations(locale, callback, scope);
}, 'intl-polyfill');
} else {
translationLoaders[locale](callback, scope);
loadTranslations(locale, callback, scope);
}
};

@@ -1,82 +0,37 @@

const loadArIntl = () =>
require.ensure([],
require => require('intl/locale-data/jsonp/ar.js'),
'ar-intl-local');
/* eslint-disable */
import intlLoaders from 'intlLoaders';
const loadEnIntl = () =>
require.ensure([],
require => require('intl/locale-data/jsonp/en.js'),
'en-intl-local');
const loadFallbackIntl = (localeContext) => {
try {
intlLoaders['en']();
if (process.env.NODE_ENV !== 'production') {
console.warn(`Locale data was not supplied for the ${localeContext}. Using en data as the fallback locale data.`);
}
} catch (e) {
throw new Error(`Locale data was not supplied for the ${localeContext}, or the en fallback locale.`);
}
}
const loadEnGBIntl = () =>
require.ensure([],
require => require('intl/locale-data/jsonp/en-GB.js'),
'en-GB-intl-local');
const loadEnUSIntl = () =>
require.ensure([],
require => require('intl/locale-data/jsonp/en-US.js'),
'en-US-intl-local');
const loadDeIntl = () =>
require.ensure([],
require => require('intl/locale-data/jsonp/de.js'),
'de-intl-local');
const loadPtIntl = () =>
require.ensure([],
require => require('intl/locale-data/jsonp/pt.js'),
'pt-intl-local');
const loadPtBRIntl = () =>
require.ensure([],
require => require('intl/locale-data/jsonp/pt-BR.js'),
'pt-BR-intl-local');
const loadFrIntl = () =>
require.ensure([],
require => require('intl/locale-data/jsonp/fr.js'),
'fr-intl-local');
const loadFrFRIntl = () =>
require.ensure([],
require => require('intl/locale-data/jsonp/fr-FR.js'),
'fr-FR-intl-local');
const loadEsIntl = () =>
require.ensure([],
require => require('intl/locale-data/jsonp/es.js'),
'es-intl-local');
const loadEsUSIntl = () =>
require.ensure([],
require => require('intl/locale-data/jsonp/es-US.js'),
'es-US-intl-local');
const loadEsESIntl = () =>
require.ensure([],
require => require('intl/locale-data/jsonp/es-ES.js'),
'es-ES-intl-local');
const loadFiFIIntl = () =>
require.ensure([],
require => require('intl/locale-data/jsonp/fi-FI.js'),
'fi-FI-intl-local');
const intlLoaders = {
ar: loadArIntl,
en: loadEnIntl,
'en-GB': loadEnGBIntl,
'en-US': loadEnUSIntl,
de: loadDeIntl,
pt: loadPtIntl,
'pt-BR': loadPtBRIntl,
fr: loadFrIntl,
'fr-FR': loadFrFRIntl,
es: loadEsIntl,
'es-US': loadEsUSIntl,
'es-ES': loadEsESIntl,
'fi-FI': loadFiFIIntl,
const loadIntl = (locale) => {
const fallbackLocale = locale.split('-').length > 1 ? locale.split('-')[0] : false;
try {
intlLoaders[locale]();
} catch (e) {
if(fallbackLocale) {
try {
intlLoaders[fallbackLocale]();
if (process.env.NODE_ENV !== 'production') {
console.warn(`Locale data was not supplied for the ${locale} locale. Using ${fallbackLocale} data as the fallback locale data.`);
}
} catch (e) {
const localeContext = `${locale} or ${fallbackLocale} locales`;
loadFallbackIntl(localeContext);
}
} else {
const localeContext = `${locale} locale`;
loadFallbackIntl(localeContext);
}
}
};
module.exports = intlLoaders;
module.exports = loadIntl;

@@ -0,14 +1,41 @@

/* eslint-disable no-console */
import i18nLoader from '../../src/i18nLoader';
global.console = { warn: jest.fn() };
describe('i18nLoader', () => {
describe('when locale is not supported', () => {
it('throws error', () => {
const invalidLocale = () => i18nLoader('invalidLocale', jest.fn());
beforeEach(() => {
console.warn.mockClear();
});
expect(invalidLocale).toThrowErrorMatchingSnapshot();
describe('permitParams', () => {
it('logs a warning message when locale is not supported', () => {
expect(() => i18nLoader('cy', jest.fn())).toThrowError();
expect(console.warn).toBeCalledWith(expect.stringContaining('cy is not a supported locale, supported locales include:'));
});
it('throws error when callback is not function', () => {
const invalidCallback = () => i18nLoader('en');
expect(invalidCallback).toThrowErrorMatchingSnapshot();
});
});
describe('when callback is not function', () => {
it('throws error', () => {
describe('permitParams - prod environment', () => {
beforeEach(() => {
process.env.NODE_ENV = 'production';
});
afterEach(() => {
delete process.env.NODE_ENV;
});
it('does not log a warning message when locale is not supported', () => {
expect(() => i18nLoader('cy', jest.fn())).toThrowError();
expect(console.warn).not.toHaveBeenCalled();
});
it('still throws error when callback is not function', () => {
const invalidCallback = () => i18nLoader('en');

@@ -15,0 +42,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc