Help us shape the future of Go! Take the 2026 Go Developer Survey

normalize

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Oct 5, 2026 License: BSD-3-Clause Imports: 1 Imported by: 0

README

go-ruby-unicode-normalize/unicode-normalize

unicode-normalize — go-ruby-unicode-normalize

Docs License Go Coverage

A pure-Go (no cgo) reimplementation of Ruby's String#unicode_normalize and String#unicode_normalized? — the unicode_normalize standard library — matching MRI 4.0.5 (Unicode 17.0.0) byte-for-byte. It normalizes UTF-8 text to any of the four Unicode Standard Annex #15 forms (NFC, NFD, NFKC, NFKD) and reports whether a string is already normalized, without any Ruby runtime.

It is the Unicode-normalization backend for go-embedded-ruby, but is a standalone, reusable module — a sibling of go-ruby-regexp (the Onigmo engine), go-ruby-marshal and go-ruby-yaml.

API

import normalize "github.com/go-ruby-unicode-normalize/unicode-normalize"

// Normalize returns s in the given form (NFC is the default form for Ruby's
// unicode_normalize when called with no argument).
func Normalize(s string, form normalize.Form) string

// IsNormalized reports whether s is already in the given form
// (Ruby's unicode_normalized?).
func IsNormalized(s string, form normalize.Form) bool
normalize.Normalize("é", normalize.NFC)   // "é"  (composes e + acute)
normalize.Normalize("é", normalize.NFD)          // "é" (decomposes)
normalize.Normalize("fi", normalize.NFKC)         // "fi"  (ligature -> ascii)
normalize.Normalize("①", normalize.NFKC)         // "1"
normalize.Normalize("A", normalize.NFKC)         // "A"   (full-width -> ascii)
normalize.IsNormalized("é", normalize.NFC)       // true  (composed)
normalize.IsNormalized("é", normalize.NFC) // false (decomposed)

The four forms are the normalize.Form values NFC, NFD, NFKC, NFKD, corresponding to Ruby's :nfc, :nfd, :nfkc, :nfkd. The zero value is NFC, mirroring Ruby's default. Form.String returns the Ruby symbol name (":nfc", …) for diagnostics and for the rbgo binding layer.

What the host binds. This library is the deterministic, interpreter-free core. The rbgo binding wires Normalize to String#unicode_normalize and IsNormalized to String#unicode_normalized?, defaults the form to :nfc, maps the :nfc/:nfd/:nfkc/:nfkd symbols to Form, raises ArgumentError on an unknown form symbol and on invalid UTF-8 byte sequences (matching MRI), and otherwise hands the bytes straight through.

How it works

The heavy lifting is delegated to golang.org/x/text/unicode/norm, a pure-Go (cgo-free) implementation of the same Unicode standard. Both MRI 4.0.5 and x/text target Unicode 17.0.0, so they agree across the official NormalizationTest corpus and the Ruby-specific edge cases (Hangul composition, full/half-width and ligature compatibility, combining-mark ordering).

x/text selects its Unicode tables by build tag: Unicode 17.0.0 under go1.27, Unicode 15.0.0 otherwise. This module requires Go 1.27.1, so it is always built against the Unicode 17.0.0 tables and needs no local patching; TestUnicodeVersion fails if that precondition ever changes. (Before the 1.27.1 floor, a small override layer patched the Unicode 16.0/17.0 additions into the 15.0.0 tables; it became a no-op under the 17.0.0 tables and was removed.)

Tests & coverage

Two complementary layers, both run in CI on every supported platform:

  • Deterministic, ruby-free tests pin the MRI-observed results and alone keep statement coverage at 100%, so the Windows and cross-architecture lanes (where MRI is absent) still pass the gate.
  • MRI oracle tests run differentially against the ruby binary: a curated edge-case corpus, a combining-sequence fuzz, and an exhaustive sweep of every assigned scalar value × all four forms. They skip themselves when ruby is absent and gate on RUBY_VERSION >= "4.0" (Unicode 17.0.0); the oracle script binmodes stdin/stdout so the UTF-8 bytes survive intact.
go test -race -coverpkg=./... -coverprofile=cover.out ./...   # 100.0%
go test -short ./...                                          # skip the exhaustive sweep

CI enforces the 100% coverage gate plus builds and tests on three operating systems (Linux, macOS, Windows) and all six supported 64-bit architectures (amd64, arm64, riscv64, loong64, ppc64le, s390x). The package is cgo-free (CGO_ENABLED=0).

License

BSD-3-Clause — see LICENSE. Copyright (c) 2026, the go-ruby-unicode-normalize/unicode-normalize authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

Documentation

Overview

Package normalize implements MRI-compatible Unicode normalization, matching Ruby's String#unicode_normalize / String#unicode_normalized? (the unicode_normalize standard library).

The four normalization forms NFC, NFD, NFKC and NFKD are the four forms defined by Unicode Standard Annex #15. Ruby's String#unicode_normalize accepts the corresponding symbols :nfc, :nfd, :nfkc and :nfkd, with :nfc the default when no argument is given.

The work is delegated to golang.org/x/text/unicode/norm, a pure-Go (CGO=0) implementation of the same Unicode standard. MRI 4.0.5 normalizes against Unicode 17.0.0. x/text ships Unicode 17.0.0 tables but selects them by the go1.27 build tag (Unicode 15.0.0 otherwise); this module requires go 1.27.1, so the Unicode 17.0.0 tables are the only ones it can be built with, and TestUnicodeVersion fails if that ever stops being true. The oracle test suite verifies the agreement differentially against MRI.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsNormalized

func IsNormalized(s string, form Form) bool

IsNormalized reports whether s is already in the given normalization form, matching the result of Ruby's s.unicode_normalized?(form). A string is normalized for a form iff normalizing it leaves it unchanged.

func Normalize

func Normalize(s string, form Form) string

Normalize returns s normalized to the given form, matching the result of Ruby's s.unicode_normalize(form). Bytes that are not valid UTF-8 are passed through unchanged, as x/text does; the Ruby binding layer is responsible for raising ArgumentError on invalid byte sequences to match MRI.

Types

type Form

type Form int

Form selects a Unicode normalization form. The zero value is NFC, matching the default form of Ruby's String#unicode_normalize.

const (
	// NFC is Normalization Form C (canonical decomposition followed by
	// canonical composition). It is the default form, matching :nfc.
	NFC Form = iota
	// NFD is Normalization Form D (canonical decomposition), matching :nfd.
	NFD
	// NFKC is Normalization Form KC (compatibility decomposition followed by
	// canonical composition), matching :nfkc.
	NFKC
	// NFKD is Normalization Form KD (compatibility decomposition), matching :nfkd.
	NFKD
)

func (Form) String

func (f Form) String() string

String reports the Ruby symbol name of the form (":nfc", ":nfd", ":nfkc" or ":nfkd"). It is used in diagnostics and by the rbgo binding layer.

Jump to

Keyboard shortcuts

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