Git Product home page Git Product logo

babelfish's Introduction

BabelFish - human friendly i18n for JS

CI NPM version Coverage Status

Internationalization with easy syntax for node.js and browser.

Classic solutions use multiple phrases for plurals. Babelfish defines plurals inline instead - that's more compact, and easy for programmers. Also, phrases are grouped into nested scopes, like in Ruby.

BabelFish supports all plural rules from unicode CLDR (via plurals-cldr).

Installation

node.js:

$ npm install babelfish

browser:

$ bower install babelfish

Use es5-shim for old browsers compatibility.

Phrases Syntax

  • #{varname} Echoes value of variable
  • ((Singular|Plural1|Plural2)):count Plural form

example:

  • А у меня в кармане #{nails_count} ((гвоздь|гвоздя|гвоздей)):nails_count

You can also omit anchor variable for plurals, by default it will be count. Thus following variants are equal:

  • I have #{count} ((nail|nails))
  • I have #{count} ((nail|nails)):count

Also you can use variables in plural parts:

  • I have ((#{count} nail|#{count} nails))

Need special zero form or overwrite any specific value? No problems:

  • I have ((=0 no nails|#{count} nail|#{count} nails))
Escape chars

If you need #{, ((, | or )) somewhere in text, where it can be considered as markup part - just escape them with \.

Example with YAML

As BabelFish flatten scopes, it's really fun and nice to store translations in YAML files:

---
ru-RU:
  profile: Профиль
  forums: Форумы
  apps:
    forums:
      new_topic: Новая тема
      last_post:
        title : Последнее сообщение
        by : от
  demo:
    apples: "На столе лежит #{count} ((яблоко|яблока|яблок))"

Usage

// Create new instance of BabelFish with default language/locale: 'en-GB'
var BabelFish = require('babelfish');
var i18n = new BabelFish('en-GB');


// Fill in some phrases
i18n.addPhrase('en-GB', 'demo.hello',         'Hello, #{user.name}.');
i18n.addPhrase('en-GB', 'demo.conv.wazup',    'Whats up?');
i18n.addPhrase('en-GB', 'demo.conv.alright',  'Alright, man!');
i18n.addPhrase('en-GB', 'demo.coerce',        'Total: #{count}.');

i18n.addPhrase('ru-RU', 'demo.hello',         'Привет, #{user.name}.');
i18n.addPhrase('ru-RU', 'demo.conv.wazup',    'Как дела?');

i18n.addPhrase('uk-UA', 'demo.hello',         'Здоровенькі були, #{user.name}.');


// Set locale fallback to use the most appropriate translation when possible
i18n.setFallback('uk-UA', 'ru-RU');


// Translate
var params = {user: {name: 'ixti'}};

i18n.t('ru-RU', 'demo.hello', params);  // -> 'Привет, ixti.'
i18n.t('ru-RU', 'demo.conv.wazup');     // -> 'Как дела?'
i18n.t('ru-RU', 'demo.conv.alright');   // -> 'Alright, man!'

i18n.t('uk-UA', 'demo.hello', params);  // -> 'Здоровенькі були, ixti.'
i18n.t('uk-UA', 'demo.conv.wazup');     // -> 'Как дела?'
i18n.t('uk-UA', 'demo.conv.alright');   // -> 'Alright, man!'

// When params is number or strings, it will be coerced to
// `{ count: XXX, value: XXX }` - use any of those in phrase.
i18n.t('en-GB', 'demo.coerce', 5);      // -> 'Total: 5.'


// You may wish to "dump" translations to load in browser later
// Dump will include all fallback translations and fallback rules
var locale_dump = i18n.stringify('ru-RU');

var i18n_new = require('babelfish')('en-GB'); // init without `new` also works
i18n_new.load(locale_dump);


// Use objects instead of strings (object/array/number/boolean) - can be
// useful to prepare bulk data for external libraries.
// Note, only JSON-supported types are ok (no date & regex)
i18n.addPhrase('en-GB', 'demo.boolean',  true);
i18n.addPhrase('en-GB', 'demo.number',   123);
i18n.addPhrase('en-GB', 'demo.array',    [1, 2, 3]);
// fourth param required for hashes (objects) to disable flattening,
// other types are autodetected
i18n.addPhrase('en-GB', 'demo.array',    { foo:1, bar:"2" }, false);

Implementations in other languages

License

View the LICENSE file (MIT).

babelfish's People

Contributors

akzhan avatar dervus avatar dvv avatar elmigranto avatar guria avatar ixti avatar kirill89 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

babelfish's Issues

Support for pluralization-based keys

Take a look at this dictionary entries:

datetime:
  distance_in_words:
    less_than_x_minutes:
      one: "less than a minute"
      other: "less than #{count} minutes"

Rails pluralization backend supports selection of key based on count value.

In addition to typical one, few, manu and other suffixes it supports optional zero suffix.

This cannot be repeated by Babelfish syntax because here a case when no value should be printed.

coerce simple types for simple calls

babelfish.t('en', 'phrase_key', param1, param2, ...)

  1. param1, if not object, can be casted in special way:
    • String, Number X -> { count: X, value: X } (done)
    • Date d -> ???
  2. param2 can be casted as format for special helpers:
    • String X -> { format: X }

Babelfish Perl module

Just note that we plan to release CPAN (open source) Perl module as counterpart for your's one.

Release date planned to ~11 Jun 2014.

Also this pair will be disputed on YAPC::Russia 2014 conference (Spb, 13-14 Jun 2014).

Question: Default fallback for country variant

Hello.

I would like to ask question about using country variants:

var BabelFish = require('babelfish');
var i18n = new BabelFish('en-GB');
i18n.addPhrase('pt', 'test.a', 'Apelido')
i18n.addPhrase('pt-BR', 'test.a', 'Sobrenome')
i18n.addPhrase('pt', 'test.b', 'Título')
> i18n.t('pt', 'test.a')
'Apelido'
> i18n.t('pt-BR', 'test.a')
'Sobrenome'
> i18n.t('pt-BR', 'test.b')
'pt-BR: No translation for [test.b]'

Shouldn't last line fallback to 'pt' ? Or i should manually add every possible fallback with .setFallback?

Thanks.

parser rewrite

It would be nice to rewrite parser for:

  • (?) autoguess plural counter, if variable already used in phrase (not sure if needed)
  • more clear & compact code, no pegjs dependency

See esprima sources for example.

`locale` in `.getCompiledData()` should default to fallback locale

locale in .getCompiledData() should default to fallback locale, but following adds store['undefined']:

var BabelFish = require('babelfish');
var i18n = new BabelFish('ru');

i18n.addPhrase('ru', 'i', 'я');

console.dir(i18n.getCompiledData());
console.dir(i18n._storage);

Outputs:

{ ru: 
   { i: 
      { type: 'string',
        locale: 'ru',
        translation: 'я' } },
  undefined: 
   { i: 
      { type: 'string',
        locale: 'ru',
        translation: 'я' } } }

.getTranslation

README.md contains examples which call getTranslation() on BabelFish instance. This is probably an artefact left from some previous version(s) and should be changed to translation() or t() call.

angular babelfish

This library worth to be wrap into angular module with directive, service, filter stuff.
Is there any wip in that way? Maybe someone from community started to develop such module for its own.

NB There is already some angular i18n modules but I wasn't able find one which fit me. At least it lacks of normal namespacing and such powerfull stringify method.

Zero-count in plurals

Following

var translator = require('babelfish').create('ru');
translator.addPhrase('ru', 'nailsCount', 'У меня #{n} ((гвоздь|гвоздя|гвоздей)):n');
[0, 1, 2, 5].forEach(function (count) {
  console.log(translator.t('ru', 'nailsCount', {n: count}));
});

outputs

У меня  гвоздей
У меня 1 гвоздь
У меня 2 гвоздя
У меня 5 гвоздей

I also think that ability to define different form for zero-count should be added. ("No items" instead of "0 items".) Same for singular ("One item"). That could be optional last two forms inside (()).

YAML

How can I load a YAML definition file ?
appears that load method only accept a result from stringify method

Move pluralizer to separate package with autogenerated src

Lazy compile

It looks more simple to store phrases "as is" with compile on demand - we have full lib on client anyway, and loading.

pros

  • simple & more compact serialization, less problems with functions
  • faster load time at startup.
  • more clear architecture with 2-level cache (parse + compile) + right processing for plural parts

cons

  • no compilation on client with strict CSRF policy (but parse phase still cacheable)
  • more difficult to create minimalistic browser-only version (but still possible, if it will be loaded with AST tree)

syntax extention: formats

If one need piped processing for variables to do additional l10n, it's possible to use this notation:

#{variable:processor|param1|param2|...}

Examples:

  • #{count:localize_number}
  • #{date:format|short}
  • #{another.translation:phrase} - insert another translation by this key

The same can be done for selector notation, to change selection rule:

  • ((male|female))[:gender]
  • ((male|female))[:ordinal]

That's not planned right now, because no real requests exist. Also syntax is subject to discuss.

Probably, that should not be done at all, because data can be prepared before passing to translate()

Relax plurals syntax - find variable automatically

Current format:

#{count} active %{user|users}:count

Could be:

#{count} active %{user|users}

Rule:

  • take first "yet unused" variable, if not defined strictly. Variable can be before of after plural
  • the same, when several plurals/vars (very rare case, can be skipped)
  • if no variable in string - take the first key from params.

Support for complex types

Many internationalization engines like Rails i18n support complex value types.

We need to support something like t('en_US', 'date.abbr_day_names') with:

date:
  abbr_day_names:
  - Sun
  - Mon
  - Tue
  - Wed
  - Thu
  - Fri
  - Sat

But when we try to compile it using Babelfish errors thrown.

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.