Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pick random values from array? #1070

Closed
muttoni opened this issue Jul 8, 2018 · 13 comments
Closed

Pick random values from array? #1070

muttoni opened this issue Jul 8, 2018 · 13 comments

Comments

@muttoni
Copy link

muttoni commented Jul 8, 2018

Hey, I'm a bit stumped. I'm trying to write a postProcessor plugin that will detect if a value is an Array and return a random value from it. This is to help with randomization of responses. It's not working because when the value is an array the postProcessor is not even called and instead returns undefined.

What I have so far:

my localization:

const langStrings = {
	'en' : {
		'translation' : {
			'WELCOME_MESSAGE' : 'Hello',
			'MULTIPLE_WELCOME': ['Hello', 'Hi', 'How are you', 'Whats up'],
			'GOODBYE_MESSAGE' : 'Bye %s', // ignore sprintf for now
			'MULTIPLE_GOODBYE': ['Bye %s', 'Goodbye %s'] // ignore sprintf for now
		}
	}
}

my postProcessor:

const arrayPicker = {
	type: 'postProcessor',
	name: 'arrayPicker',
	process: function (value, key, options, translator) {
		console.log('postProcessor::', value, key, options, translator);
		if (Array.isArray(value)) {
			return value[Math.floor(Math.random() * value.length - 1)];
		} else {
			return value;
		}
	}
}

my istantiation:

const opts = {
	lng: 'en',
	resources: langStrings,
	postProcess: ['arrayPicker']
};

i18n
	.use(arrayPicker)
	.init(opts);

When I run:

console.log(i18n.t('WELCOME_MESSAGE'))
-> "Hello" // and any console.logs in the postProcessor are called

console.log(i18n.t('MULTIPLE_WELCOME'))
-> undefined // complete silence from my postProcessor :'(

I also tried making a overloadTranslationOptionHandler where I specify returnObjects : true, but the issue is I only get access to the individual items of the array one at a time in the postProcessor.

const arrayPicker = {
	type: 'postProcessor',
	name: 'arrayPicker',
	process: function (value, key, options, translator) {
		console.log('postProcessor::', value, key, options);
		if (Array.isArray(value)) {
			return value[Math.floor(Math.random() * value.length - 1)];
		} else {
			return value;
		}
	},

	overloadTranslationOptionHandler: function(args) {
		return {
			returnObjects : true
		}
	}
}
console.log(i18n.t('MULTIPLE_WELCOME'));

OUTPUTS:
>    postProcessor:: Hello [ 'MULTIPLE_WELCOME.0' ] { returnObjects: true, joinArrays: false, ns: [ 'translation' ] }
>    postProcessor:: Hi [ 'MULTIPLE_WELCOME.1' ] { returnObjects: true, joinArrays: false, ns: [ 'translation' ] }
>    postProcessor:: How are you [ 'MULTIPLE_WELCOME.2' ] { returnObjects: true, joinArrays: false, ns: [ 'translation' ] }
>    postProcessor:: Whats up [ 'MULTIPLE_WELCOME.3' ] { returnObjects: true, joinArrays: false, ns: [ 'translation' ] }
>    [ 'Hello', 'Hi', 'How are you', 'Whats up' ]

Any ideas?

@jamuhl
Copy link
Member

jamuhl commented Jul 9, 2018

Hm...it goes through every individual value in the array and applies that postProcessor: https://github.com/i18next/i18next/blob/master/src/Translator.js#L106

But still it should go through it with the array...https://github.com/i18next/i18next/blob/master/src/Translator.js#L171

Can you debug why it does not enter here: https://github.com/i18next/i18next/blob/master/src/Translator.js#L210

@muttoni
Copy link
Author

muttoni commented Jul 11, 2018

It goes here: https://github.com/i18next/i18next/blob/master/src/Translator.js#L92
since this condition evaluates to true: !(joinArrays && resType === '[object Array]') as I don't specify joinArrays.

Either way, my postProcessor is only given access to 1 value at a time. What I want to do is receive all array values, pick one at random, and return that so as to randomize strings. Seems like a fairly simple and common use case.

If that way is not possible given how arrays are treated, how would you implement string randomization in i18next?

Thanks,

Andrea

@jamuhl
Copy link
Member

jamuhl commented Jul 11, 2018

Hm...then you should get res set here: https://github.com/i18next/i18next/blob/master/src/Translator.js#L110 -> your array (and n time called postprocessor with each array items)

then it goes here: https://github.com/i18next/i18next/blob/master/src/Translator.js#L172

and finally here: https://github.com/i18next/i18next/blob/master/src/Translator.js#L211

right? looks to me like it should theoretically work - guess need to create a sample to reproduce

@muttoni
Copy link
Author

muttoni commented Jul 12, 2018

guess need to create a sample to reproduce

In the original issue, if you copy and paste all the code snippets sequentially (and require i18next) and then do node test.js you will have a sample ready to reproduce and it doesn't work (returns undefined).

@jamuhl
Copy link
Member

jamuhl commented Jul 12, 2018

Should have written "guess I need to create a sample to reproduce" -> not hard to do...just time consuming

@jamuhl
Copy link
Member

jamuhl commented Jul 12, 2018

Arghh...make sense. Code lands here after array gets set to res:

https://github.com/i18next/i18next/blob/master/src/Translator.js#L182

-> there is no extend https://github.com/i18next/i18next/blob/master/src/Translator.js#L172 as we wrote all extend functions to expect a string -> which makes sense as we go over each array item separately.

Guess we can't change this without making postProcessors, ... complexer and without making a breaking change...so i would suggest doing:

i18next.randomFromT(key, opts) {
   const value = i18next.t(key, opts);
if (Array.isArray(value)) {
			return value[Math.floor(Math.random() * value.length - 1)];
		} else {
			return value;
		}
}

And call that function for those cases

@muttoni
Copy link
Author

muttoni commented Jul 12, 2018

Ok, so we're 50% of the way there.

The following test case works fine:

const i18n = require('i18next')

const langStrings = {
	'en' : {
		'translation' : {
			'WELCOME_MESSAGE' : 'Hello',
			'MULTIPLE_WELCOME': ['Hello' , 'Hello2']
		}
	}
}

const opts = {
	lng: 'en',
	resources: langStrings
};



i18n.init(opts);

i18n.randomT = function (key, values) {
	const value = i18n.t(key, { returnObjects: true });

	if (Array.isArray(value)) {
		return value[Math.floor(Math.random() * value.length)];
	} else {
		return value;
	}
}

console.log(i18n.randomT('MULTIPLE_WELCOME'));

However 😄

What if I want to use sprintf?

const i18n = require('i18next')
const sprintf = require('i18next-sprintf-postprocessor')

const langStrings = {
	'en' : {
		'translation' : {
			'WELCOME_MESSAGE' : 'Hello',
			'MULTIPLE_WELCOME': ['Hello %s' , 'Hello2 %s'],
		}
	}
}

const opts = {
	lng: 'en',
	resources: langStrings,
	overloadTranslationOptionHandler : sprintf.overloadTranslationOptionHandler
};

i18n
	.use(sprintf)
	.init(opts);

i18n.randomT = function (key, values) {
	const value = i18n.t(key, { 
		returnObjects: true,
	        postProcess: 'sprintf',
	        sprintf: values
	});

	if (Array.isArray(value)) {
		return value[Math.floor(Math.random() * value.length)];
	} else {
		return value;
	}
}

console.log('Final output', i18n.randomT('MULTIPLE_WELCOME', 'sir'));

// -> outputs 'Hello %s' instead of 'Hello sir'

@jamuhl
Copy link
Member

jamuhl commented Jul 12, 2018

@bdelee-zz
Copy link

I'm having the same issue as @muttoni, and I've tried to put @jamuhl's suggestion in, but still have this issue. I can't get the postprocess to work with sprintf. Did you end up solving this, @muttoni?

@muttoni
Copy link
Author

muttoni commented Aug 8, 2018 via email

@jamuhl
Copy link
Member

jamuhl commented Aug 8, 2018

i18n.randomSprintfT = function () {
    const args = arguments;
    let values = [];

    for (var i = 1; i < args.length; i++) {
      values.push(args[i]);
    }
	const value = i18n.t(args[0], { 
		returnObjects: true,
	        postProcess: 'sprintf',
	        sprintf: values
	});

	if (Array.isArray(value)) {
		return value[Math.floor(Math.random() * value.length)];
	} else {
		return value;
	}
}

console.log('Final output', i18n.randomT('MULTIPLE_WELCOME', 'sir'));

@jamuhl
Copy link
Member

jamuhl commented Sep 3, 2018

Could this be closed? Seems to be inactive for some time. Honestly not even can catch up current state of this issue.

@muttoni
Copy link
Author

muttoni commented Sep 4, 2018

Yes, thanks. Solved with your help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants