Skip to content

Go Bindings

The github.com/libraz/go-suzume module provides Go bindings for Suzume. Use it when you want the same Japanese tokenizer from Go services and command-line tools without going through the JavaScript/WASM package.

The bindings are a thin cgo layer over the native C++ core. The core and user dictionaries are embedded in the module with go:embed and made available to the analyzer automatically at startup, so a built binary needs no external dictionary files.

Requirements

  • Go 1.26 or later
  • CGO enabled, with a C++17 compiler (GCC 8+, Clang 10+, Apple Clang 12+)
  • CMake 3.15 or later (for the one-time static-library build)

Unlike the Python wheel, the module does not ship a precompiled binary: the Suzume static library is built once from source on your machine.

Installation

The static library and the dictionaries are both produced from the C++ sources, and neither is published inside the Go module, so go get on its own is not enough. Clone the repository and build it once:

bash
git clone https://github.com/libraz/go-suzume.git
cd go-suzume
make lib    # Fetches the Suzume C++ source and builds libsuzume.a
make test   # Optional: run the tests

Then point your module at the checkout:

bash
go mod edit -replace github.com/libraz/go-suzume=/path/to/go-suzume
go get github.com/libraz/go-suzume

The build writes into the checkout, so keep it in a directory you can write to. The Go module cache is read-only and cannot host the build.

Quick start

The package name is suzume. Create an analyzer with New(), defer Close() so the native handle is released, and iterate over the analyzed morphemes:

go
package main

import (
	"fmt"
	"log"

	"github.com/libraz/go-suzume"
)

func main() {
	s, err := suzume.New()
	if err != nil {
		log.Fatal(err)
	}
	defer s.Close()

	for _, m := range s.Analyze("東京都に住んでいます") {
		fmt.Printf("%s\t%s\t%s\n", m.Surface, m.POS, m.BaseForm)
	}
}

Analyze() returns a []Morpheme slice, or nil when the input produces no morphemes or the call fails. It does not return an error, and LastError() cannot reliably distinguish these cases: the native diagnostic belongs to the current OS thread, while the Go runtime may move the goroutine before it reads the value. Close() is safe to call multiple times, and a finalizer frees the handle as a fallback, but deferring Close() explicitly is the intended pattern.

An instance holds native mutable state and is not safe for concurrent calls. Use one instance per goroutine, or serialize access; separate instances may run concurrently, and creating one is inexpensive.

Embedded dictionaries

At package init, the embedded dictionaries are written to a content-addressed cache directory and exposed to the core via SUZUME_DATA_DIR. If you set SUZUME_DATA_DIR yourself before the program starts, your directory is respected and the embedded copies are not used.

Analysis modes

NewWithExtendedOptions() gives full control over segmentation, lemmatization, and compound merging. Start from DefaultExtendedOptions() — the zero value of ExtendedOptions does not match the library defaults (it would disable lemmatization and case/ヴ preservation):

go
opts := suzume.DefaultExtendedOptions()
opts.Mode = suzume.ModeSearch // Search-oriented segmentation; merges noun compounds
opts.MergeCompounds = true

s, err := suzume.NewWithExtendedOptions(opts)
if err != nil {
	log.Fatal(err)
}
defer s.Close()

The available modes are ModeNormal (default), ModeSearch, and ModeSplit. See Analysis Modes for what each mode does to segmentation.

For the common case of tweaking only normalization, NewWithOptions(Options) takes just the three toggles PreserveVu, PreserveCase, and PreserveSymbols, keeping the library defaults for mode and lemmatization. PreserveSymbols controls punctuation-like SYMBOL tokens; content-bearing symbols and emoji remain OTHER either way.

Morpheme fields

Analyze() returns a slice of Morpheme structs:

FieldTypeDescription
SurfacestringSurface form as it appears in text
POSstringPart of speech in English (UPPERCASE, e.g. NOUN)
BaseFormstringDictionary/base form
POSJastringPart of speech in Japanese (e.g. 名詞)
ConjTypestringConjugation type; may be empty even when IsConjugatable is true
ConjFormstringConjugation form; meaningful when IsConjugatable is true
ExtendedPOSstringStable extended POS code (e.g. VERB_連用)
StartintStart character offset in normalized text
EndintEnd character offset in normalized text
IsUserDictboolTrue when matched from a user dictionary
IsFormalNounboolTrue for formal nouns such as こと and もの
IsLowInfoboolTrue when marked as low information for tag generation
IsUnknownboolTrue when generated as an unknown-word candidate
IsFromDictionaryboolTrue when matched from any dictionary
IsConjugatableboolTrue when the conjugation fields carry meaning
Scorefloat32Candidate score/cost used by the analyzer

IsConjugatable includes auxiliaries as well as verbs and adjectives. A conjugatable morpheme can still have an empty ConjType when no conjugation type applies.

See the API Reference for the full list of POS and ExtendedPOS values.

Tag generation

GenerateTags() extracts keyword tags from text. By default it keeps content words (nouns, verbs, adjectives, adverbs) and filters out particles, auxiliaries, formal nouns, and low-information words:

go
for _, t := range s.GenerateTags("東京都の天気予報を確認する") {
	fmt.Printf("%s (%s)\n", t.Tag, t.POS)
}

Each result is a Tag struct with two fields: Tag (the keyword text) and POS (its part of speech).

GenerateTagsWithOptions() takes a TagOptions struct. Start from DefaultTagOptions() — the zero value of TagOptions disables every exclusion filter, which differs from the library defaults. The POSFilter field is a bitmask built from the POSNoun, POSVerb, POSAdjective, and POSAdverb constants (0 means all):

go
opts := suzume.DefaultTagOptions()
opts.POSFilter = suzume.POSNoun | suzume.POSVerb // Nouns and verbs only
opts.MaxTags = 10                                // Keep the top 10 tags

tags := s.GenerateTagsWithOptions("美味しいラーメンを食べた", opts)

The remaining TagOptions fields and their library defaults are:

FieldTypeDefaultDescription
POSFilteruint80POS bitmask to include (0 = all)
ExcludeBasicboolfalseExclude words whose lemma is hiragana-only
UseLemmabooltrueUse the lemma (dictionary form) instead of the surface form
MinLengthint2Minimum tag length in characters
MaxTagsint0Maximum number of tags (0 = unlimited)
ExcludeParticlesbooltrueExclude particles
ExcludeAuxiliariesbooltrueExclude auxiliaries
ExcludeFormalNounsbooltrueExclude formal nouns such as こと and もの
ExcludeLowInfobooltrueExclude low-information words
RemoveDuplicatesbooltrueRemove duplicate tags

User dictionaries

Add custom words at runtime from the current TSV format with LoadUserDictionary(). Legacy three-column CSV is also accepted:

go
source := []byte("東京公園\tNOUN\n点検する\tVERB\tSURU\n")
if err := s.LoadUserDictionary(source); err != nil {
	log.Fatal(err)
}

for _, m := range s.Analyze("東京公園を点検する") {
	fmt.Println(m.Surface, m.POS, m.IsUserDict)
}

Pre-compiled binary .dic dictionaries can be loaded from memory with LoadBinaryDictionary():

go
data, err := os.ReadFile("custom.dic")
if err != nil {
	log.Fatal(err)
}
if err := s.LoadBinaryDictionary(data); err != nil {
	log.Fatal(err)
}

Both methods return a non-nil error when loading fails. DictionaryWarnings() returns warnings from automatic loading at instance creation and later source/binary dictionary loads, or nil when there are none. A partially accepted source dictionary can therefore return no error while still adding a warning:

go
for _, w := range s.DictionaryWarnings() {
	fmt.Println("warning:", w)
}

API summary

Package-level functions:

FunctionDescription
New() (*Suzume, error)Create an analyzer with default options
NewWithOptions(opts Options) (*Suzume, error)Create with normalization toggles only
NewWithExtendedOptions(opts ExtendedOptions) (*Suzume, error)Create with mode, lemmatization, and compound merging
DefaultExtendedOptions() ExtendedOptionsLibrary-default ExtendedOptions starting point
DefaultTagOptions() TagOptionsLibrary-default TagOptions starting point
Version() stringNative Suzume library version string
LastError() stringCurrent OS thread's native diagnostic; unreliable as a later failure check from Go
LastErrorCode() ErrorCodeCurrent OS thread's native diagnostic code, with the same thread-affinity caveat

Methods on *Suzume:

MethodDescription
Analyze(text string) []MorphemeAnalyze text (see Morpheme fields)
GenerateTags(text string) []TagExtract keyword Tags with the default filters
GenerateTagsWithOptions(text string, opts TagOptions) []TagExtract keyword Tags with custom filters and limits
LoadUserDictionary(data []byte) errorLoad a TSV or legacy CSV user dictionary
LoadBinaryDictionary(data []byte) errorLoad a binary .dic dictionary
DictionaryWarnings() []stringWarnings from creation-time and runtime dictionary loading
Close()Release the native handle (safe to call multiple times)

See also