1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 //go:generate go run gen.go gen_index.go -output tables.go
6 //go:generate go run gen_parents.go
10 // TODO: Remove above NOTE after:
11 // - verifying that tables are dropped correctly (most notably matcher tables).
16 "golang.org/x/text/internal/language"
19 // Tag represents a BCP 47 language tag. It is used to specify an instance of a
20 // specific language or locale. All language tag values are guaranteed to be
23 // NOTE: exported tags will become part of the public API.
26 full fullTag // always a language.Tag for now.
31 type fullTag interface {
36 // Make a compact Tag from a fully specified internal language Tag.
37 func Make(t language.Tag) (tag Tag) {
38 if region := t.TypeForKey("rg"); len(region) == 6 && region[2:] == "zzzz" {
39 if r, err := language.ParseRegion(region[:2]); err == nil {
41 t, _ = t.SetTypeForKey("rg", "")
42 // TODO: should we not consider "va" for the language tag?
43 var exact1, exact2 bool
44 tag.language, exact1 = FromTag(t)
46 tag.locale, exact2 = FromTag(t)
47 if !exact1 || !exact2 {
53 lang, ok := FromTag(t)
62 // Tag returns an internal language Tag version of this tag.
63 func (t Tag) Tag() language.Tag {
65 return t.full.(language.Tag)
67 tag := t.language.Tag()
68 if t.language != t.locale {
70 tag, _ = tag.SetTypeForKey("rg", strings.ToLower(loc.RegionID.String())+"zzzz")
75 // IsCompact reports whether this tag is fully defined in terms of ID.
76 func (t *Tag) IsCompact() bool {
80 // MayHaveVariants reports whether a tag may have variants. If it returns false
81 // it is guaranteed the tag does not have variants.
82 func (t Tag) MayHaveVariants() bool {
83 return t.full != nil || int(t.language) >= len(coreTags)
86 // MayHaveExtensions reports whether a tag may have extensions. If it returns
87 // false it is guaranteed the tag does not have them.
88 func (t Tag) MayHaveExtensions() bool {
89 return t.full != nil ||
90 int(t.language) >= len(coreTags) ||
91 t.language != t.locale
94 // IsRoot returns true if t is equal to language "und".
95 func (t Tag) IsRoot() bool {
97 return t.full.IsRoot()
99 return t.language == _und
102 // Parent returns the CLDR parent of t. In CLDR, missing fields in data for a
103 // specific language are substituted with fields from the parent language.
104 // The parent for a language may change for newer versions of CLDR.
105 func (t Tag) Parent() Tag {
107 return Make(t.full.Parent())
109 if t.language != t.locale {
110 // Simulate stripping -u-rg-xxxxxx
111 return Tag{language: t.language, locale: t.language}
113 // TODO: use parent lookup table once cycle from internal package is
114 // removed. Probably by internalizing the table and declaring this fast
116 // lang := compactID(internal.Parent(uint16(t.language)))
117 lang, _ := FromTag(t.language.Tag().Parent())
118 return Tag{language: lang, locale: lang}
121 // returns token t and the rest of the string.
122 func nextToken(s string) (t, tail string) {
123 p := strings.Index(s[1:], "-")
131 // LanguageID returns an index, where 0 <= index < NumCompactTags, for tags
132 // for which data exists in the text repository.The index will change over time
133 // and should not be stored in persistent storage. If t does not match a compact
134 // index, exact will be false and the compact index will be returned for the
135 // first match after repeatedly taking the Parent of t.
136 func LanguageID(t Tag) (id ID, exact bool) {
137 return t.language, t.full == nil
140 // RegionalID returns the ID for the regional variant of this tag. This index is
141 // used to indicate region-specific overrides, such as default currency, default
142 // calendar and week data, default time cycle, and default measurement system
143 // and unit preferences.
145 // For instance, the tag en-GB-u-rg-uszzzz specifies British English with US
146 // settings for currency, number formatting, etc. The CompactIndex for this tag
147 // will be that for en-GB, while the RegionalID will be the one corresponding to
149 func RegionalID(t Tag) (id ID, exact bool) {
150 return t.locale, t.full == nil
153 // LanguageTag returns t stripped of regional variant indicators.
155 // At the moment this means it is stripped of a regional and variant subtag "rg"
156 // and "va" in the "u" extension.
157 func (t Tag) LanguageTag() Tag {
159 return Tag{language: t.language, locale: t.language}
162 tt.SetTypeForKey("rg", "")
163 tt.SetTypeForKey("va", "")
167 // RegionalTag returns the regional variant of the tag.
169 // At the moment this means that the region is set from the regional subtag
170 // "rg" in the "u" extension.
171 func (t Tag) RegionalTag() Tag {
172 rt := Tag{language: t.locale, locale: t.locale}
176 b := language.Builder{}
178 // tag, _ = tag.SetTypeForKey("rg", "")
179 b.SetTag(t.locale.Tag())
180 if v := tag.Variants(); v != "" {
181 for _, v := range strings.Split(v, "-") {
185 for _, e := range tag.Extensions() {
191 // FromTag reports closest matching ID for an internal language Tag.
192 func FromTag(t language.Tag) (id ID, exact bool) {
193 // TODO: perhaps give more frequent tags a lower index.
194 // TODO: we could make the indexes stable. This will excluded some
195 // possibilities for optimization, so don't do this quite yet.
200 if t.IsPrivateUse() {
201 // We have no entries for user-defined tags.
206 if t.HasExtensions() {
207 build := language.Builder{}
208 build.SetTag(language.Tag{LangID: b, ScriptID: s, RegionID: r})
209 build.AddVariant(t.Variants())
214 } else if _, ok := t.Extension('u'); ok {
215 // TODO: va may mean something else. Consider not considering it.
216 // Strip all but the 'va' entry.
218 variant := t.TypeForKey("va")
219 t = language.Tag{LangID: b, ScriptID: s, RegionID: r}
221 t, _ = t.SetTypeForKey("va", variant)
229 // We have some variants.
230 for i, s := range specialTags {
232 return ID(i + len(coreTags)), exact
238 if x, ok := getCoreIndex(t); ok {
242 if r != 0 && s == 0 {
243 // Deal with cases where an extra script is inserted for the region.
245 if x, ok := getCoreIndex(t); ok {
249 for t = t.Parent(); t != root; t = t.Parent() {
250 // No variants specified: just compare core components.
251 // The key has the form lllssrrr, where l, s, and r are nibbles for
252 // respectively the langID, scriptID, and regionID.
253 if x, ok := getCoreIndex(t); ok {
260 var root = language.Tag{}