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

about setting locale for faker #63

Open
apollon-nishikawa-craif opened this issue Jul 22, 2024 · 7 comments
Open

about setting locale for faker #63

apollon-nishikawa-craif opened this issue Jul 22, 2024 · 7 comments

Comments

@apollon-nishikawa-craif

Thank you for developing this amazing library.
Is it possible to use this repository with a specific locale setting for faker?
I am planning to use this npm package as part of software targeted towards the Japanese market.
Thank you for your assistance.

@apollon-nishikawa-craif apollon-nishikawa-craif changed the title Question about setting locale about setting locale for faker Jul 22, 2024
@ctholho
Copy link
Contributor

ctholho commented Oct 8, 2024

I'm also interested in this feature.

Let's investigate what we need. In order to use your own locales you would need to:

  • fork this project
  • add your preferred locale to src/locales
  • and replace all imports of ./locales/en with your own locale

I assume the reason the maintainers are importing the english locale this way is because they don't want copycat to return totally different values when fakerJS updates their locale. You might have similar strict requirements.

So there are two paths about integrating the new locales: Should copycat copy the current locales (like it did with the en locale) or simply use the other locales as defined in the dependencies in the package.json?

Regardless, I think I would prefer if we can import a copycat locale just like fakerJS:

import { copycatJP as copycat } from '@snaplet/copycat

Initializing with something like this would also be possible:

import { defineLocale } from '@snaplet/copycat

const copycat = defineLocale('jp')
copycat.fullName('id-1')

Maybe others can chime in?

I would love if we can make this happen. Our application is also not in english and we use copycat for seeding training databases. English is a bit of an oddity for them.

@justinvdm
Copy link
Collaborator

Hey @ctholho, thank you for detailed input, it is quite spot on.

And sorry @apollon-nishikawa-craif, this one fell of my radar.

I assume the reason the maintainers are importing the english locale this way is because they don't want copycat to return totally different values when fakerJS updates their locale.

This is exactly why we did this :)

So there are two paths about integrating the new locales: Should copycat copy the current locales (like it did with the en locale) or simply use the other locales as defined in the dependencies in the package.json?

Back when this was a Snaplet project, supporting locales was something we wanted to do and never got around to. I'm not sure where things stand now, but here's my take:

Allowing users to plug in their own locale support might be the more achievable approach for copycat to support right now. This will probably be a combination of:

  • (a) allowing copycat to be instantiated with user-defined locale data;
  • (b) allowing copycat API methods to be overridden entirely for each user-defined locale (for the cases where the way things are formatted differs considerably enough per-locale to warrant being defined entirely separately)

Will still take quite some reworking to support that, but long term it may be more manageable than full locale support for what I think is now a community-maintained project.

@ctholho as someone using copycat, how would that approach sit with you?

@ctholho
Copy link
Contributor

ctholho commented Oct 10, 2024

Thanks for your response @justinvdm.

For me "supporting locales" and "allowing users to plug in their own locale support" comes down to the same. I don't think copycat should burden itself with maintaining static copies of all the locales FakerJS offers.

But instantiating copycat with a custom locale is a great way to go forward. Pinning the FakerJS version to guarantee deterministic seed generation for e.g. tests would then become something users have to look out for.

How would you imagine (b) being used in an example?

@justinvdm
Copy link
Collaborator

For me "supporting locales" and "allowing users to plug in their own locale support" comes down to the same. I don't think copycat should burden itself with maintaining static copies of all the locales FakerJS offers.

Good to know this!

How would you imagine (b) being used in an example?

I was imagining something to this effect:

const copycat = defineLocale('jp', {
  // for when providing the locale data is enough
  data: {
    address: { city_name: [...] }
  },
  // for when the API methods themselves need to be overridden (it'll still need to conform to copycat's own API - we can enforce this with typescript)
  overrides: {
    fullName: (input) => { ... }  
  }
})

@ctholho
Copy link
Contributor

ctholho commented Oct 10, 2024

Reflecting on this I think I have to differentiate my previous comment. Copycat can hopefully refrain from copying the hundreds of lists of words Faker offers, but I hope functions that need a locale override can get a place in copycat.

In your example, I'm wondering how the first string argument of defineLocale() comes to fruition? Do you mean building locales like fakerJS has with their custom locales? I think the flexibility would be great.

On the other hand: I was thinking about an easier way for the cases when there is already a locale in faker. (which is true for most commonly used locales). I guess this is what you refer to with (a).

import { copycat } from '@snaplet/copycat'
import { fakerJP } from '@faker-js/faker'

const cc = copycat.setLocale('jp', { data: fakerJP })

setLocale takes two arguments:

  • the first is a string enum of locales that copycat offers
  • the second is a data provider that gives us the lists of strings Faker offers.

This way you could do something funny with const cc = copycat.setLocale('fr', { data: fakerJP }) which would offer french overrides and japanese data. Weird, but whatever floats your boat, I guess. :)

As discussed before in this project, it's prudent to be uncoupled from potentially breaking updates from fakerJS. However, I think letting users manage this on their own is totally ok.

Because right now copycat comes with fakerJS as dependency it could be even easier with

import { copycat } from '@snaplet/copycat'

const cc = copycat.setLocaleInconstant('jp')

But I'm not married to that idea.

@justinvdm
Copy link
Collaborator

justinvdm commented Oct 14, 2024

Hi @ctholho, thank you for giving this thought.

In your example, I'm wondering how the first string argument of defineLocale() comes to fruition? Do you mean building locales like fakerJS has with their custom locales? I think the flexibility would be great.

To be honest I didn't give that first argument enough thought - I was imagining it just being used as some handle to identify/manage the locale (e.g. for error messages), but it actually doesn't sound too important.

On the other hand: I was thinking about an easier way for the cases when there is already a locale in faker.

Good point, I actually didn't think of that possibility. What I'm worried about is faker changing the naming/structure for the locale data though => we're back to the same problem with a dependence on faker, just now a more implicit one.

That said, we can have data expect a shape matching what faker currently exposes. The consequence is that if the naming or structure of the locale data in faker happens to change, the user would have to take care of massaging the data into something copycat can use as input. That isn't great, but arguably still better than being dependent on faker.

@ctholho what do you think of this API below? If I've been following well enough, I think it would solve what you're needing to do, and be a good start for locale support in copycat. Let me know and I'll find time to work on this.

// 1. returns a new instance of copycat that uses the given locale data and overrides (no mutating happens to existing copycat instances)
const copycatA = defineLocale({
  // 2. for when providing the locale data is enough
  // 3. we can take in a shape that matches faker's own locale data's (current) shape, for convenience
  data: {
    address: { city_name: [...] }
  },
  // 4. for when the API methods themselves need to be overridden (it'll still need to conform to copycat's own API - we can enforce this with typescript)
  overrides: {
    fullName: (input) => { ... }  
  }
})

// 5. and to support composition/fallbacks/overriding like faker:
// merge the locale data and methods in A and B, where B overrides A (=> A is the fallback)
const copycatB = defineLocale({ ... })
const copycatComposed = composeLocales([copycatA, copycatB])

@ctholho
Copy link
Contributor

ctholho commented Oct 16, 2024

You're highlighting a fair point. I think this will work nicely.

Additionally I'm just thinking, wouldn't this also mean we could expose all of fakers static data functions automatically this way? There's a bunch of locale's content (also english), which would mean to simply oneOf() over an array.

If you think you can delegate some stuff, I'm happy to 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