Skip to content
This repository has been archived by the owner on Jul 9, 2018. It is now read-only.

Commit

Permalink
WordPress i18n package: Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad committed Mar 22, 2018
1 parent 138c958 commit ec9d4b4
Show file tree
Hide file tree
Showing 8 changed files with 912 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/i18n/.npmrc
@@ -0,0 +1 @@
package-lock=false
82 changes: 82 additions & 0 deletions packages/i18n/README.md
@@ -0,0 +1,82 @@
i18n
======

Internationalization utilities for client-side localization.

https://codex.wordpress.org/I18n_for_WordPress_Developers


## Installation

Install the module:

```bash
npm install @wordpress/i18n --save-dev
```

```js
import { sprintf, _n } from '@wordpress/i18n';

sprintf( _n( '%d hat', '%d hats', 4, 'text-domain' ), 4 );
// 4 hats
```

Note that you will not need to specify [domain](https://codex.wordpress.org/I18n_for_WordPress_Developers#Text_Domains) for the strings.

## Build

Included is a [custom Babel plugin](./tools/babel-plugin.js) which, when integrated into a Babel configuration, will scan all processed JavaScript files for use of localization functions. It then compiles these into a [gettext POT formatted](https://en.wikipedia.org/wiki/Gettext) file as a template for translation. By default the output file will be written to `gettext.pot` of the root project directory. This can be overridden using the `"output"` option of the plugin:

```json
[ "babel-plugin-wp-i18n", {
"output": "languages/myplugin.pot"
} ]
```

The package also includes a `pot-to-php` script used to generate a php files containing the messages listed in a `.pot` file. This is usefull to trick WordPress.org translation strings discovery since at the moment, WordPress.org is not capable of parsing strings directly from JavaScript files.

```sh
node tools/pot-to-php languages/myplugin.pot languages/myplugin-translations.php text-domain
```

## API

`__( text: string, domain: string ): string`

Retrieve the translation of text.

See: https://developer.wordpress.org/reference/functions/__/

`_x( text: string, context: string, domain: string ): string`

Retrieve translated string with gettext context.

See: https://developer.wordpress.org/reference/functions/_x/

`_n( single: string, plural: string, number: Number, domain: string ): string`

Translates and retrieves the singular or plural form based on the supplied number.

See: https://developer.wordpress.org/reference/functions/_n/

`_nx( single: string, plural: string, number: Number, context: string, domain: string ): string`

Translates and retrieves the singular or plural form based on the supplied number, with gettext context.

See: https://developer.wordpress.org/reference/functions/_nx/

`sprintf( format: string, ...args: mixed[] ): string`

Returns a formatted string.

See: http://www.diveintojavascript.com/projects/javascript-sprintf

`setLocaleData( data: Object, domain: string )`

Creates a new Jed instance with specified locale data configuration.

`getI18n(): Jed`

Returns the current Jed instance, initializing with a default configuration if not already assigned.

See: http://messageformat.github.io/Jed/
33 changes: 33 additions & 0 deletions packages/i18n/package.json
@@ -0,0 +1,33 @@
{
"name": "@wordpress/i18n",
"version": "1.0.0-beta.0",
"description": "WordPress i18n library",
"author": "WordPress",
"license": "GPL-2.0-or-later",
"keywords": [
"i18n"
],
"homepage": "https://github.com/WordPress/packages/tree/master/packages/i18n/README.md",
"repository": {
"type": "git",
"url": "https://github.com/WordPress/packages.git"
},
"bugs": {
"url": "https://github.com/WordPress/packages/issues"
},
"main": "build/index.js",
"module": "build-module/index.js",
"publishConfig": {
"access": "public"
},
"dependencies": {
"gettext-parser": "^1.3.1",
"jed": "^1.1.1",
"lodash": "^4.17.5",
"memize": "^1.0.5"
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-traverse": "^6.26.0"
}
}
163 changes: 163 additions & 0 deletions packages/i18n/src/index.js
@@ -0,0 +1,163 @@
/**
* External dependencies
*/
import Jed from 'jed';
import memoize from 'memize';

let i18n;

/**
* Log to console, once per message; or more precisely, per referentially equal
* argument set. Because Jed throws errors, we log these to the console instead
* to avoid crashing the application.
*
* @param {...*} args Arguments to pass to `console.error`
*/
const logErrorOnce = memoize( console.error ); // eslint-disable-line no-console

/**
* Merges locale data into the Jed instance by domain. Creates a new Jed
* instance if one has not yet been assigned.
*
* @see http://messageformat.github.io/Jed/
*
* @param {?Object} localeData Locale data configuration.
* @param {?string} domain Domain for which configuration applies.
*/
export function setLocaleData( localeData = { '': {} }, domain = 'default' ) {
if ( ! i18n ) {
i18n = new Jed( {
domain: 'default',
locale_data: {
default: {},
},
} );
}

i18n.options.locale_data[ domain ] = localeData;
}

/**
* Returns the current Jed instance, initializing with a default configuration
* if not already assigned.
*
* @return {Jed} Jed instance.
*/
export function getI18n() {
if ( ! i18n ) {
setLocaleData();
}

return i18n;
}

/**
* Wrapper for Jed's `dcnpgettext`, its most qualified function. Absorbs errors
* which are thrown as the result of invalid translation.
*
* @param {?string} domain Domain to retrieve the translated text.
* @param {?string} context Context information for the translators.
* @param {string} single Text to translate if non-plural. Used as fallback
* return value on a caught error.
* @param {?string} plural The text to be used if the number is plural.
* @param {?number} number The number to compare against to use either the
* singular or plural form.
*
* @return {string} The translated string.
*/
export function dcnpgettext( domain = 'default', context, single, plural, number ) {
try {
return getI18n().dcnpgettext( domain, context, single, plural, number );
} catch ( error ) {
logErrorOnce( 'Jed localization error: \n\n' + error.toString() );

return single;
}
}

/**
* Retrieve the translation of text.
*
* @see https://developer.wordpress.org/reference/functions/__/
*
* @param {string} text Text to translate.
* @param {?string} domain Domain to retrieve the translated text.
*
* @return {string} Translated text.
*/
export function __( text, domain ) {
return dcnpgettext( domain, undefined, text );
}

/**
* Retrieve translated string with gettext context.
*
* @see https://developer.wordpress.org/reference/functions/_x/
*
* @param {string} text Text to translate.
* @param {string} context Context information for the translators.
* @param {?string} domain Domain to retrieve the translated text.
*
* @return {string} Translated context string without pipe.
*/
export function _x( text, context, domain ) {
return dcnpgettext( domain, context, text );
}

/**
* Translates and retrieves the singular or plural form based on the supplied
* number.
*
* @see https://developer.wordpress.org/reference/functions/_n/
*
* @param {string} single The text to be used if the number is singular.
* @param {string} plural The text to be used if the number is plural.
* @param {number} number The number to compare against to use either the
* singular or plural form.
* @param {?string} domain Domain to retrieve the translated text.
*
* @return {string} The translated singular or plural form.
*/
export function _n( single, plural, number, domain ) {
return dcnpgettext( domain, undefined, single, plural, number );
}

/**
* Translates and retrieves the singular or plural form based on the supplied
* number, with gettext context.
*
* @see https://developer.wordpress.org/reference/functions/_nx/
*
* @param {string} single The text to be used if the number is singular.
* @param {string} plural The text to be used if the number is plural.
* @param {number} number The number to compare against to use either the
* singular or plural form.
* @param {string} context Context information for the translators.
* @param {?string} domain Domain to retrieve the translated text.
*
* @return {string} The translated singular or plural form.
*/
export function _nx( single, plural, number, context, domain ) {
return dcnpgettext( domain, context, single, plural, number );
}

/**
* Returns a formatted string. If an error occurs in applying the format, the
* original format string is returned.
*
* @param {string} format The format of the string to generate.
* @param {string[]} ...args Arguments to apply to the format.
*
* @see http://www.diveintojavascript.com/projects/javascript-sprintf
*
* @return {string} The formatted string.
*/
export function sprintf( format, ...args ) {
try {
return Jed.sprintf( format, ...args );
} catch ( error ) {
logErrorOnce( 'Jed sprintf error: \n\n' + error.toString() );

return format;
}
}
28 changes: 28 additions & 0 deletions packages/i18n/src/test/index.js
@@ -0,0 +1,28 @@
/**
* Internal dependencies
*/
import { dcnpgettext, sprintf } from '../';

// Mock memoization as identity function. Inline since Jest errors on out-of-
// scope references in a mock callback.
jest.mock( 'memize', () => ( fn ) => fn );

describe( 'i18n', () => {
describe( 'dcnpgettext()', () => {
it( 'absorbs errors', () => {
const result = dcnpgettext( 'domain-without-data', undefined, 'Hello' );

expect( console ).toHaveErrored();
expect( result ).toBe( 'Hello' );
} );
} );

describe( 'sprintf()', () => {
it( 'absorbs errors', () => {
const result = sprintf( 'Hello %(placeholder-not-provided)s' );

expect( console ).toHaveErrored();
expect( result ).toBe( 'Hello %(placeholder-not-provided)s' );
} );
} );
} );

0 comments on commit ec9d4b4

Please sign in to comment.