Giter Site home page Giter Site logo

smartloc's Introduction

Purpose

If like me:

  • you are developping a NodeJS API server which requires internationalization.
  • you find most i18n libraries too complicated for your needs, or requiring a refactoring of your existing architecture
  • you find it painful to propagate the request accepted languages in all your application parts

... then this library might be for you.

Read a tutorial to learn how to use this lib here

Framework support

There are almost-one-liners integrations with the following frameworks:

How this works

Install it

npm install smartloc --save

Just forget manipulating already translated strings in your code. It is cumbersome, and might leak languages you dont understand in your logs.

Smartloc allows you to declare in your code strings like that:

// recommanded: Explicitely specify a string unique ID
const myString = loc('stringUniqueId')`Hello ${name}, how are you today ?`;

// If you are not affraid of occasionally losing some translations when changing your code,
// then you can use this simpler form:
const myString = loc`Hello ${name}, how are you today ?`;
// => An ID will be autogenerated based on this string hash
//   => you might lose translations when changing the original string in code.

Those will give you an instance of StrLoc interface.

Here is how you can use it:

// build a translatable string
const name = 'world';
const str = loc`Hello ${name}`; // nb: This one will have an auto-generated id.

// Just fake loading translations
setDefaultLocale('en');
addLocale('fr', {
    [str.id]: 'Bonjour {0}',
});

// Use the string without language context (logs, ...)
console.log(str.toString()); // => Hello world
console.log(JSON.stringify({msg: str})); // => {"msg": "Hello world"}

// ... or with language context (when returning a query result, ...)
console.log(withLocales(['it', 'fr'], () => str.toString())); // => Bonjour world
console.log(withLocales(['it', 'fr'], () => JSON.stringify({msg: str})); // => {"msg": "Bonjour world"}

As you might see, the translation is NOT performed when you build the string, but when you actually try to send a result to your end user.

This allows you to build your app without caring about knowing which language your user accepts. The translation will be automatically performed when sending actual json to your user, through a simple middleware (see samples listed in "Framework Support")

Translating your app

Generating/updating translations from code

As an example, if you write your code in english, and you would like to translate your app in French and Deutsch, add the following script to your package.json file:

{
    "scripts": {
        "smartloc": "smartloc collect --format=json --locales=fr-FR,de-DE --defaultLocale=en-US"
    }
}

Once you have written your code (or each time you have changed it), you can run npm run smartloc to create/update your translation files.

nb: The --defaultLocale argument is optional, and will be infered from your code if you explicitly call setDefaultLocale() somewhere.

Loading available translations on server boot

Before serving any request, you must:

  1. Tell smartloc which is the default locale (the one you wrote your translations in)
  2. Load other locales

For 1), this is straightforward: setDefaultLocale('en-US')

To load other locales, you have several options:

Option 1 - Define in code:

import {addLocale} from 'smartloc';

// you could also pass here an object loaded from your database, or whatever
addLocale('fr-FR', {
    mySentenceId: 'Une traduite en français',
});

Option 2 - Load a single given file

import { loadAllLocales } from 'smartloc/node';

await loadAllLocales('/path/to/my-translation.json', true);

nb: The second argument is 'merge'... if false, all previously loaded translations will be cleared. Else, translations will be merged.

Option 3 - Scan a directory for translations

import { loadAllLocales } from 'smartloc/node';

await loadAllLocales('/path/to/dir/to/scan', true);

nb: The second argument is 'merge'... if false, all previously loaded translations will be cleared. Else, translations will be merged.

Supported formats

Smartloc cli implements two translation format through the --format argument

  • --format=json : JSON translation files
  • --format=xliff : XLIFF translation files

nb: Smartloc is grouping your translation IDs by category, detected by the first "." in your ID.

Other use cases

The LocStr interface has several implementations:

Smartloc

The default one which is returned when using the loc tag:

return loc`Hello`;

MultiLoc

If you wish to declare all translations directly in your code:

return new MultiLoc({
    en: 'Hello',
    fr: 'Bonjour',
});

SingleLoc

If you wish to declare a string that is the same in all languages, but which is typed as a LocStr:

return new SingleLoc('Typescript');

TransformedLoc

Sometimes, you will want to apply transformations to your final string. You can do that using the .transform() method available on LocStr, which will return you a transformed translatable string.

return loc`Some string wich can contain html`
        .transform(x => escapeHtml(x)); // apply a transformation

Array of LocStr

When you have an array of smartloc strings that you want to join, you can use the LocStringArray class:

const array = new LocStringArray([loc`Hello`, loc`world`]);

const str = array.join(' ').transform(x => x + ' !');

console.log(str.toString('en')); // => Hello world !
console.log(str.toString('fr')); // => Bonjour monde !

Serialization in an untranslated form

Somtimes, you will want to serialize an arbitrary LocStr in its untranslated form (to store a localizable sentence in a DB, for instance).

In this case, you can serialize it like that:

import {loc, MultiLoc, withSerializationContext} from 'smartloc';
const sampleObject = {
    reference: loc('stringId')`Hello {world}`,
    multi: new MultiLoc({ en: 'A string', fr: 'Une chaine' }),
};

// serialize
const serialized = withSerializationContext(() => JSON.stringify(sampleObject));

// store ... nb: it will look like {"reference": "i18n/id:stringId", "multi": {"i18n:fr": "A string", "i18n:en": "Une chaine"}}
storeInDb(serialized);

You can deserialize it back to translatable instance later like that:

import {toLocalizable} from 'smartloc';
const obj = loadFromDb();

// get back a translatable intance
const serializable = toLocalizable(obj);

Cloning

Beware, if you deep-clone an object containing smartloc string instances, you must:

  • Clone the object prototype
  • Clone symbol properties

... or you could just ignore cloning smartloc strings altogether (they are immutable anyway): You can detect them using the isLocStr() method and skip them when performing your deep clone.

NB: Of course, if you clone your object using JSON.parse(JSON.stringify(obj)), then you will lose translatability (smartloc strings will be translated as strings in your default language).

smartloc's People

Contributors

oguimbal avatar dependabot[bot] avatar

Stargazers

Carlos Saraiva avatar  avatar nuintun avatar Yannis avatar Daniel Hritzkiv avatar Maurício Weber avatar Sylvain avatar  avatar  avatar

Watchers

 avatar  avatar

smartloc's Issues

Other middleware?

I'd like to give this a spin with our Apollo server, but is this usable with other middleware like Koa?

[CLI] add "new"/"dirty" flag on translations when source has changed

When refreshing translations from the CLI, all translation files must add a "new" flag for translations that have newly been added, or a "dirty" flag when the sentence has changed in source language.

New sentences to translate:

{
    "source": "XX",
    "new": true
}

Modified in source languages:

{
    "source": "Updated version of XXX",
    "target": "Translation of old XXX",
    "dirty": true
}

Better serialization of some localizable strings

As of today, there are only two kinds of localizable strings that can be stored.

MultiLoc & LocStr which have no template element:

const a = toJsonStorable({ msg: loc('someId')`Hello` }); //  =>  {"msg": "i18n/id:someId"}

const b = toJsonStorable({
          msg: new MultiLoc({ en: 'Hello', fr: 'Bonjour' })
}); //  =>  {"msg": { "i18n:en": "Hello", "i18n:fr": "Bonjour"}}

Both can be deserizalized like this:

toLocalizable(a).msg.toString('fr') //  => "Bonjour"
toLocalizable(b).msg.toString('fr') //  => "Bonjour"

But for instance, templated localizable strings this is not yet supported:

const name: string | LocStr = something;
const a =  toJsonStorable({ something: loc('someId')`Hello ${name}` });

serialization/deserialization mechanisms could be improved to support this, in order to serialize the previous into something like:

{
    "something":  {
            "i18n/id": "someId",
            "args": [
                    .... arguments (strings or other serialized versions of the interpolation arguments)
            ]
    }
}

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.