William Kurkian | ea86948 | 2019-04-09 15:16:11 -0400 | [diff] [blame] | 1 | // Copyright 2014 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. |
| 4 | |
| 5 | package language |
| 6 | |
| 7 | import ( |
| 8 | "fmt" |
| 9 | "sort" |
Abhilash S.L | 3b49463 | 2019-07-16 15:51:09 +0530 | [diff] [blame] | 10 | |
| 11 | "golang.org/x/text/internal/language" |
William Kurkian | ea86948 | 2019-04-09 15:16:11 -0400 | [diff] [blame] | 12 | ) |
| 13 | |
| 14 | // The Coverage interface is used to define the level of coverage of an |
| 15 | // internationalization service. Note that not all types are supported by all |
| 16 | // services. As lists may be generated on the fly, it is recommended that users |
| 17 | // of a Coverage cache the results. |
| 18 | type Coverage interface { |
| 19 | // Tags returns the list of supported tags. |
| 20 | Tags() []Tag |
| 21 | |
| 22 | // BaseLanguages returns the list of supported base languages. |
| 23 | BaseLanguages() []Base |
| 24 | |
| 25 | // Scripts returns the list of supported scripts. |
| 26 | Scripts() []Script |
| 27 | |
| 28 | // Regions returns the list of supported regions. |
| 29 | Regions() []Region |
| 30 | } |
| 31 | |
| 32 | var ( |
| 33 | // Supported defines a Coverage that lists all supported subtags. Tags |
| 34 | // always returns nil. |
| 35 | Supported Coverage = allSubtags{} |
| 36 | ) |
| 37 | |
| 38 | // TODO: |
| 39 | // - Support Variants, numbering systems. |
| 40 | // - CLDR coverage levels. |
| 41 | // - Set of common tags defined in this package. |
| 42 | |
| 43 | type allSubtags struct{} |
| 44 | |
| 45 | // Regions returns the list of supported regions. As all regions are in a |
| 46 | // consecutive range, it simply returns a slice of numbers in increasing order. |
| 47 | // The "undefined" region is not returned. |
| 48 | func (s allSubtags) Regions() []Region { |
Abhilash S.L | 3b49463 | 2019-07-16 15:51:09 +0530 | [diff] [blame] | 49 | reg := make([]Region, language.NumRegions) |
William Kurkian | ea86948 | 2019-04-09 15:16:11 -0400 | [diff] [blame] | 50 | for i := range reg { |
Abhilash S.L | 3b49463 | 2019-07-16 15:51:09 +0530 | [diff] [blame] | 51 | reg[i] = Region{language.Region(i + 1)} |
William Kurkian | ea86948 | 2019-04-09 15:16:11 -0400 | [diff] [blame] | 52 | } |
| 53 | return reg |
| 54 | } |
| 55 | |
| 56 | // Scripts returns the list of supported scripts. As all scripts are in a |
| 57 | // consecutive range, it simply returns a slice of numbers in increasing order. |
| 58 | // The "undefined" script is not returned. |
| 59 | func (s allSubtags) Scripts() []Script { |
Abhilash S.L | 3b49463 | 2019-07-16 15:51:09 +0530 | [diff] [blame] | 60 | scr := make([]Script, language.NumScripts) |
William Kurkian | ea86948 | 2019-04-09 15:16:11 -0400 | [diff] [blame] | 61 | for i := range scr { |
Abhilash S.L | 3b49463 | 2019-07-16 15:51:09 +0530 | [diff] [blame] | 62 | scr[i] = Script{language.Script(i + 1)} |
William Kurkian | ea86948 | 2019-04-09 15:16:11 -0400 | [diff] [blame] | 63 | } |
| 64 | return scr |
| 65 | } |
| 66 | |
| 67 | // BaseLanguages returns the list of all supported base languages. It generates |
| 68 | // the list by traversing the internal structures. |
| 69 | func (s allSubtags) BaseLanguages() []Base { |
Abhilash S.L | 3b49463 | 2019-07-16 15:51:09 +0530 | [diff] [blame] | 70 | bs := language.BaseLanguages() |
| 71 | base := make([]Base, len(bs)) |
| 72 | for i, b := range bs { |
| 73 | base[i] = Base{b} |
William Kurkian | ea86948 | 2019-04-09 15:16:11 -0400 | [diff] [blame] | 74 | } |
| 75 | return base |
| 76 | } |
| 77 | |
| 78 | // Tags always returns nil. |
| 79 | func (s allSubtags) Tags() []Tag { |
| 80 | return nil |
| 81 | } |
| 82 | |
Abhilash S.L | 3b49463 | 2019-07-16 15:51:09 +0530 | [diff] [blame] | 83 | // coverage is used by NewCoverage which is used as a convenient way for |
William Kurkian | ea86948 | 2019-04-09 15:16:11 -0400 | [diff] [blame] | 84 | // creating Coverage implementations for partially defined data. Very often a |
| 85 | // package will only need to define a subset of slices. coverage provides a |
| 86 | // convenient way to do this. Moreover, packages using NewCoverage, instead of |
| 87 | // their own implementation, will not break if later new slice types are added. |
| 88 | type coverage struct { |
| 89 | tags func() []Tag |
| 90 | bases func() []Base |
| 91 | scripts func() []Script |
| 92 | regions func() []Region |
| 93 | } |
| 94 | |
| 95 | func (s *coverage) Tags() []Tag { |
| 96 | if s.tags == nil { |
| 97 | return nil |
| 98 | } |
| 99 | return s.tags() |
| 100 | } |
| 101 | |
| 102 | // bases implements sort.Interface and is used to sort base languages. |
| 103 | type bases []Base |
| 104 | |
| 105 | func (b bases) Len() int { |
| 106 | return len(b) |
| 107 | } |
| 108 | |
| 109 | func (b bases) Swap(i, j int) { |
| 110 | b[i], b[j] = b[j], b[i] |
| 111 | } |
| 112 | |
| 113 | func (b bases) Less(i, j int) bool { |
| 114 | return b[i].langID < b[j].langID |
| 115 | } |
| 116 | |
| 117 | // BaseLanguages returns the result from calling s.bases if it is specified or |
| 118 | // otherwise derives the set of supported base languages from tags. |
| 119 | func (s *coverage) BaseLanguages() []Base { |
| 120 | if s.bases == nil { |
| 121 | tags := s.Tags() |
| 122 | if len(tags) == 0 { |
| 123 | return nil |
| 124 | } |
| 125 | a := make([]Base, len(tags)) |
| 126 | for i, t := range tags { |
Abhilash S.L | 3b49463 | 2019-07-16 15:51:09 +0530 | [diff] [blame] | 127 | a[i] = Base{language.Language(t.lang())} |
William Kurkian | ea86948 | 2019-04-09 15:16:11 -0400 | [diff] [blame] | 128 | } |
| 129 | sort.Sort(bases(a)) |
| 130 | k := 0 |
| 131 | for i := 1; i < len(a); i++ { |
| 132 | if a[k] != a[i] { |
| 133 | k++ |
| 134 | a[k] = a[i] |
| 135 | } |
| 136 | } |
| 137 | return a[:k+1] |
| 138 | } |
| 139 | return s.bases() |
| 140 | } |
| 141 | |
| 142 | func (s *coverage) Scripts() []Script { |
| 143 | if s.scripts == nil { |
| 144 | return nil |
| 145 | } |
| 146 | return s.scripts() |
| 147 | } |
| 148 | |
| 149 | func (s *coverage) Regions() []Region { |
| 150 | if s.regions == nil { |
| 151 | return nil |
| 152 | } |
| 153 | return s.regions() |
| 154 | } |
| 155 | |
| 156 | // NewCoverage returns a Coverage for the given lists. It is typically used by |
| 157 | // packages providing internationalization services to define their level of |
| 158 | // coverage. A list may be of type []T or func() []T, where T is either Tag, |
| 159 | // Base, Script or Region. The returned Coverage derives the value for Bases |
| 160 | // from Tags if no func or slice for []Base is specified. For other unspecified |
| 161 | // types the returned Coverage will return nil for the respective methods. |
| 162 | func NewCoverage(list ...interface{}) Coverage { |
| 163 | s := &coverage{} |
| 164 | for _, x := range list { |
| 165 | switch v := x.(type) { |
| 166 | case func() []Base: |
| 167 | s.bases = v |
| 168 | case func() []Script: |
| 169 | s.scripts = v |
| 170 | case func() []Region: |
| 171 | s.regions = v |
| 172 | case func() []Tag: |
| 173 | s.tags = v |
| 174 | case []Base: |
| 175 | s.bases = func() []Base { return v } |
| 176 | case []Script: |
| 177 | s.scripts = func() []Script { return v } |
| 178 | case []Region: |
| 179 | s.regions = func() []Region { return v } |
| 180 | case []Tag: |
| 181 | s.tags = func() []Tag { return v } |
| 182 | default: |
| 183 | panic(fmt.Sprintf("language: unsupported set type %T", v)) |
| 184 | } |
| 185 | } |
| 186 | return s |
| 187 | } |