Path: blob/main/vendor/golang.org/x/text/internal/language/match.go
2893 views
// Copyright 2013 The Go Authors. All rights reserved.1// Use of this source code is governed by a BSD-style2// license that can be found in the LICENSE file.34package language56import "errors"78type scriptRegionFlags uint8910const (11isList = 1 << iota12scriptInFrom13regionInFrom14)1516func (t *Tag) setUndefinedLang(id Language) {17if t.LangID == 0 {18t.LangID = id19}20}2122func (t *Tag) setUndefinedScript(id Script) {23if t.ScriptID == 0 {24t.ScriptID = id25}26}2728func (t *Tag) setUndefinedRegion(id Region) {29if t.RegionID == 0 || t.RegionID.Contains(id) {30t.RegionID = id31}32}3334// ErrMissingLikelyTagsData indicates no information was available35// to compute likely values of missing tags.36var ErrMissingLikelyTagsData = errors.New("missing likely tags data")3738// addLikelySubtags sets subtags to their most likely value, given the locale.39// In most cases this means setting fields for unknown values, but in some40// cases it may alter a value. It returns an ErrMissingLikelyTagsData error41// if the given locale cannot be expanded.42func (t Tag) addLikelySubtags() (Tag, error) {43id, err := addTags(t)44if err != nil {45return t, err46} else if id.equalTags(t) {47return t, nil48}49id.RemakeString()50return id, nil51}5253// specializeRegion attempts to specialize a group region.54func specializeRegion(t *Tag) bool {55if i := regionInclusion[t.RegionID]; i < nRegionGroups {56x := likelyRegionGroup[i]57if Language(x.lang) == t.LangID && Script(x.script) == t.ScriptID {58t.RegionID = Region(x.region)59}60return true61}62return false63}6465// Maximize returns a new tag with missing tags filled in.66func (t Tag) Maximize() (Tag, error) {67return addTags(t)68}6970func addTags(t Tag) (Tag, error) {71// We leave private use identifiers alone.72if t.IsPrivateUse() {73return t, nil74}75if t.ScriptID != 0 && t.RegionID != 0 {76if t.LangID != 0 {77// already fully specified78specializeRegion(&t)79return t, nil80}81// Search matches for und-script-region. Note that for these cases82// region will never be a group so there is no need to check for this.83list := likelyRegion[t.RegionID : t.RegionID+1]84if x := list[0]; x.flags&isList != 0 {85list = likelyRegionList[x.lang : x.lang+uint16(x.script)]86}87for _, x := range list {88// Deviating from the spec. See match_test.go for details.89if Script(x.script) == t.ScriptID {90t.setUndefinedLang(Language(x.lang))91return t, nil92}93}94}95if t.LangID != 0 {96// Search matches for lang-script and lang-region, where lang != und.97if t.LangID < langNoIndexOffset {98x := likelyLang[t.LangID]99if x.flags&isList != 0 {100list := likelyLangList[x.region : x.region+uint16(x.script)]101if t.ScriptID != 0 {102for _, x := range list {103if Script(x.script) == t.ScriptID && x.flags&scriptInFrom != 0 {104t.setUndefinedRegion(Region(x.region))105return t, nil106}107}108} else if t.RegionID != 0 {109count := 0110goodScript := true111tt := t112for _, x := range list {113// We visit all entries for which the script was not114// defined, including the ones where the region was not115// defined. This allows for proper disambiguation within116// regions.117if x.flags&scriptInFrom == 0 && t.RegionID.Contains(Region(x.region)) {118tt.RegionID = Region(x.region)119tt.setUndefinedScript(Script(x.script))120goodScript = goodScript && tt.ScriptID == Script(x.script)121count++122}123}124if count == 1 {125return tt, nil126}127// Even if we fail to find a unique Region, we might have128// an unambiguous script.129if goodScript {130t.ScriptID = tt.ScriptID131}132}133}134}135} else {136// Search matches for und-script.137if t.ScriptID != 0 {138x := likelyScript[t.ScriptID]139if x.region != 0 {140t.setUndefinedRegion(Region(x.region))141t.setUndefinedLang(Language(x.lang))142return t, nil143}144}145// Search matches for und-region. If und-script-region exists, it would146// have been found earlier.147if t.RegionID != 0 {148if i := regionInclusion[t.RegionID]; i < nRegionGroups {149x := likelyRegionGroup[i]150if x.region != 0 {151t.setUndefinedLang(Language(x.lang))152t.setUndefinedScript(Script(x.script))153t.RegionID = Region(x.region)154}155} else {156x := likelyRegion[t.RegionID]157if x.flags&isList != 0 {158x = likelyRegionList[x.lang]159}160if x.script != 0 && x.flags != scriptInFrom {161t.setUndefinedLang(Language(x.lang))162t.setUndefinedScript(Script(x.script))163return t, nil164}165}166}167}168169// Search matches for lang.170if t.LangID < langNoIndexOffset {171x := likelyLang[t.LangID]172if x.flags&isList != 0 {173x = likelyLangList[x.region]174}175if x.region != 0 {176t.setUndefinedScript(Script(x.script))177t.setUndefinedRegion(Region(x.region))178}179specializeRegion(&t)180if t.LangID == 0 {181t.LangID = _en // default language182}183return t, nil184}185return t, ErrMissingLikelyTagsData186}187188func (t *Tag) setTagsFrom(id Tag) {189t.LangID = id.LangID190t.ScriptID = id.ScriptID191t.RegionID = id.RegionID192}193194// minimize removes the region or script subtags from t such that195// t.addLikelySubtags() == t.minimize().addLikelySubtags().196func (t Tag) minimize() (Tag, error) {197t, err := minimizeTags(t)198if err != nil {199return t, err200}201t.RemakeString()202return t, nil203}204205// minimizeTags mimics the behavior of the ICU 51 C implementation.206func minimizeTags(t Tag) (Tag, error) {207if t.equalTags(Und) {208return t, nil209}210max, err := addTags(t)211if err != nil {212return t, err213}214for _, id := range [...]Tag{215{LangID: t.LangID},216{LangID: t.LangID, RegionID: t.RegionID},217{LangID: t.LangID, ScriptID: t.ScriptID},218} {219if x, err := addTags(id); err == nil && max.equalTags(x) {220t.setTagsFrom(id)221break222}223}224return t, nil225}226227228