Source File
parse.go
Belonging Package
golang.org/x/text/language
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package language
import (
)
// ValueError is returned by any of the parsing functions when the
// input is well-formed but the respective subtag is not recognized
// as a valid value.
type ValueError interface {
error
// Subtag returns the subtag for which the error occurred.
Subtag() string
}
// Parse parses the given BCP 47 string and returns a valid Tag. If parsing
// failed it returns an error and any part of the tag that could be parsed.
// If parsing succeeded but an unknown value was found, it returns
// ValueError. The Tag returned in this case is just stripped of the unknown
// value. All other values are preserved. It accepts tags in the BCP 47 format
// and extensions to this standard defined in
// https://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers.
// The resulting tag is canonicalized using the default canonicalization type.
func ( string) ( Tag, error) {
return Default.Parse()
}
// Parse parses the given BCP 47 string and returns a valid Tag. If parsing
// failed it returns an error and any part of the tag that could be parsed.
// If parsing succeeded but an unknown value was found, it returns
// ValueError. The Tag returned in this case is just stripped of the unknown
// value. All other values are preserved. It accepts tags in the BCP 47 format
// and extensions to this standard defined in
// https://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers.
// The resulting tag is canonicalized using the canonicalization type c.
func ( CanonType) ( string) ( Tag, error) {
defer func() {
if recover() != nil {
= Tag{}
= language.ErrSyntax
}
}()
, := language.Parse()
if != nil {
return makeTag(),
}
, := canonicalize(, )
if {
.RemakeString()
}
return makeTag(),
}
// Compose creates a Tag from individual parts, which may be of type Tag, Base,
// Script, Region, Variant, []Variant, Extension, []Extension or error. If a
// Base, Script or Region or slice of type Variant or Extension is passed more
// than once, the latter will overwrite the former. Variants and Extensions are
// accumulated, but if two extensions of the same type are passed, the latter
// will replace the former. For -u extensions, though, the key-type pairs are
// added, where later values overwrite older ones. A Tag overwrites all former
// values and typically only makes sense as the first argument. The resulting
// tag is returned after canonicalizing using the Default CanonType. If one or
// more errors are encountered, one of the errors is returned.
func ( ...interface{}) ( Tag, error) {
return Default.Compose(...)
}
// Compose creates a Tag from individual parts, which may be of type Tag, Base,
// Script, Region, Variant, []Variant, Extension, []Extension or error. If a
// Base, Script or Region or slice of type Variant or Extension is passed more
// than once, the latter will overwrite the former. Variants and Extensions are
// accumulated, but if two extensions of the same type are passed, the latter
// will replace the former. For -u extensions, though, the key-type pairs are
// added, where later values overwrite older ones. A Tag overwrites all former
// values and typically only makes sense as the first argument. The resulting
// tag is returned after canonicalizing using CanonType c. If one or more errors
// are encountered, one of the errors is returned.
func ( CanonType) ( ...interface{}) ( Tag, error) {
defer func() {
if recover() != nil {
= Tag{}
= language.ErrSyntax
}
}()
var language.Builder
if = update(&, ...); != nil {
return und,
}
.Tag, _ = canonicalize(, .Tag)
return makeTag(.Make()),
}
var errInvalidArgument = errors.New("invalid Extension or Variant")
func update( *language.Builder, ...interface{}) ( error) {
for , := range {
switch v := .(type) {
case Tag:
.SetTag(.tag())
case Base:
.Tag.LangID = .langID
case Script:
.Tag.ScriptID = .scriptID
case Region:
.Tag.RegionID = .regionID
case Variant:
if .variant == "" {
= errInvalidArgument
break
}
.AddVariant(.variant)
case Extension:
if .s == "" {
= errInvalidArgument
break
}
.SetExt(.s)
case []Variant:
.ClearVariants()
for , := range {
.AddVariant(.variant)
}
case []Extension:
.ClearExtensions()
for , := range {
.SetExt(.s)
}
// TODO: support parsing of raw strings based on morphology or just extensions?
case error:
if != nil {
=
}
}
}
return
}
var errInvalidWeight = errors.New("ParseAcceptLanguage: invalid weight")
var errTagListTooLarge = errors.New("tag list exceeds max length")
// ParseAcceptLanguage parses the contents of an Accept-Language header as
// defined in http://www.ietf.org/rfc/rfc2616.txt and returns a list of Tags and
// a list of corresponding quality weights. It is more permissive than RFC 2616
// and may return non-nil slices even if the input is not valid.
// The Tags will be sorted by highest weight first and then by first occurrence.
// Tags with a weight of zero will be dropped. An error will be returned if the
// input could not be parsed.
func ( string) ( []Tag, []float32, error) {
defer func() {
if recover() != nil {
= nil
= nil
= language.ErrSyntax
}
}()
if strings.Count(, "-") > 1000 {
return nil, nil, errTagListTooLarge
}
var string
for != "" {
if , = split(, ','); == "" {
continue
}
, := split(, ';')
// Scan the language.
, := Parse()
if != nil {
, := acceptFallback[]
if ! {
return nil, nil,
}
= makeTag(language.Tag{LangID: })
}
// Scan the optional weight.
:= 1.0
if != "" {
= consume(, 'q')
= consume(, '=')
// consume returns the empty string when a token could not be
// consumed, resulting in an error for ParseFloat.
if , = strconv.ParseFloat(, 32); != nil {
return nil, nil, errInvalidWeight
}
// Drop tags with a quality weight of 0.
if <= 0 {
continue
}
}
= append(, )
= append(, float32())
}
sort.Stable(&tagSort{, })
return , , nil
}
// consume removes a leading token c from s and returns the result or the empty
// string if there is no such token.
func consume( string, byte) string {
if == "" || [0] != {
return ""
}
return strings.TrimSpace([1:])
}
func split( string, byte) (, string) {
if := strings.IndexByte(, ); >= 0 {
return strings.TrimSpace([:]), strings.TrimSpace([+1:])
}
return strings.TrimSpace(), ""
}
// Add hack mapping to deal with a small number of cases that occur
// in Accept-Language (with reasonable frequency).
var acceptFallback = map[string]language.Language{
"english": _en,
"deutsch": _de,
"italian": _it,
"french": _fr,
"*": _mul, // defined in the spec to match all languages.
}
type tagSort struct {
tag []Tag
q []float32
}
func ( *tagSort) () int {
return len(.q)
}
func ( *tagSort) (, int) bool {
return .q[] > .q[]
}
func ( *tagSort) (, int) {
.tag[], .tag[] = .tag[], .tag[]
.q[], .q[] = .q[], .q[]
}
![]() |
The pages are generated with Golds v0.6.7. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |