Skip to content

randNickname

Generates nicknames and returns count of them as strings. Each one is an everyday word with something added to it: an adjective in front, a verb in front, a second word behind, or a possessive between the two. Person names are never used. With output: 'detail' it reports the words it used instead.

javascript
import { randNickname } from 'randino';

randNickname();
// ['MistyOwl']
dart
import 'package:randino/randino.dart';

randNickname();
// ['MistyOwl']
python
from randino import rand_nickname

rand_nickname()
# ['MistyOwl']

Options

Every option is optional, and the defaults are what the empty call above uses.

OptionTypeDefaultDescription
languageWordLanguageOptionWordLanguage?WordLanguageOption | None'all'nullNoneLanguage of the generated nicknames. 'all'null"all" mixes every supported language, picking one per nickname.
themeWordThemeOptionWordTheme?WordThemeOption'all'null"all"What the nickname is about. See Themes.
slotsWordSlotOptionSet<WordSlot>?WordSlotOption'all'null"all"Which shapes to accept, by what they put beside the noun. See Picking the shape.
countnumberintint1How many nicknames to return. Clamped to 010000.
realismRandRealism<Lang js="'real'" dart="RandRealism.real" py="\"real\"" />real uses real words, invented builds words that only read like the language, and mixed decides per word.
vocabularyRandVocabulary<Lang js="'full'" dart="RandVocabulary.full" py="\"full\"" />How common the noun the nickname is built around has to be: basic, common or full. See Common words.
minLengthminLengthmin_lengthnumberint?int | NonelanguageMinimum length in characters.
maxLengthmaxLengthmax_lengthnumberint?int | NonelanguageMaximum length in characters.
wordSeparatorwordSeparatorword_separatorstringString?str | NonelanguagePlaced between the words. Counts toward the length range. Defaults to running them together.
startsWithstartsWithstarts_withstringString?strnull""Keep only nicknames whose first character is this one.
uniquebooleanboolboolfalsefalseFalseNever return the same nickname twice. May return fewer than count once the pools run out of combinations.
outputRandOutputRandOutput'value'"value"Strings, or a NicknameDetail per nickname. Dart has no such parameter — see the detail output.
random() => numberRandom?Callable[[], float] | NonenullNoneWhere the randomness comes from — see Choosing the source. Defaults to the platform's ordinary generator.

The detail output

output: 'detail' reports the pieces each nickname was built from instead of returning a string: the words in order, the language and the theme. Use it to highlight the base word, or to group by theme.

Dart spells this as a second function, randNicknameDetails, because it has no way to make one function's return type depend on an argument. It takes the same parameters as randNickname.

javascript
import { randNickname } from 'randino';

randNickname({ language: 'en', output: 'detail' });
// [{
//   nickname: 'MistyOwl',
//   words: ['Misty', 'Owl'],
//   slots: ['adjective', 'noun'],
//   language: 'en',
//   theme: 'animal'
// }]
dart
import 'package:randino/randino.dart';

randNicknameDetails(language: WordLanguage.en);
// [NicknameDetail(MistyOwl, [Misty, Owl], en, animal)]
randNicknameDetails(language: WordLanguage.en).first.slots;
// [WordSlot.adjective, WordSlot.noun]
python
from randino import rand_nickname

rand_nickname(language="en", output="detail")
# [NicknameDetail(nickname='MistyOwl', words=('Misty', 'Owl'),
#                 slots=('adjective', 'noun'), language='en', theme='animal')]
FieldTypeDescription
nicknamestringStringstrThe finished nickname.
wordsstring[]List<String>tuple[str, ...]The words it is made of, in order — the words only.
slotsWordSlot[]List<WordSlot>tuple[WordSlot, ...]What each word does in the shape, at the same index as words.
languageWordLanguageThe language this nickname was generated in.
themeWordTheme | nullWordTheme?WordTheme | NoneTheme of the base word, or null when that word is not one the generator knows.

slots lines up with words: the word at index 2 is the thing slots[2] names. Every shape has exactly one noun, and the rest is whatever the shape put beside it.

words holds the words and nothing else. A shape that needs a particle between two of them carries it in nickname alone, so 사자의눈물 reports ['사자', '눈물'] and joining the two back together does not reproduce it. Read nickname for the finished string, and words for what it was built from.

About theme

The theme is reported, not asserted. A word drawn from a theme reports it; an invented word is looked up across every theme, because it can spell a real one by accident, and reports null when it is found nowhere.

Two coincidences follow from that, and both are worth expecting rather than treating as bugs. A word can be both a modifier and a noun, as Marble is, and an invented word can spell a real one by accident: the syllable templates spell Snake now and then, so a nickname built at realism: 'invented' can come back with theme set to animal.

Examples

One language at a time

javascript
randNickname({ language: 'ko', count: 4 });
// ['오래된곰', '영원한도마뱀', '귀여운신화다발', '노을']

randNickname({ language: 'en', count: 4 });
// ['FoggyHillside', 'CraneVoyage', 'TinyLeopardCloak', 'MathematicsShard']

randNickname({ language: 'ja', count: 4 });
// ['小さな雨', '海の彗星', '鋭いペンギン', '柔らかい記憶']

randNickname({ language: 'zh', count: 4 });
// ['勇敢余烬', '快乐薄雾', '节日', '安静小狗']
dart
randNickname(language: WordLanguage.ko, count: 4);
// ['오래된곰', '영원한도마뱀', '귀여운신화다발', '노을']

randNickname(language: WordLanguage.en, count: 4);
// ['FoggyHillside', 'CraneVoyage', 'TinyLeopardCloak', 'MathematicsShard']

randNickname(language: WordLanguage.ja, count: 4);
// ['小さな雨', '海の彗星', '鋭いペンギン', '柔らかい記憶']

randNickname(language: WordLanguage.zh, count: 4);
// ['勇敢余烬', '快乐薄雾', '节日', '安静小狗']
python
rand_nickname(language="ko", count=4)
# ['오래된곰', '영원한도마뱀', '귀여운신화다발', '노을']

rand_nickname(language="en", count=4)
# ['FoggyHillside', 'CraneVoyage', 'TinyLeopardCloak', 'MathematicsShard']

rand_nickname(language="ja", count=4)
# ['小さな雨', '海の彗星', '鋭いペンギン', '柔らかい記憶']

rand_nickname(language="zh", count=4)
# ['勇敢余烬', '快乐薄雾', '节日', '安静小狗']

A theme

javascript
randNickname({ language: 'en', theme: 'animal', count: 3 });
// ['FloatingFalcon', 'ChewyOtter', 'PlacidMantis']

randNickname({ language: 'en', theme: 'gem', count: 3 });
// ['PolarObsidian', 'AmberGeode', 'QuietMalachite']
dart
randNickname(language: WordLanguage.en, theme: WordTheme.animal, count: 3);
// ['FloatingFalcon', 'ChewyOtter', 'PlacidMantis']

randNickname(language: WordLanguage.en, theme: WordTheme.gem, count: 3);
// ['PolarObsidian', 'AmberGeode', 'QuietMalachite']
python
rand_nickname(language="en", theme="animal", count=3)
# ['FloatingFalcon', 'ChewyOtter', 'PlacidMantis']

rand_nickname(language="en", theme="gem", count=3)
# ['PolarObsidian', 'AmberGeode', 'QuietMalachite']

The twenty-nine themes, and what each one holds, are on Themes.

Picking the shape

slots names what a shape may put beside the noun, and the shapes that use none of it are dropped. A shape qualifies when it uses at least one of the slots, so naming two asks for either and leaves the choice to chance, which is what ['adjective', 'action'] below does.

javascript
randNickname({ language: 'en', slots: 'action', count: 3 });
// ['CountingHarmonics', 'HaulingBurrito', 'FloatingSelkie']

randNickname({ language: 'en', slots: 'part', count: 3 });
// ['CardTrack', 'DreamyBlackthornBreeze', 'ThrowingParachuteHorn']

randNickname({ language: 'en', slots: ['adjective', 'action'], count: 3 });
// ['JadeOdyssey', 'DownyBreeze', 'MidnightFinchLair']

randNickname({ language: 'en', slots: 'none', count: 3 });
// ['Captain', 'Bronze', 'Clown']
dart
randNickname(language: WordLanguage.en, slots: {WordSlot.action}, count: 3);
// ['CountingHarmonics', 'HaulingBurrito', 'FloatingSelkie']

randNickname(language: WordLanguage.en, slots: {WordSlot.part}, count: 3);
// ['CardTrack', 'DreamyBlackthornBreeze', 'ThrowingParachuteHorn']

randNickname(
  language: WordLanguage.en,
  slots: {WordSlot.adjective, WordSlot.action},
  count: 3,
);
// ['JadeOdyssey', 'DownyBreeze', 'MidnightFinchLair']

// The empty set is what `'none'` spells in the other two packages.
randNickname(language: WordLanguage.en, slots: {}, count: 3);
// ['Captain', 'Bronze', 'Clown']
python
rand_nickname(language="en", slots="action", count=3)
# ['CountingHarmonics', 'HaulingBurrito', 'FloatingSelkie']

rand_nickname(language="en", slots="part", count=3)
# ['CardTrack', 'DreamyBlackthornBreeze', 'ThrowingParachuteHorn']

rand_nickname(language="en", slots=("adjective", "action"), count=3)
# ['JadeOdyssey', 'DownyBreeze', 'MidnightFinchLair']

rand_nickname(language="en", slots="none", count=3)
# ['Captain', 'Bronze', 'Clown']

A language answers with what it has. The shapes are the language's own, so not every one of them can answer every request: Spanish, Italian, German and Russian have no trailing-noun shape, because cola de gato needs a preposition rather than a joiner. Asking one of them for part falls back to every shape it does have, the same way a length range too narrow for a shape is answered with the closest fit rather than with an error.

javascript
randNickname({ language: 'de', slots: 'part', count: 3 });
// ['klingender Obstler', 'freier Geysir', 'wilder Drache']
dart
randNickname(language: WordLanguage.de, slots: {WordSlot.part}, count: 3);
// ['klingender Obstler', 'freier Geysir', 'wilder Drache']
python
rand_nickname(language="de", slots="part", count=3)
# ['klingender Obstler', 'freier Geysir', 'wilder Drache']

With no language named, the ones that can answer are preferred over the ones that cannot, so asking every language for a trailing noun draws from the five that have one.

A separator between the words

javascript
randNickname({ language: 'en', wordSeparator: ' ', count: 4 });
// ['Soldier', 'Hollow Petal', 'Syrupy Mica Tale', 'Spinning Cathedral']

randNickname({ language: 'en', wordSeparator: '-', count: 4 });
// ['Headphone', 'Soft-Bat', 'Genial-Moose-Cove', 'Dreamy-Umbrella-Halo']
dart
randNickname(language: WordLanguage.en, wordSeparator: ' ', count: 4);
// ['Soldier', 'Hollow Petal', 'Syrupy Mica Tale', 'Spinning Cathedral']

randNickname(language: WordLanguage.en, wordSeparator: '-', count: 4);
// ['Headphone', 'Soft-Bat', 'Genial-Moose-Cove', 'Dreamy-Umbrella-Halo']
python
rand_nickname(language="en", word_separator=" ", count=4)
# ['Soldier', 'Hollow Petal', 'Syrupy Mica Tale', 'Spinning Cathedral']

rand_nickname(language="en", word_separator="-", count=4)
# ['Headphone', 'Soft-Bat', 'Genial-Moose-Cove', 'Dreamy-Umbrella-Halo']

Its length counts toward minLengthminLengthmin_length and maxLengthmaxLengthmax_length. Pass it to nicknameLengthRange to see what is left.

A unique suffix

There is no option for one. randSuffix attaches a random token to whatever you hand it, these nicknames included.

javascript
randSuffix(randNickname({ language: 'en', count: 3 }));
// ['RoundSeason_RVBnC', 'RowdyDusk_dwtu5', 'RovingLakeShard_QqMVH']

randSuffix(randNickname({ language: 'en', count: 2 }), { length: 8, separator: '-' });
// ['GenialFern-9xq9SgJf', 'BoldBicycle-PGc4keqM']
dart
randSuffixAll(randNickname(language: WordLanguage.en, count: 3));
// ['RoundSeason_RVBnC', 'RowdyDusk_dwtu5', 'RovingLakeShard_QqMVH']

randSuffixAll(
  randNickname(language: WordLanguage.en, count: 2),
  length: 8,
  separator: '-',
);
// ['GenialFern-9xq9SgJf', 'BoldBicycle-PGc4keqM']
python
rand_suffix(rand_nickname(language="en", count=3))
# ['RoundSeason_RVBnC', 'RowdyDusk_dwtu5', 'RovingLakeShard_QqMVH']

rand_suffix(rand_nickname(language="en", count=2), length=8, separator="-")
# ['GenialFern-9xq9SgJf', 'BoldBicycle-PGc4keqM']

Because it happens afterwards, minLengthminLengthmin_length / maxLengthmaxLengthmax_length describe the whole nickname rather than the part in front of a suffix.

Invented words

javascript
randNickname({ language: 'en', realism: 'invented', count: 3 });
// ['Duhusk', 'DresaelSlobru', 'BroureexGrosex']
dart
randNickname(language: WordLanguage.en, realism: RandRealism.invented, count: 3);
// ['Duhusk', 'DresaelSlobru', 'BroureexGrosex']
python
rand_nickname(language="en", realism="invented", count=3)
# ['Duhusk', 'DresaelSlobru', 'BroureexGrosex']

Highlighting the base word

javascript
for (const { words, theme } of randNickname({ language: 'en', count: 3, output: 'detail' })) {
	console.log(words.join(' + '), theme);
}
// Grumpy + Zither music
// Fierce + Printer product
// Endless + Obsidian + Wing gem
dart
for (final detail in randNicknameDetails(language: WordLanguage.en, count: 3)) {
  print('${detail.words.join(' + ')} ${detail.theme?.name}');
}
// Grumpy + Zither music
// Fierce + Printer product
// Endless + Obsidian + Wing gem
python
for detail in rand_nickname(language="en", count=3, output="detail"):
    print(" + ".join(detail.words), detail.theme)
# Grumpy + Zither music
# Fierce + Printer product
# Endless + Obsidian + Wing gem

Storing the readable part and the discriminator

A sign-up flow usually wants the readable part and the collision-breaking part in two columns, so that a later rename can keep one and replace the other. randSuffix is what makes the second one, and it never has to be pulled back out of the first:

javascript
const [detail] = randNickname({ language: 'en', output: 'detail' });
const discriminator = randSuffix('', { separator: '' });

await users.insert({
	handle: `${detail.nickname}_${discriminator}`,
	display: detail.nickname,
	discriminator
});
dart
final detail = randNicknameDetails(language: WordLanguage.en).first;
final discriminator = randSuffix('', separator: '');

await users.insert(
  handle: '${detail.nickname}_$discriminator',
  display: detail.nickname,
  discriminator: discriminator,
);
python
detail = rand_nickname(language="en", output="detail")[0]
discriminator = rand_suffix("", separator="")

await users.insert(
    handle=f"{detail.nickname}_{discriminator}",
    display=detail.nickname,
    discriminator=discriminator,
)

Grouping by theme

javascript
const byTheme = {};

for (const detail of randNickname({ language: 'en', count: 100, output: 'detail' })) {
	(byTheme[detail.theme] ??= []).push(detail.nickname);
}
dart
final byTheme = <WordTheme?, List<String>>{};

for (final detail in randNicknameDetails(language: WordLanguage.en, count: 100)) {
  byTheme.putIfAbsent(detail.theme, () => <String>[]).add(detail.nickname);
}
python
from collections import defaultdict

by_theme: defaultdict[WordTheme | None, list[str]] = defaultdict(list)

for detail in rand_nickname(language="en", count=100, output="detail"):
    by_theme[detail.theme].append(detail.nickname)

See also

  • randSuffix — the random token, for when a nickname has to be collision-free.
  • Themes — the twenty-nine slices of vocabulary a nickname is built from.
  • nicknameLengthRange — every length a language can produce.

Released under the MIT License