Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

node-gettext

Package Overview
Dependencies
Maintainers
2
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

node-gettext - npm Package Compare versions

Comparing version 2.0.0-rc.0 to 2.0.0-rc.1

79

lib/gettext.js

@@ -12,3 +12,3 @@ 'use strict';

* @constructor
* @param {Object} options A set of options
* @param {Object} [options] A set of options
* @param {Boolean} options.debug Whether to output debug info into the

@@ -22,3 +22,3 @@ * console.

this.catalogs = {};
this.locale = null;
this.locale = '';
this.domain = 'messages';

@@ -71,2 +71,3 @@

*
* @private
* @param {String} eventName An event name

@@ -126,10 +127,16 @@ * @param {any} eventData Data to pass to event listeners

Gettext.prototype.setLocale = function(locale) {
if (!locale) {
this.warn('You called setLocale() with an empty value, which makes little sense.');
if (typeof locale !== 'string') {
this.warn(
'You called setLocale() with an argument of type ' + (typeof locale) + '. ' +
'The locale must be a string.'
);
return;
}
if (locale.trim() === '') {
this.warn('You called setLocale() with an empty value, which makes little sense.');
}
if (!this.catalogs[locale]) {
this.warn('You called setLocale() with "' + locale + '", but no translations for that locale has been added.');
return;
}

@@ -149,7 +156,14 @@

Gettext.prototype.setTextDomain = function(domain) {
if (!domain) {
this.warn('You called setTextDomain() with an empty `domain` value, which is not allowed.');
if (typeof domain !== 'string') {
this.warn(
'You called setTextDomain() with an argument of type ' + (typeof domain) + '. ' +
'The domain must be a string.'
);
return;
}
if (domain.trim() === '') {
this.warn('You called setTextDomain() with an empty `domain` value.');
}
this.domain = domain;

@@ -285,7 +299,2 @@ };

if (!this.locale) {
this.warn('You need to set a locale using setLocale(locale) before getting translated messages.');
return defaultTranslation;
}
translation = this._getTranslation(domain, msgctxt, msgid);

@@ -320,2 +329,3 @@

*
* @private
* @param {String} domain A gettext domain name

@@ -359,3 +369,4 @@ * @param {String} msgctxt Translation context

*
* @param {String} locale A case-insensitive locale string
* @private
* @param {String} locale A case-insensitive locale string
* @returns {String} A language code

@@ -367,19 +378,33 @@ */

/* C-style aliases */
/**
* This function will be removed in the final 2.0.0 release.
* C-style alias for [setTextDomain](#gettextsettextdomaindomain)
*
* @deprecated
* @see Gettext#setTextDomain
*/
Gettext.prototype.addTextdomain = function() {
Gettext.prototype.textdomain = function(domain) {
if (this.debug) {
console.warn('textdomain(domain) was used to set locales in node-gettext v1. ' +
'Make sure you are using it for domains, and switch to setLocale(locale) if you are not.\n\n ' +
'To read more about the migration from node-gettext v1 to v2, ' +
'see https://github.com/alexanderwallin/node-gettext/#migrating-from-1x-to-2x\n\n' +
'This warning will be removed in the final 2.0.0');
}
// TODO(alexanderwallin): Add instructions for file i/o
console.error('addTextdomain() is deprecated.\n\n' +
'* To add translations, use addTranslations()\n' +
'* To set the default domain, use setTextDomain()\n' +
'\n' +
'To read more about the migration from node-gettext v1 to v2, ' +
'see https://github.com/alexanderwallin/node-gettext/#migrating-from-1x-to-2x');
this.setTextDomain(domain);
};
/**
* C-style alias for [setLocale](#gettextsetlocalelocale)
*
* @see Gettext#setLocale
*/
Gettext.prototype.setlocale = function(locale) {
this.setLomain(locale);
};
/* Deprecated functions */
/**
* This function will be removed in the final 2.0.0 release.

@@ -389,6 +414,6 @@ *

*/
Gettext.prototype.textdomain = function() {
console.error('textdomain() is deprecated.\n\n' +
'* To set the current locale, use setLocale()\n' +
'* To set the default domain, use setTextDomain()\n' +
Gettext.prototype.addTextdomain = function() {
console.error('addTextdomain() is deprecated.\n\n' +
'* To add translations, use addTranslations()\n' +
'* To set the default domain, use setTextDomain() (or its alias textdomain())\n' +
'\n' +

@@ -395,0 +420,0 @@ 'To read more about the migration from node-gettext v1 to v2, ' +

{
"name": "node-gettext",
"description": "A JavaScript implementation of gettext, a localization framework",
"version": "2.0.0-rc.0",
"version": "2.0.0-rc.1",
"author": "Andris Reinman",

@@ -41,3 +41,3 @@ "maintainers": [

"grunt-mocha-test": "^0.12.7",
"jsdoc-to-markdown": "^2.0.1",
"jsdoc-to-markdown": "^3.0.0",
"mocha": "^2.4.5",

@@ -44,0 +44,0 @@ "sinon": "^1.17.7"

@@ -13,3 +13,3 @@

**`node-gettext`** is a JavaScript implementation of [gettext](https://www.gnu.org/software/gettext/gettext.html), a localization framework.
**`node-gettext`** is a JavaScript implementation of (a large subset of) [gettext](https://www.gnu.org/software/gettext/gettext.html), a localization framework originally written in C.

@@ -21,6 +21,9 @@ If you just want to parse or compile mo/po files, check out [gettext-parser](https://github.com/smhg/gettext-parser).

* [Features](#features)
* [Differences from GNU gettext](#differences-from-gnu-gettext)
* [Installation](#installation)
* [Usage](#usage)
* [Error events](#error-events)
* [Recipes](#recipes)
* [API](#api)
* [Migrating from v1 to v2](#migrating-from-v1-to-v2)
* [API](#api)
* [License](#license)

@@ -40,2 +43,14 @@ * [See also](#see-also)

### Differences from GNU gettext
There are two main differences between `node-gettext` and GNU's gettext:
1. **There are no categories.** GNU gettext features [categories such as `LC_MESSAGES`, `LC_NUMERIC` and `LC_MONETARY`](https://www.gnu.org/software/gettext/manual/gettext.html#Locale-Environment-Variables), but since there already is a plethora of great JavaScript libraries to deal with numbers, currencies, dates etc, `node-gettext` is simply targeted towards strings/phrases. You could say it just assumes the `LC_MESSAGES` category at all times.
2. **You have to read translation files from the file system yourself.** GNU gettext is a C library that reads files from the file system. This is done using `bindtextdomain(domain, localesDirPath)` and `setlocale(category, locale)`, where these four parameters combined are used to read the appropriate translations file.
However, since `node-gettext` needs to work both on the server in web browsers (which usually is referred to as it being *universal* or *isomorphic* JavaScript), it is up to the developer to read translation files from disk or somehow provide it with translations as pure JavaScript objects using [`addTranslations(locale, domain, translations)`](#gettextsetlocalelocale).
`bindtextdomain` will be provided as an optional feature in a future release.
## Installation

@@ -72,19 +87,35 @@

### Recipes
## Migrating from v1 to v2
#### Load and add translations from .mo or .po files
Version 1 of `node-gettext` confused domains with locales, which version 2 has corrected. `node-gettext` also no longer parses files or file paths for you, but accepts only ready-parsed JSON translation objects.
`node-gettext` expects all translations to be in the format specified by [`gettext-parser`](https://github.com/smhg/gettext-parser). Therefor, you should use that to parse .mo or .po files.
Here is a full list of all breaking changes:
Here is an example where we read a bunch of translation files from disk and add them to our `Gettext` instance:
* `textdomain(domain)` is now `setLocale(locale)`
* `dgettext`, `dngettext`, `dpgettext` and `dnpgettext` does not treat the leading `domain` argument as a locale, but as a domain. To get a translation from a certain locale you need to call `setLocale(locale)` beforehand.
* A new `setTextDomain(domain)` has been introduced
* `addTextdomain(domain, file)` is now `addTranslations(locale, domain, translations)`
* `addTranslations(locale, domain, translations)` **only accepts a JSON object with the [shape described in the `gettext-parser` README](https://github.com/smhg/gettext-parser#data-structure-of-parsed-mopo-files)**. To load translations from .mo or .po files, use [gettext-parser](https://github.com/smhg/gettext-parser), and it will provide you with valid JSON objects.
* `_currentDomain` is now `domain`
* `domains` is now `catalogs`
* The instance method `__normalizeDomain(domain)` has been replaced by a static method `Gettext.getLanguageCode(locale)`
```js
import fs from 'fs'
import path from 'path'
import Gettext from 'node-gettext'
import { po } from 'gettext-parser'
// In this example, our translations are found at
// path/to/locales/LOCALE/DOMAIN.po
const translationsDir = 'path/to/locales'
const locales = ['en', 'fi-FI', 'sv-SE']
const domain = 'messages'
const gt = new Gettext()
locales.forEach((locale) => {
const fileName = `${domain}.po`
const translationsFilePath = path.join(translationsDir, locale, filename)
const translationsContent = fs.readSync(translationsFilePath)
const parsedTranslations = po.parse(translationsContent)
gt.addTranslations(locale, domain, parsedTranslations)
})
```
## API

@@ -95,5 +126,25 @@

## Gettext
* [Gettext](#Gettext)
* [new Gettext([options])](#new_Gettext_new)
* [.on(eventName, callback)](#Gettext+on)
* [.off(eventName, callback)](#Gettext+off)
* [.addTranslations(locale, domain, translations)](#Gettext+addTranslations)
* [.setLocale(locale)](#Gettext+setLocale)
* [.setTextDomain(domain)](#Gettext+setTextDomain)
* [.gettext(msgid)](#Gettext+gettext) ⇒ <code>String</code>
* [.dgettext(domain, msgid)](#Gettext+dgettext) ⇒ <code>String</code>
* [.ngettext(msgid, msgidPlural, count)](#Gettext+ngettext) ⇒ <code>String</code>
* [.dngettext(domain, msgid, msgidPlural, count)](#Gettext+dngettext) ⇒ <code>String</code>
* [.pgettext(msgctxt, msgid)](#Gettext+pgettext) ⇒ <code>String</code>
* [.dpgettext(domain, msgctxt, msgid)](#Gettext+dpgettext) ⇒ <code>String</code>
* [.npgettext(msgctxt, msgid, msgidPlural, count)](#Gettext+npgettext) ⇒ <code>String</code>
* [.dnpgettext(domain, msgctxt, msgid, msgidPlural, count)](#Gettext+dnpgettext) ⇒ <code>String</code>
* [.textdomain()](#Gettext+textdomain)
* [.setlocale()](#Gettext+setlocale)
* ~~[.addTextdomain()](#Gettext+addTextdomain)~~
<a name="new_Gettext_new"></a>
### new Gettext(options)
### new Gettext([options])
Creates and returns a new Gettext instance.

@@ -104,3 +155,3 @@

- `options`: <code>Object</code> - A set of options
- `[options]`: <code>Object</code> - A set of options
- `.debug`: <code>Boolean</code> - Whether to output debug info into the

@@ -129,12 +180,2 @@ console.

<a name="Gettext+emit"></a>
### gettext.emit(eventName, eventData)
Emits an event to all registered event listener.
**Params**
- `eventName`: <code>String</code> - An event name
- `eventData`: <code>any</code> - Data to pass to event listeners
<a name="Gettext+addTranslations"></a>

@@ -310,19 +351,14 @@

```
<a name="Gettext+getComment"></a>
<a name="Gettext+textdomain"></a>
### gettext.getComment(domain, msgctxt, msgid) ⇒ <code>Object</code>
Retrieves comments object for a translation. The comments object
has the shape `{ translator, extracted, reference, flag, previous }`.
### gettext.textdomain()
C-style alias for [setTextDomain](#gettextsettextdomaindomain)
**Returns**: <code>Object</code> - Comments object or false if not found
**Params**
**See**: Gettext#setTextDomain
<a name="Gettext+setlocale"></a>
- `domain`: <code>String</code> - A gettext domain name
- `msgctxt`: <code>String</code> - Translation context
- `msgid`: <code>String</code> - String to be translated
### gettext.setlocale()
C-style alias for [setLocale](#gettextsetlocalelocale)
**Example**
```js
const comment = gt.getComment('domainname', 'sports', 'Backs')
```
**See**: Gettext#setLocale
<a name="Gettext+addTextdomain"></a>

@@ -335,26 +371,20 @@

<a name="Gettext+textdomain"></a>
### ~~gettext.textdomain()~~
***Deprecated***
This function will be removed in the final 2.0.0 release.
## Migrating from v1 to v2
<a name="Gettext.getLanguageCode"></a>
Version 1 of `node-gettext` confused domains with locales, which version 2 has corrected. `node-gettext` also no longer parses files or file paths for you, but accepts only ready-parsed JSON translation objects.
### Gettext.getLanguageCode(locale) ⇒ <code>String</code>
Returns the language code part of a locale
Here is a full list of all breaking changes:
**Returns**: <code>String</code> - A language code
**Params**
* `textdomain(domain)` is now `setLocale(locale)`
* `dgettext`, `dngettext`, `dpgettext` and `dnpgettext` does not treat the leading `domain` argument as a locale, but as a domain. To get a translation from a certain locale you need to call `setLocale(locale)` beforehand.
* A new `setTextDomain(domain)` has been introduced
* `addTextdomain(domain, file)` is now `addTranslations(locale, domain, translations)`
* `addTranslations(locale, domain, translations)` **only accepts a JSON object with the [shape described in the `gettext-parser` README](https://github.com/smhg/gettext-parser#data-structure-of-parsed-mopo-files)**. To load translations from .mo or .po files, use [gettext-parser](https://github.com/smhg/gettext-parser), and it will provide you with valid JSON objects.
* `_currentDomain` is now `domain`
* `domains` is now `catalogs`
* The instance method `__normalizeDomain(domain)` has been replaced by a static method `Gettext.getLanguageCode(locale)`
- `locale`: <code>String</code> - A case-insensitive locale string
**Example**
```js
Gettext.getLanguageCode('sv-SE')
// -> "sv"
```
## License

@@ -361,0 +391,0 @@

@@ -45,15 +45,28 @@ 'use strict';

describe('#setLocale', function() {
it('should have no default locale', function() {
expect(gt.locale).to.equal(null);
it('should have the empty string as default locale', function() {
expect(gt.locale).to.equal('');
});
it ('should not accept a locale that has no translations', function() {
it('should accept whatever string is passed as locale', function() {
gt.setLocale('de-AT');
expect(gt.locale).to.equal(null);
expect(gt.locale).to.equal('de-AT');
gt.setLocale('01234');
expect(gt.locale).to.equal('01234');
gt.setLocale('');
expect(gt.locale).to.equal('');
});
it('should change locale if translations exist', function() {
gt.addTranslations('et-EE', 'messages', jsonFile);
gt.setLocale('et-EE');
expect(gt.locale).to.equal('et-EE');
it('should reject non-string locales', function() {
gt.setLocale(null);
expect(gt.locale).to.equal('');
gt.setLocale(123);
expect(gt.locale).to.equal('');
gt.setLocale(false);
expect(gt.locale).to.equal('');
gt.setLocale(function() {});
expect(gt.locale).to.equal('');
gt.setLocale(NaN);
expect(gt.locale).to.equal('');
gt.setLocale();
expect(gt.locale).to.equal('');
});

@@ -63,14 +76,28 @@ });

describe('#setTextDomain', function() {
it('defaults to "messages"', function() {
it('should default to "messages"', function() {
expect(gt.domain).to.equal('messages');
});
it('does not accept an empty value', function() {
it('should accept and store any string as domain name', function() {
gt.setTextDomain('mydomain');
expect(gt.domain).to.equal('mydomain');
gt.setTextDomain('01234');
expect(gt.domain).to.equal('01234');
gt.setTextDomain('');
expect(gt.domain).to.equal('messages');
expect(gt.domain).to.equal('');
});
it('accepts and stores a non-empty domain name', function() {
gt.setTextDomain('mydomain');
expect(gt.domain).to.equal('mydomain');
it('should reject non-string domains', function() {
gt.setTextDomain(null);
expect(gt.domain).to.equal('messages');
gt.setTextDomain(123);
expect(gt.domain).to.equal('messages');
gt.setTextDomain(false);
expect(gt.domain).to.equal('messages');
gt.setTextDomain(function() {});
expect(gt.domain).to.equal('messages');
gt.setTextDomain(NaN);
expect(gt.domain).to.equal('messages');
gt.setTextDomain();
expect(gt.domain).to.equal('messages');
});

@@ -167,6 +194,9 @@ });

it('should pass msgid until a locale is set', function() {
expect(gt.gettext('o2-1')).to.equal('o2-1');
gt.setLocale('et-EE');
expect(gt.gettext('o2-1')).to.equal('t2-1');
it('should pass msgid when no translation is found', function() {
expect(gt.gettext('unknown phrase')).to.equal('unknown phrase');
expect(gt.dnpgettext('unknown domain', null, 'hello')).to.equal('hello');
expect(gt.dnpgettext('messages', 'unknown context', 'hello')).to.equal('hello');
// 'o2-1' is translated, but no locale has been set yet
expect(gt.dnpgettext('messages', '', 'o2-1')).to.equal('o2-1');
});

@@ -201,3 +231,47 @@

});
it('should emit an error event when a locale that has no translations is set', function() {
gt.setLocale('et-EE');
expect(errorListener.callCount).to.equal(1);
});
it('should emit an error event when no locale has been set', function() {
gt.addTranslations('et-EE', 'messages', jsonFile);
gt.gettext('o2-1');
expect(errorListener.callCount).to.equal(1);
gt.setLocale('et-EE');
gt.gettext('o2-1');
expect(errorListener.callCount).to.equal(1);
});
it('should emit an error event when a translation is missing', function() {
gt.addTranslations('et-EE', 'messages', jsonFile);
gt.setLocale('et-EE');
gt.gettext('This message is not translated');
expect(errorListener.callCount).to.equal(1);
});
it('should not emit any error events when a translation is found', function() {
gt.addTranslations('et-EE', 'messages', jsonFile);
gt.setLocale('et-EE');
gt.gettext('o2-1');
expect(errorListener.callCount).to.equal(0);
});
});
describe('Aliases', function() {
it('should forward textdomain(domain) to setTextDomain(domain)', function() {
sinon.stub(gt, 'setTextDomain');
gt.textdomain('messages');
expect(gt.setTextDomain.calledWith('messages'));
gt.setTextDomain.restore();
});
it('should forward setlocale(locale) to setLocale(locale)', function() {
sinon.stub(gt, 'setLocale');
gt.setLocale('et-EE');
expect(gt.setLocale.calledWith('et-EE'));
gt.setLocale.restore();
});
});
});
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