Locale-Aware Data
Locale-Aware Data
Most identity, address, text, and money generators support locale via GeneratorConfig or locale constructors.
Example
GeneratorConfig de = GeneratorConfig.builder()
.locale(Locale.GERMANY)
.seed(7L)
.build();
String fullName = new FullNameGenerator(de).generate();
String city = new CityGenerator(de).generate();
String street = new StreetAddressGenerator(de).generate();
String money = new MoneyGenerator(de).generate();
String sentence = new SentenceGenerator(de).generate();
Recommended pattern
- Build one
GeneratorConfigper test fixture profile. - Reuse it across generators to keep locale + seed consistent.
Custom locale bundles
For application-level locales or test-only overrides, you can register one locale pack instead of wiring every provider registry by hand.
Locale locale = Locale.of("es", "MX");
LocaleDataBundle bundle = LocaleDataBundle.builder(locale)
.firstNames(new String[]{"Mateo"}, new String[]{"Sofía"})
.lastNames("Hernández", "Ramírez")
.genderLabels("Hombre", "Mujer")
.cities("Ciudad de México", "Guadalajara")
.states(new String[]{"Jalisco", "Ciudad de México"}, new String[]{"JAL", "CDMX"})
.countries("México")
.streetAddress(new String[]{"Juárez"}, new String[]{"Av"}, new String[]{"Avenida"})
.build();
DataRegistryContext context = DataRegistryContext.builder()
.isolated()
.registerLocaleData(bundle)
.build();
GeneratorConfig mx = GeneratorConfig.builder()
.locale(locale)
.registryContext(context)
.seed(7L)
.build();
String firstName = new FirstNameGenerator(mx).generate(Gender.MALE);
String city = new CityGenerator(mx).generate();
String street = new StreetAddressGenerator(mx).generate();
Register bundles through DataRegistryContext.builder().registerLocaleData(...) on the GeneratorConfig that consumes them. 2.0.0 removed process-wide registration (registerGlobal() and the per-registry register(...) methods): custom vocabulary is always scoped to a configuration and cannot leak between tests.
Registering a single vocabulary
A bundle covers the person and address vocabularies. Any vocabulary, including the ones a bundle
does not cover, can be registered on its own under its Vocabulary key; the provider implements
that vocabulary’s data interface:
DataRegistryContext context = DataRegistryContext.builder()
.register(Vocabulary.WEATHER, new RainyWeather(Locale.UK)) // implements WeatherDataProvider
.build();
GeneratorConfig config = GeneratorConfig.builder().registryContext(context).build();
WeatherDataProvider weather = context.provider(Vocabulary.WEATHER, Locale.UK);
Set<String> keys = context.registeredKeys(Vocabulary.WEATHER); // "en_GB", "en" and the bundled keys
Vocabulary.all() lists the 41 built-in vocabularies. Lookups resolve the exact
language_COUNTRY key first, then the bundled data for that key, then the language entry (a
regional registration also serves its language) and finally the bundled language data;
isolated() contexts skip the bundled data. Vocabulary.NATIONAL_ID is the one exception: for a
locale with a country, the third step is the registration for that country whatever its language,
because identity-number formats are national; language-only locales use the language entry.
Generators you write yourself can define their own vocabulary with
Vocabulary.custom("colour", ColourProvider.class), where ColourProvider extends
LocaleDataProvider; keep the key in a constant, because keys compare by identity.
Locale quality tiers
SupportedLocale now exposes built-in dataset quality metadata so callers can tell whether a locale is backed by native data or a fallback.
NATIVE_DATASET: resource-backed identity/address data and profession data are native for that locale.CURATED_FALLBACK_DATASET: a provider uses a deliberate fallback dataset chosen to stay usable until native data exists.ALIAS_FALLBACK_DATASET: resource-backed providers reuse another supported locale’s packaged dataset directly.
Example:
SupportedLocale locale = SupportedLocale.fromLocale(Locale.of("nl", "NL")).orElseThrow();
LocaleDataQualityTier overall = locale.qualityTier();
LocaleDataQualityTier resourceTier = locale.resourceDataTier();
LocaleDataQualityTier professionTier = locale.professionDataTier();
Optional<SupportedLocale> resourceFallback = locale.resourceFallbackLocale();
Current built-in quality split:
- 35 native locale datasets: the original 20 plus
da_DK,fi_FI,hu_HU,ro_RO,sk_SK,uk_UA,bg_BG,hr_HR,el_GR,th_TH,vi_VN,id_ID,ms_MY,he_IL, andca_ES - 15 curated fallback variants:
en_CA,en_NZ,en_IE,en_IN,en_ZA,fr_CA,fr_BE,fr_CH,de_AT,de_CH,es_MX,es_AR,pt_PT,nl_BE, andzh_TW - 50 supported locale variants in total
Fallback tiers are productized compatibility behavior, not hidden implementation details. Each fallback variant exposes its canonical resource locale through resourceFallbackLocale().
The 35 native datasets satisfy the current resource coverage gates. The 15 regional variants remain explicitly fallback-backed until equivalent native datasets are contributed.
Locale contribution quality bar
Native locale additions are expected to ship real dataset coverage, not only registration hooks.
The current built-in quality gates enforce:
- minimum dataset sizes for names, cities, states, streets, titles, suffixes, and professions
- no blank values in validated built-in arrays
- duplicate ratio no worse than
5% - script sanity coverage for the locale’s expected writing system
Contributor-facing details for built-in resource layout, validation rules, and application-level custom locale bundles are documented in the repository-level docs/locale-contribution-guide.md.