ISO 639-1 two-letter language codes. Code normalizes the language identifiers found in HTTP headers, locales and other standards ("EN", "deu", "ger", "en-US", "zh_Hant") to the canonical lower-case ISO 639-1 code, returns English language names, and reads and writes SQL, JSON and XML. Two lookup functions cover the full ISO 639-3 tables. The date package uses Code as a parsing hint.
import "github.com/domonda/go-types/language"
type Code string //#enum
const Null Code = "" //#excludeValue writes Null as SQL NULL and Scan reads SQL NULL as Null. The constant is marked //#exclude so that go-enum leaves it out of Enums(). Null is not a valid code: Null.Valid() is false and Null.Validate() returns an error. There is no nullable code type in this package.
The package declares 184 Code constants, one per ISO 639-1 code, named after the upper-case code with a lower-case value: EN Code = "en", DE Code = "de", ZH Code = "zh", ... Note that language.EL is Greek, unrelated to country.EL.
List them with the go-enum generated methods. Both return the codes in declaration order, which is close to alphabetical by English name:
codes := language.Code("").Enums() // []language.Code{language.AA, language.AB, language.AF, ...}
names := language.Code("").EnumStrings() // []string{"aa", "ab", "af", ...}| Identifier | Description |
|---|---|
ParseCode(str) |
Canonical code from a code, tag or English language name, see below. |
Normalized() |
The canonical ISO 639-1 code for the input shapes listed below. On failure returns the input unchanged and an error that quotes it. |
Valid() |
True if Normalized() succeeds, so Code("EN").Valid() is true. |
Validate() |
The error of Normalized(), which quotes the input, or nil. |
ValidAndNormalized() |
True only for a canonical lower-case code. |
LanguageName() |
English name from the ISO 639-1 table. Normalizes first; "" if invalid. |
String() |
The code as stored, generated by go-enum. |
Enums(), EnumStrings() |
All 184 codes, generated by go-enum. |
ISO6393Name(code) |
English reference name of an ISO 639-3 code ("deu" gives "German"), "" if unknown. |
ISO6393Macrolanguage(code) |
ISO 639-3 macrolanguage of an individual language ("twi" gives "aka"), "" if none. |
Implements: fmt.Stringer, database/sql.Scanner, database/sql/driver.Valuer, encoding/json.Marshaler, encoding/json.Unmarshaler, encoding.TextMarshaler, encoding/xml.Unmarshaler, encoding/xml.UnmarshalerAttr, encoding/xml.MarshalerAttr, types.NormalizableValidator[Code] and JSONSchema() ({"type":"string","pattern":"^[a-z]{2}$"}). ScanString makes it a strfmt.Scannable, which reads a code like Scan. Code has no UnmarshalText, so encoding/json reads the keys of a map[Code]V as given.
Valid and Validate are hand-written and carry a // #custom marker, so go-enum keeps them. They normalize first like those of country.Code and money.Currency. Reading and Value only trim. Call Normalized() for the ISO 639-1 code. String is the one go-enum generates: it returns the stored code.
Normalized(), and so Valid() and LanguageName(), accept the spellings below. Reading and writing only trim, so "eng", "ger" and "en-US" are stored as given:
- ISO 639-1 codes in any case with surrounding whitespace:
"en","EN"," en ". - ISO 639-2/T and ISO 639-3 codes that have an ISO 639-1 equivalent:
"eng","deu","fra". - ISO 639-2/B bibliographic codes:
"ger"gives"de","fre"gives"fr","chi"gives"zh". - BCP-47 tags and POSIX locales, using only the primary subtag:
"en-US","zh-Hant-CN","sr-Latn","en_US".
ISO 639-3 codes without an ISO 639-1 equivalent (most of the roughly 7900 codes, like "abq") are rejected.
ParseCode(str) tries the cheapest interpretation first:
- A canonical code (
"en") is returned as is. - Anything
Normalizedaccepts. - An English language name, matched case-insensitively:
"German","english","Valencian". Names come from the ISO 639-1 table, including each synonym of entries like"Catalan; Valencian", and from the ISO 639-3 names of languages that have an ISO 639-1 code. The ISO 639-1 names win on conflicts.
On failure it returns the input unchanged and the error of Normalized.
ISO6393Name and ISO6393Macrolanguage read tables generated from the SIL ISO 639-3 data. Both are case-sensitive and expect the canonical lower-case code: ISO6393Name("DEU") is "". A code with several names returns its primary name. A macrolanguage is not a member of itself (ISO6393Macrolanguage("aka") is ""), and retired mappings are not returned.
Code follows the rules of Reading and writing values: reading and writing only trim, so "EN" stays "EN", and fmt prints the stored code.
| Value | SQL Value() |
JSON (json.Marshal) |
MarshalText, XML |
fmt |
|---|---|---|---|---|
Null, Code(" ") |
NULL |
"" |
"" |
as stored |
Code("en") |
"en" |
"en" |
"en" |
en |
Code("EN") |
"EN" |
"EN" |
"EN" |
EN |
Code("xx") (invalid) |
"xx" |
"xx" |
"xx" |
xx |
| Input | Result |
|---|---|
Scan(nil) |
Null |
Scan of a string or []byte, JSON string, XML element or attribute |
trimmed, Null if blank, case kept, without error |
Scan of other types |
error |
JSON null |
field left unchanged |
| other JSON values | error |
Validaccepts other spellings.Code("EN").Valid()andCode("EN").Validate()accept"EN","eng"and"en-US", liketypes.Validate,types.DeepValidateandstrfmtscanning. CheckValidAndNormalizedto require the canonical code.- A code keeps its spelling.
Scan, JSON and XML only trim, so"EN"stays"EN"and"en-US"stays"en-US". CallNormalized()for the ISO 639-1 code. - Optional languages.
Nullis SQL NULL but failsValidate, so an empty language field failstypes.DeepValidate. Use a*language.Codefor optional fields or check forNullbefore validating. - JSON is a string.
Nullencodes as"", notnull. The JSON schema pattern^[a-z]{2}$describes a canonical code.MarshalJSONwrites the trimmed code, so it can write"EN". - English names must match a table entry.
ParseCode("Greek")fails, because the names are"Greek, Modern (1453-)"(ISO 639-1) and"Modern Greek (1453-)"(ISO 639-3). - Names may list synonyms.
language.CA.LanguageName()is"Catalan; Valencian",language.NL.LanguageName()is"Dutch; Flemish". - Tags lose detail.
"zh-Hant"and"zh-Hans"both normalize to"zh". - Date parsing hints.
date.Normalizeonly distinguishes English from everything else, see thedatepackage.
package main
import (
"fmt"
"github.com/domonda/go-types/language"
)
func main() {
for _, s := range []string{"en", "EN", "deu", "ger", "en-US", "zh_Hant", "German", "Klingon"} {
c, err := language.ParseCode(s)
fmt.Println(c, err)
}
// Valid, Validate and Normalized accept other spellings
c := language.Code("EN")
fmt.Println(c.Valid(), c.Validate(), c.LanguageName())
norm, err := c.Normalized()
fmt.Println(norm, err, norm.Validate())
// Output:
// en <nil>
// en <nil>
// de <nil>
// de <nil>
// en <nil>
// zh <nil>
// de <nil>
// Klingon invalid language.Code: "Klingon"
// true <nil> English
// en <nil> <nil>
}package main
import (
"encoding/json"
"fmt"
"github.com/domonda/go-types/language"
)
func main() {
// Value and MarshalJSON write the trimmed code
v, _ := language.Code("EN").Value()
fmt.Println(v)
j, _ := json.Marshal(language.Code("en-US"))
fmt.Println(string(j))
fmt.Println(language.ISO6393Name("deu"), language.ISO6393Macrolanguage("twi"))
fmt.Println(language.CA.LanguageName())
fmt.Println(len(language.Code("").Enums()))
// Output:
// EN
// "en-US"
// German aka
// Catalan; Valencian
// 184
}| File | Content | Maintained by |
|---|---|---|
code.go |
Code, its methods and the ISO 639-1 constants; Enums() and EnumStrings() at the end |
hand; go-enum for Enums and EnumStrings |
constants.go |
codeNames: ISO 639-1 code to English name |
hand |
iso6393.go |
iso6393To1: ISO 639-2/T, 639-2/B and 639-3 codes to ISO 639-1, and the two lookup functions |
hand |
iso6393names.go |
iso6393Names: ISO 639-3 code to English name |
gen-lang-maps |
iso6393macro.go |
iso6393Macro: individual language to macrolanguage |
gen-lang-maps |
Refresh the two generated tables with go generate ./language/... (needs network access), see gen-lang-maps.
- Add the constant to the
constblock inlanguage/code.go:XX Code = "xx" // Name. - Add
"xx": "Name"tocodeNamesinlanguage/constants.go.TestCode_EnumsMatchCodeNamesfails if the two lists differ. - Add the ISO 639-2/T, 639-2/B and 639-3 codes of the language to
iso6393To1inlanguage/iso6393.go, so thatNormalizedmaps them. - Run
./run-go-enum.shfrom the repository root to regenerateEnums()andEnumStrings(), thengo test ./.... - CI runs
go-enum -validateand fails if the generated methods are stale.
See Add an enum code for the full steps.
country: ISO 3166-1 country codes, built the same way.date: usesCodeas a hint for ambiguous dates.gen-lang-maps: the generator for the ISO 639-3 tables.- Validation and normalization: why
Validaccepts other spellings. - Null conventions
- Lenient parsing: language hints for dates.
- Add an enum code