A general purpose internationalization library in 292 bytes

Overview
rosetta
A general purpose internationalization library in 298 bytes!

Features

  • Simple and Familiar API
  • Unobstrusive and Unopinionated
  • Less than 300 bytes – including dependencies!

Install

$ npm install --save rosetta

Usage

import rosetta from 'rosetta';

const i18n = rosetta({
  en: {
    intro: {
      welcome: 'Welcome, {{username}}!',
      text: 'I hope you find this useful.',
    },
    support(obj) {
      let hour = Math.floor(Math.random() * 3) + 9;
      let str = `For questions, I'm available on ${obj.date.toLocaleDateString()}`;
      str += `, any time after ${hour}:00.`
      return str;
    }
  }
});

// set default language
i18n.locale('en');

// add new language
i18n.set('pt', {
  intro: {
    welcome: obj => `Benvind${obj.feminine ? 'a' : 'o'}, ${obj.username}!`,
    text: 'Espero que você ache isso útil.'
  }
});

// append extra key(s) to existing language
i18n.set('pt', {
  support(obj) {
    let hour = Math.floor(Math.random() * 3) + 9;
    let str = `Se tiver perguntas, estou disponível em ${obj.date.toLocaleDateString()}`;
    str += `, qualquer hora depois às ${hour}:00.`
    return str;
  }
});

const data = {
  feminine: false,
  username: 'lukeed',
  date: new Date()
};

// Retrieve translations
// NOTE: Relies on "en" default
i18n.t('intro.welcome', data); //=> 'Welcome, lukeed!'
i18n.t('intro.text', data); //=> 'I hope you find this useful.'
i18n.t('support', data); //=> 'For questions, I'm available on 4/8/2020, any time after 11:00.'

// Retrieve translations w/ lang override
i18n.t('intro.welcome', data, 'pt'); //=> 'Benvindo, lukeed!'

// Change default language key
i18n.locale('pt');

// Retrieve translations w/ new defaults
i18n.t('intro.text', data); //=> 'Espero que você ache isso útil.'
i18n.t('intro.text', data, 'en'); //=> 'I hope you find this useful.'

API

rosetta(dict?)

Returns: Rosetta

Initializes a new Rosetta instance.
You may optionally provide an initial translation object.

rosetta.locale(lang?)

Returns: String

Sets the language code for the Rosetta instance.
This will cause all rossetta.t() lookups to assume this lang code.

The function will return the currently active lang code. This means that a setting a new value will reply with the same value. Additionally, calling locale() without any argument will return the lang code that the Rosetta instance was last given.

lang

Type: String
Required: false

The language code to choose.
If locale() is called without an argument (or with a falsey value), then the current lang code is returned.

rosetta.set(lang, table)

Merge (or override) translation keys into the lang collection.

lang

Type: String

The language code to target.

table

Type: Object

A new record of key-values to merge into the lang's dictionary.

Each key within the table can correspond to a function or a string template.

When using a function, it will receive the entire data input (see params).
You are required to ensure the function returns a (string) value of your liking.

When using a string template, anything within double curly brackets ({{ example }}) will be interpreted as a key path and interpolated via templite. The key path can use dot-notation to access nested values from the data input (see params). Additionally, if a key path did not resolve to a value, an empty string is injected.

const ctx = rosetta({
  en: {
    foo: (obj) => `function sees "${obj.value || '~DEFAULT~'}"`,
    bar: 'template sees "{{value}}"'
  }
});

ctx.t('foo', {}, 'en');
//=> 'function sees "~DEFAULT~"
ctx.t('foo', { value: 123 }, 'en');
//=> 'function sees "123"

ctx.t('bar', {}, 'en');
//=> 'template sees ""
ctx.t('bar', { value: 123 }, 'en');
//=> 'template sees "123"

rosetta.table(lang)

Returns: Object or undefined

Retrieve the the lang's full dictionary/table of translation keys.

If the language does not exist (aka, no translations have been provided for it), you'll receive undefined.
Otherwise, you'll receive the full object as it exists within the Rosetta instance. See table.

Important: Manipulating this object is any way will mutate and affect your Rosetta instance. Be careful!

lang

Type: String

The language code's table to retrieve.

rosetta.t(key, params?, lang?)

Returns: String

Retrieve the value for a given key.

Important: In the normal/default mode, an empty string will be returned for unknown keys.
Conversely, in "debug" mode, an error message will be printed and undefined will be returned for unknown keys.

key

Type: String or Array<String|Number>

The identifier to retrieve.

A key can access nested properties via:

  • a string that with dot notation — 'foo.bar.1.baz'
  • an array of individual key segments — ['foo', 'bar', 1, 'baz']

Important: You are expected to know & traverse your own dictionary structure correctly.

const ctx = rosetta({
  en: {
    fruits: {
      apple: 'apple',
    }
  }
});

ctx.locale('en');

ctx.t('fruits.apple'); //=> 'apple'
ctx.t(['fruits', 'apple']); //=> 'apple'

params

Type: any
Optional: true

The data object argument to pass your dictionary keys' string templates and/or functions.

Note: If your string template tries to access a key that doesn't exist, an empty string is injected.

const ctx = rosetta({
  es: {
    hello: '¡Hola {{name}}!'
  },
  en: {
    hello(obj) {
      return obj.name === 'lukeed' ? 'wazzzuppp' : `Hello, ${obj.name}!`;
    },
  },
  pt: {
    hello: 'Oi {{person}}, tudo bem?' // <-- key is wrong
  },
});

const user1 = { name: 'lukeed' };
const user2 = { name: 'Billy' };

ctx.t('hello', user1, 'es'); //=> '¡Hola lukeed!'

ctx.t('hello', user1, 'en'); //=> 'wazzzuppp'
ctx.t('hello', user2, 'en'); //=> 'Hello, Billy!'

ctx.t('hello', user1, 'pt'); //=> 'Oi , tudo bem?'

lang

Type: String
Optional: true

A language code override without changing the entire Rosetta instance's default language.

const ctx = rosetta();

ctx.locale('en'); //=> set default

ctx.t('greeting', 'lukeed');
//=> (en) 'Hello lukeed!'
ctx.t('greeting', 'lukeed', 'es');
//=> (es) '¡Hola lukeed!'
ctx.t('bye');
//=> (en) 'Cya'

Debugging

There is a "debug" mode included for development environments.

The only difference with "debug" mode is that rossetta.t() will log an error to the console when attempting to access a key that does not exist. Conversely, the main/default runtime will quietly return an an empty string for consistent output.

Otherwise, the API is exactly the same as the main/default export!
This makes it easy to alias or swap the versions for development vs production bundles. Checkout the Configuration section below for recipes.

// debug mode
import rosetta from 'rosetta/debug';

const i18n = rosetta({
  en: {
    hello: 'hello'
  }
});

i18n.locale('en');

i18n.t('hello');
//=> 'hello'

i18n.t('foobar');
// [rosetta] Missing the "foobar" key within the "en" dictionary
//=> undefined

Note: With the non-"debug" runtime, an empty string would be returned for the foobar key.

Configuration

Here are quick configuration recipes for Rollup and webpack that allow you to choose the right version of rosetta for your current environment without changing you application code.

With both recipes, you will import rosetta like this:

import rosetta from 'rosetta';

It is up to the bundler to change what 'rosetta' resolves to...

Rollup

You will need to install @rollup/plugin-alias before continuing.

const isDev = /*custom logic*/ || !!process.env.ROLLUP_WATCH;

export default {
  // ...,
  plugins: [
    // ...
    require('@rollup/plugin-alias')({
      entries: {
        rosetta: isDev ? 'rosetta/debug' : 'rosetta'
      }
    })
  ]
}

webpack

The ability to add aliases within webpack comes by default.
One simply needs to add a resolve.alias value depending on the environment:

const isDev = /*specific to your config*/;

module.exports = {
  //...,
  resolve: {
    alias: {
      // ...,
      rosetta: isDev ? 'rosetta/debug' : 'rosetta'
    }
  }
}

Runtime Support

The library makes use of Object shorthand methods and Object.assign.
This yields the following support matrix:

Chrome Safari Firefox Edge IE Node.js
45+ 9+ 34+ 12+ 4.0+

If you need to support older platforms, simply attach rosetta to your project's Babel (or similar) configuration.

Examples

  • Using Next.js — Thank you @SharpTech
    Official Next.js example using React Hooks and Context to provide SSR, SSG, CSR compatible i18n solutions.

Credits

Thank you @7sempra for gifting the rosetta name on npm.

License

MIT © Luke Edwards

Comments
  • Support returning the language a translation resolves to

    Support returning the language a translation resolves to

    To start with: great little library 👏🏼.

    One minor thing I would love to see is a way of knowing which language your returned translation is actually in. This is very useful in case of missing translations.

    // i18n.js
    import rosetta from 'rosetta'
    
    const i18n = rosetta({ en: {...}, es: {...} })
    i18n.locale('en')
    
    export default i18n
    
    // ...elsewhere
    import i18n from './i18n.js'
    
    const text = i18n.t('foo.bar', {}, 'it') // <--- No way of knowing, at this point, what language `text` is in
    

    Another way of looking at this is exposing whether or not rosetta managed to find a matching property in the dictionary.

    One common use case for this is setting a correct <html language="{{lang}}"> attribute value in an HTML template.

    opened by nfantone 5
  • Html tag

    Html tag

    Hello,

    I am migrating from "next-i18next" to Rosseta and I would not like to change all the translations.

    I have translations like the one below:

    "h1": "The most recognized <br/><i>Taxi</i><br/> company in Nerja",

    You know the best way to be able to render that HTML

    Thanks

    opened by milabron 4
  • Great piece!

    Great piece!

    Hi, thanks for the lib. I test it in my app and it does a good job. The only thing I'm missing is a tiny lib for language detection (header on server-side + navigator.language on client-side). Based on the right user locale rosetta should use en-US or fallback to en.

    question 
    opened by StarpTech 4
  • Feature to skip the dot notation

    Feature to skip the dot notation

    My keys are stored like so

    {
        "account.email.title": "Email",
        "account.familyName.title": "Last name",
        "account.givenName.title": "First name",
        "account.pushNotifications": "Enable push notifications",
        "account.removeFromDevice.logout": "Logout",
    

    Would love to use this lib but can't without an option to skip dot notation. Could that be on the horizon?

    opened by mfbx9da4 2
  • readme doc unclear or wrong on array notation for t()

    readme doc unclear or wrong on array notation for t()

    Great library simple and flexible.

    Small issue I've found, using the readme doc notation doesn't work for me a string that with dot notation — 'foo.bar[1].baz'

    from the tests I think the correct syntax should be 'foo.bar.1.baz'. Unless of course you can help me correct my understanding of this case.

    Cheers

    opened by kairos666 1
  • Scaling for large server-side language requirements?

    Scaling for large server-side language requirements?

    Just want to say this is an awesome little library. Wanting to ask if you have any thoughts about how it could scale to use a large server-side database of strings supporting multiple languages and remain memory performant?

    opened by aeddie-zapidhire 0
Releases(v1.1.0)
  • v1.1.0(Jun 29, 2020)

    Features

    • Add getter support for locale() method (#9): 74558c4 Now locale() always returns the currently active lang code; including when no arguments are given.

    • Add table() method: 0ef7242 Retrieve a language's full dictionary. Provides direct access to object for quick existence checks and/or mutation.

    Chores

    • Update type declaration format (for CommonJS and ESM/TS support): 3135013
    • Update builder version: ebbae96
    • Update test runner: 679c88c
    • Update README badge for CI: 10e576b
    • Update module size (+7B): 97290d7
    Source code(tar.gz)
    Source code(zip)
  • v1.0.1(Jun 29, 2020)

Owner
Luke Edwards
Luke Edwards
The alternative internationalization (i18n) library for Angular

NGX Locutus The alternative Angular Translation Library used for large scale microfrontend translations. No more worrying about shared translation ass

Stefan Haas 9 May 31, 2022
lightweight jQuery plugin for providing internationalization to javascript from ‘.properties’ files

jQuery.i18n.properties About jQuery.i18n.properties is a lightweight jQuery plugin for providing internationalization to javascript from ‘.properties’

null 408 Dec 25, 2022
Internationalization for react done right. Using the i18next i18n ecosystem.

react-i18next IMPORTANT: Master Branch is the new v10 using hooks. $ v10.0.0 npm i react-i18next react-native: To use hooks within react-native, you m

i18next 7.9k Dec 30, 2022
:globe_with_meridians: Internationalization plugin for Vue.js

vue-i18n Internationalization plugin for Vue.js ?? Gold Sponsors ?? Silver Sponsors ?? Bronze Sponsors ⚠️ NOTICE This repository is for Vue I18n v8.x.

kazuya kawaguchi 6.9k Dec 29, 2022
A JavaScript Internationalization Framework

FBT is an internationalization framework for JavaScript designed to be not just powerful and flexible, but also simple and intuitive. It helps with th

Facebook 3.8k Jan 8, 2023
🌍📖 A readable, automated, and optimized (5 kb) internationalization for JavaScript

Linguijs ?? ?? A readable, automated, and optimized (5 kb) internationalization for JavaScript Documentation · Documentation 2.x · Quickstart · Exampl

Lingui 3.5k Jan 2, 2023
Type-safe internationalization (i18n) for Next.js

Type-safe internationalization (i18n) for Next.js Features Usage Examples Scoped translations Change current locale Use JSON files instead of TS for l

Tom Lienard 251 Dec 22, 2022
A simple library used to handle our multi-language needs.

Vespucci Multiplayer - i18n Integration A simple library used to handle our multi-language needs. ⚠️ Important! This is a very simple implementation o

Vespucci - România 2 Feb 7, 2022
A general purpose, real-time visualization library.

Epoch By Ryan Sandor Richards Epoch is a general purpose charting library for application developers and visualization designers. It focuses on two di

Epoch 5k Dec 30, 2022
A general-purpose message and event queuing library for MongoDB

MongoMQ2 MongoMQ2 is a light-weight Node.js library that turns MongoDB collections into general-purpose message queues or event logs, without addition

Morris Brodersen 11 Dec 28, 2022
A general purpose, real-time visualization library.

Epoch By Ryan Sandor Richards Epoch is a general purpose charting library for application developers and visualization designers. It focuses on two di

Epoch 5k Dec 30, 2022
A jQuery-free general purpose library for building credit card forms, validating inputs and formatting numbers.

A jQuery-free general purpose library for building credit card forms, validating inputs and formatting numbers.

Jesse Pollak 528 Dec 30, 2022
Simple general purpose Discord Bot Generator

Welcome to HYMITY ?? Simple general purpose Discord Bot Generator ?? Homepage Prerequisites Knowledge in coding! Know how to make Discord Application

Sarvesh M Rao 3 Jul 3, 2022
The AKE-less General Purpose Build System with JavaScript DSL for Node.js platform.

The AKE-less General Purpose Build System with JavaScript DSL for Node.js platform. Inspired by NUKE. This project is reaching a mature stage, althoug

Aleksej Komarov 33 Oct 16, 2022
Tritan is a Discord.js bot that has many general-purpose features such as logging, moderation, image manipulation, music, and much more!

Tritan Bot Tritan Bot is a Discord Verified general purpose bot built with discord.js and express (yes, it has a dashboard included). Please read thro

Dylan J. 0 Jul 3, 2022
Tritan is a Discord.js bot that has many general-purpose features such as logging, moderation, image manipulation, music, and much more!

Tritan Bot Tritan Bot is a Discord Verified general purpose bot built with discord.js and express (yes, it has a dashboard included). Please read thro

Team Tritan 5 Nov 19, 2022
The Main Purpose The main purpose of creating an anaonline information system, as an effort responsive to the management of the data of the Members of the Persis Youth based on information technology systems

landing-page-pp landing-page-pp.vercel.app #The Main Purpose The main purpose of creating an anaonline information system, as an effort responsive to

Hilman Firdaus 6 Oct 21, 2022
A super tiny Javascript library to make DOM elements draggable and movable. ~500 bytes and no dependencies.

dragmove.js A super tiny Javascript library to make DOM elements draggable and movable. Has touch screen support. Zero dependencies and 500 bytes Gzip

Kailash Nadh 814 Dec 29, 2022
A navigation aid (aka, router) for the browser in 850 bytes~!

A navigation aid (aka, router) for the browser in 865 bytes~! Install $ npm install --save navaid Usage const navaid = require('navaid'); // Setup r

Luke Edwards 732 Dec 27, 2022