emailnormalizer

package module
v1.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 12, 2026 License: MIT Imports: 4 Imported by: 0

README

go-email-normalizer

CI codecov Go Reference

Fork of dimuska139/go-email-normalizer

This is Golang library for providing a canonical representation of email address. It allows to prevent multiple signups. This module contains some popular providers but you can easily append others.

What this fork adds on top of upstream:

  • Normalize2: validates the input (RFC 5322-compatible regex) instead of silently passing through malformed addresses, and returns a NormalizeResult listing every individual transformation applied (see Change values), so callers can audit or log exactly what changed instead of only seeing the final address.
  • An RFC compliance audit (NORMALIZATION.md) documenting, per provider, which transformations are applied and which RFC justifies them.

RFC compliance note: All normalization transformations (dot removal, + tag stripping, domain alias resolution) are applied only for specific, known providers where the behavior is documented. For unknown domains the local part is returned unchanged, consistent with RFC 5321 which treats the local part as opaque to external systems. See NORMALIZATION.md for the full audit and per-provider rule reference.

Usage

Normalize

Normalize returns the canonical form of an email address as a plain string.

package main

import (
	"fmt"
	"strings"
	normalizer "github.com/bobadilla-tech/go-email-normalizer"
)

type customRule struct{}

func (rule *customRule) ProcessUsername(username string) string {
	return strings.Replace(username, "-", "", -1)
}

func (rule *customRule) ProcessDomain(domain string) string {
	return domain
}

func main() {
	n := normalizer.NewNormalizer()
	fmt.Println(n.Normalize("vasya+pupkin@gmail.com")) // vasya@gmail.com
	fmt.Println(n.Normalize("t.e-St+vasya@gmail.com")) // te-st@gmail.com
	fmt.Println(n.Normalize("John+Brown@yahoo.com"))   // john+brown@yahoo.com
	fmt.Println(n.Normalize("John-Brown@yahoo.com"))   // john@yahoo.com
	fmt.Println(n.Normalize("t.e-St+@googlemail.com")) // te-st@gmail.com
	fmt.Println(n.Normalize("t.e-St+@google.com"))     // te-st@google.com

	n.AddRule("customrules.com", &customRule{})
	fmt.Println(n.Normalize(" tE-S-t@CustomRules.com.")) // tESt@customrules.com
}
Normalize2

Normalize2 accepts any string, validates it as an email address using an RFC 5322-compatible regex (sourced from go-playground/validator), and returns a NormalizeResult paired with an error. The validator requires a dot-separated domain (e.g. gmail.com), so inputs like user@gmailcom are rejected. Trailing whitespace and trailing dots are stripped before validation, so those are accepted. If the input is not a valid email address, the result is zero-valued and the error is non-nil. When the call succeeds, the result pairs the canonical address with a list of every transformation applied, in order. Each Change value appears at most once.

n := normalizer.NewNormalizer()

result, err := n.Normalize2("First.Last+tag@googlemail.com")
if err != nil {
	log.Fatal(err)
}
fmt.Println(result.Normalized) // firstlast@gmail.com
fmt.Println(result.Changes)
// [lowercase removed_dots removed_plus_tag canonicalized_domain]

result, err = n.Normalize2("User.Name_test-sub+spam@protonmail.com")
if err != nil {
	log.Fatal(err)
}
fmt.Println(result.Normalized) // usernametestsub@protonmail.com
fmt.Println(result.Changes)
// [lowercase removed_dots removed_underscores removed_hyphens removed_plus_tag]

result, err = n.Normalize2("Test+User-Name@ya.ru")
if err != nil {
	log.Fatal(err)
}
fmt.Println(result.Normalized) // testuser.name@yandex.ru
fmt.Println(result.Changes)
// [lowercase removed_plus_signs replaced_hyphens_with_dots canonicalized_domain]

// Invalid input — no "@" sign.
_, err = n.Normalize2("notanemail")
fmt.Println(err) // invalid email address: "notanemail"

// Invalid input — dotless domain is rejected.
_, err = n.Normalize2("not-an-email@gmailcom")
fmt.Println(err) // invalid email address: "not-an-email@gmailcom"
Change values
Constant Value Produced by
ChangeTrimmedWhitespace trimmed_whitespace leading/trailing whitespace stripped
ChangeRemovedTrailingDot removed_trailing_dot trailing dot stripped from raw input
ChangeLowercase lowercase username or domain uppercased → lowercased
ChangeRemovedDots removed_dots dots removed from username (Google, Protonmail)
ChangeRemovedUnderscores removed_underscores underscores removed from username (Protonmail)
ChangeRemovedHyphens removed_hyphens hyphens removed from username (Protonmail)
ChangeReplacedHyphensWithDots replaced_hyphens_with_dots hyphens replaced with dots in username (Yandex)
ChangeRemovedPlusTag removed_plus_tag +tag subaddress stripped (Google, Apple, Fastmail, Protonmail)
ChangeRemovedPlusSigns removed_plus_signs all + characters removed (Microsoft, Rackspace, Rambler, Yandex, Zoho)
ChangeRemovedSubaddress removed_subaddress -tag subaddress stripped (Yahoo)
ChangeCanonicalisedDomain canonicalized_domain domain rewritten to canonical form (e.g. googlemail.com → gmail.com)

Used in production

This library is used in production by Bobadilla Tech clients and powers the email normalization endpoint of the Requiem API. It is a core component of our backend email handling infrastructure.

Supported providers

  • Apple
  • Fastmail
  • Google
  • Microsoft
  • Protonmail
  • Rackspace
  • Rambler
  • Yahoo
  • Yandex
  • Zoho

Also you can integrate other rules using AddRule function (see an example above)

For a detailed breakdown of which transformations each provider applies and the RFC standards behind them, see NORMALIZATION.md.

Documentation

Index

Constants

View Source
const (
	// ChangeTrimmedWhitespace is applied when leading or trailing whitespace is
	// removed from the raw input.
	ChangeTrimmedWhitespace = rules.ChangeTrimmedWhitespace

	// ChangeRemovedTrailingDot is applied when one or more trailing dots are
	// stripped from the raw input.
	ChangeRemovedTrailingDot = rules.ChangeRemovedTrailingDot

	// ChangeLowercase is applied when the username or domain contains uppercase
	// letters that are converted to lower case.
	ChangeLowercase = rules.ChangeLowercase

	// ChangeRemovedDots is applied when dot characters are removed from the
	// username (e.g. Google, Protonmail).
	ChangeRemovedDots = rules.ChangeRemovedDots

	// ChangeRemovedUnderscores is applied when underscore characters are removed
	// from the username (e.g. Protonmail).
	ChangeRemovedUnderscores = rules.ChangeRemovedUnderscores

	// ChangeRemovedHyphens is applied when hyphen characters are removed from
	// the username (e.g. Protonmail).
	ChangeRemovedHyphens = rules.ChangeRemovedHyphens

	// ChangeReplacedHyphensWithDots is applied when hyphen characters in the
	// username are replaced with dots (e.g. Yandex).
	ChangeReplacedHyphensWithDots = rules.ChangeReplacedHyphensWithDots

	// ChangeRemovedPlusTag is applied when a plus-sign subaddress ("+tag") is
	// stripped from the end of the username (e.g. Google, Apple, Fastmail,
	// Protonmail).
	ChangeRemovedPlusTag = rules.ChangeRemovedPlusTag

	// ChangeRemovedPlusSigns is applied when plus-sign characters are removed
	// from the username without subaddress semantics — every "+" is deleted
	// regardless of position (e.g. Microsoft, Rackspace, Rambler, Yandex, Zoho).
	ChangeRemovedPlusSigns = rules.ChangeRemovedPlusSigns

	// ChangeRemovedSubaddress is applied when a dash-delimited subaddress
	// ("-tag") is stripped from the end of the username (e.g. Yahoo).
	ChangeRemovedSubaddress = rules.ChangeRemovedSubaddress

	// ChangeCanonicalisedDomain is applied when the domain is rewritten to its
	// canonical form (e.g. googlemail.com → gmail.com, me.com → icloud.com,
	// ya.ru → yandex.ru).
	ChangeCanonicalisedDomain = rules.ChangeCanonicalisedDomain
)

Variables

This section is empty.

Functions

func ValidateEmail added in v1.2.0

func ValidateEmail(email string) error

Types

type Change

type Change = rules.Change

Change is a string enum identifying a single transformation applied to an email address during normalization. It is returned by Normalize2.

type NormalizeResult

type NormalizeResult struct {
	// Normalized is the canonical form of the email address.
	Normalized string

	// Changes lists every transformation that was applied, in the order they
	// were first detected. Each Change value appears at most once.
	Changes []Change
}

NormalizeResult is the return value of Normalize2.

type Normalizer

type Normalizer struct {
	// contains filtered or unexported fields
}

Normalizer : main library object for normalization emails

func NewNormalizer

func NewNormalizer() *Normalizer

NewNormalizer : creates Normalizer instance

func (*Normalizer) AddRule

func (n *Normalizer) AddRule(domain string, strategy NormalizingRule)

AddRule : appends custom normalization rule

func (*Normalizer) Normalize

func (n *Normalizer) Normalize(email string) string

Normalize : converts email to canonical form

func (*Normalizer) Normalize2

func (n *Normalizer) Normalize2(email string) (NormalizeResult, error)

Normalize2 converts any input string to a canonical email address and returns a NormalizeResult that includes both the normalized address and every transformation applied.

Unlike Normalize, Normalize2 validates the input using an RFC 5322-compatible email regex (sourced from github.com/go-playground/validator). It requires a dot-separated domain (e.g. "gmail.com"), so inputs like "user@gmailcom" are rejected. Trailing whitespace and trailing dots are stripped before validation, so those are handled correctly. If the input is not a valid email address, a zero-valued NormalizeResult and a non-nil error are returned.

Rules registered via AddRule that only implement NormalizingRule (not NormalizingRuleWithChanges) are still fully supported — the normalized address will be correct, but per-rule changes will not be reported. Pre-processing changes (whitespace trimming, trailing-dot removal, domain lowercasing) are always tracked regardless of the rule type.

type NormalizingRule

type NormalizingRule interface {
	ProcessUsername(string) string
	ProcessDomain(string) string
}

NormalizingRule : interface for all email normalization rules

type NormalizingRuleWithChanges

type NormalizingRuleWithChanges interface {
	NormalizingRule
	ProcessUsernameWithChanges(username string) (result string, changes []Change)
	ProcessDomainWithChanges(domain string) (result string, changes []Change)
}

NormalizingRuleWithChanges is an optional extension of NormalizingRule. Rules that implement it allow Normalize2 to report the individual transformations applied to the username and domain. Rules added via AddRule that only satisfy NormalizingRule are still supported by Normalize2 — they will not report per-rule changes, but global pre-processing changes (whitespace trimming, trailing-dot removal, domain lowercasing) are always tracked.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL