mashpie / i18n-node

Lightweight simple translation module for node.js / express.js with dynamic json storage. Uses common __('...') syntax in app and templates.
MIT License
3.09k stars 421 forks source link

Expose applyAPItoObject? #271

Open misterdai opened 8 years ago

misterdai commented 8 years ago

Hi. I'm currently using the Sequelize ORM for Node.js, along with this fantastic i18n module. However, I've got validation messages against my database models that need translating but at that depth I don't have access to the req / res scopes.

My idea is to at least set the locale against the model, so it has the awareness to translate the messages. The problem I'm running into is I don't want to use the global i18n as I'm worried it's state may be changed by other requests, mid translation (unlikely, but just in case I do something asynchronous). After seeing how it's handled for express with the internal applyAPItoObject function, I was curious if there's any chance of this being exposed? That way I can easily create a short term instance based off the main i18n one, and set it to the correct locale for translation.

Suggestion:

import i18n form './i18n'; // <- runs .init(options) and exports i18n.

const translationValidationHook = (instance, options, error) => {
  // ^ Catchy name ;)
  const i18nInstance = i18n.copy();
  i18nInstance.setLocale(instance.get('locale'));
  error.message = i18nInstance.__.apply(null, error.message.split(',');
};

...
mashpie commented 8 years ago

The problem I'm running into is I don't want to use the global i18n as I'm worried it's state may be changed by other requests...

True, absolutely.

i18n has a register option, which might help in your use-case:

    // object or [obj1, obj2] to bind the i18n api and current locale to - defaults to null
    register: global,

and that feature is best described in it's test in https://github.com/mashpie/i18n-node/blob/master/test/i18n.configureRegister.js

You should be able to register i18n to any object of your liking. Next set the appropriate locale on that object and use it's newly attached i18n-api (obj.__(), etc.) to translate messages in the objects scope.

You could set obj.locale by i18n.setLocale(obj, 'en') or attach that attribute on your own (ie. as a result of a db-query):

let obj = {
  firstName: 'Marcus',
  locale: 'de'
};

i18n.configure({
  // [...] other options
  register: obj
});

let greeting = obj.__('Hello {{firstName}}', obj);

register also accepts an array of objects so you could pass some more references while bootstrapping your app and models.

Hope this helps.

misterdai commented 8 years ago

I did look at that. Ideally I wanted an i18n registered object when running the validation process. Are there any issues with running the .configure(options) multiple times, just to register further objects when needed? Would I have to provide all the other options each time as well to just make use of the register option for object association? Cheers :)

mashpie commented 8 years ago

configure will entirely overwrite any previous settings and will reread all files again, etc. You should try to implement either extra instances of i18n or register all object in advance. Those objects could be empty on configure as long they are used as reference and not cloned or copied.

Another possibility I am thinking of is to register i18n to a dummy object, which could get extended by your objects later, like with http://underscorejs.org/#extend - worth to try?

let translatable = {
  locale: ''
};

i18n.configure({
  // [...] other options
  register: translatable
});

let obj = _.extend({
  firstName: 'Marcus',
  locale: 'de'
}, translatable);

let greeting = obj.__('Hello {{firstName}}', obj);