| // Copyright 2016 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. |
| |
| //go:generate go run gen.go gen_common.go |
| |
| // Package plural provides utilities for handling linguistic plurals in text. |
| // |
| // The definitions in this package are based on the plural rule handling defined |
| // in CLDR. See |
| // https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules for |
| // details. |
| package plural |
| |
| import ( |
| "golang.org/x/text/internal/language/compact" |
| "golang.org/x/text/internal/number" |
| "golang.org/x/text/language" |
| ) |
| |
| // Rules defines the plural rules for all languages for a certain plural type. |
| // |
| // |
| // This package is UNDER CONSTRUCTION and its API may change. |
| type Rules struct { |
| rules []pluralCheck |
| index []byte |
| langToIndex []byte |
| inclusionMasks []uint64 |
| } |
| |
| var ( |
| // Cardinal defines the plural rules for numbers indicating quantities. |
| Cardinal *Rules = cardinal |
| |
| // Ordinal defines the plural rules for numbers indicating position |
| // (first, second, etc.). |
| Ordinal *Rules = ordinal |
| |
| ordinal = &Rules{ |
| ordinalRules, |
| ordinalIndex, |
| ordinalLangToIndex, |
| ordinalInclusionMasks[:], |
| } |
| |
| cardinal = &Rules{ |
| cardinalRules, |
| cardinalIndex, |
| cardinalLangToIndex, |
| cardinalInclusionMasks[:], |
| } |
| ) |
| |
| // getIntApprox converts the digits in slice digits[start:end] to an integer |
| // according to the following rules: |
| // - Let i be asInt(digits[start:end]), where out-of-range digits are assumed |
| // to be zero. |
| // - Result n is big if i / 10^nMod > 1. |
| // - Otherwise the result is i % 10^nMod. |
| // |
| // For example, if digits is {1, 2, 3} and start:end is 0:5, then the result |
| // for various values of nMod is: |
| // - when nMod == 2, n == big |
| // - when nMod == 3, n == big |
| // - when nMod == 4, n == big |
| // - when nMod == 5, n == 12300 |
| // - when nMod == 6, n == 12300 |
| // - when nMod == 7, n == 12300 |
| func getIntApprox(digits []byte, start, end, nMod, big int) (n int) { |
| // Leading 0 digits just result in 0. |
| p := start |
| if p < 0 { |
| p = 0 |
| } |
| // Range only over the part for which we have digits. |
| mid := end |
| if mid >= len(digits) { |
| mid = len(digits) |
| } |
| // Check digits more significant that nMod. |
| if q := end - nMod; q > 0 { |
| if q > mid { |
| q = mid |
| } |
| for ; p < q; p++ { |
| if digits[p] != 0 { |
| return big |
| } |
| } |
| } |
| for ; p < mid; p++ { |
| n = 10*n + int(digits[p]) |
| } |
| // Multiply for trailing zeros. |
| for ; p < end; p++ { |
| n *= 10 |
| } |
| return n |
| } |
| |
| // MatchDigits computes the plural form for the given language and the given |
| // decimal floating point digits. The digits are stored in big-endian order and |
| // are of value byte(0) - byte(9). The floating point position is indicated by |
| // exp and the number of visible decimals is scale. All leading and trailing |
| // zeros may be omitted from digits. |
| // |
| // The following table contains examples of possible arguments to represent |
| // the given numbers. |
| // decimal digits exp scale |
| // 123 []byte{1, 2, 3} 3 0 |
| // 123.4 []byte{1, 2, 3, 4} 3 1 |
| // 123.40 []byte{1, 2, 3, 4} 3 2 |
| // 100000 []byte{1} 6 0 |
| // 100000.00 []byte{1} 6 3 |
| func (p *Rules) MatchDigits(t language.Tag, digits []byte, exp, scale int) Form { |
| index := tagToID(t) |
| |
| // Differentiate up to including mod 1000000 for the integer part. |
| n := getIntApprox(digits, 0, exp, 6, 1000000) |
| |
| // Differentiate up to including mod 100 for the fractional part. |
| f := getIntApprox(digits, exp, exp+scale, 2, 100) |
| |
| return matchPlural(p, index, n, f, scale) |
| } |
| |
| func (p *Rules) matchDisplayDigits(t language.Tag, d *number.Digits) (Form, int) { |
| n := getIntApprox(d.Digits, 0, int(d.Exp), 6, 1000000) |
| return p.MatchDigits(t, d.Digits, int(d.Exp), d.NumFracDigits()), n |
| } |
| |
| func validForms(p *Rules, t language.Tag) (forms []Form) { |
| offset := p.langToIndex[tagToID(t)] |
| rules := p.rules[p.index[offset]:p.index[offset+1]] |
| |
| forms = append(forms, Other) |
| last := Other |
| for _, r := range rules { |
| if cat := Form(r.cat & formMask); cat != andNext && last != cat { |
| forms = append(forms, cat) |
| last = cat |
| } |
| } |
| return forms |
| } |
| |
| func (p *Rules) matchComponents(t language.Tag, n, f, scale int) Form { |
| return matchPlural(p, tagToID(t), n, f, scale) |
| } |
| |
| // MatchPlural returns the plural form for the given language and plural |
| // operands (as defined in |
| // https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules): |
| // where |
| // n absolute value of the source number (integer and decimals) |
| // input |
| // i integer digits of n. |
| // v number of visible fraction digits in n, with trailing zeros. |
| // w number of visible fraction digits in n, without trailing zeros. |
| // f visible fractional digits in n, with trailing zeros (f = t * 10^(v-w)) |
| // t visible fractional digits in n, without trailing zeros. |
| // |
| // If any of the operand values is too large to fit in an int, it is okay to |
| // pass the value modulo 10,000,000. |
| func (p *Rules) MatchPlural(lang language.Tag, i, v, w, f, t int) Form { |
| return matchPlural(p, tagToID(lang), i, f, v) |
| } |
| |
| func matchPlural(p *Rules, index compact.ID, n, f, v int) Form { |
| nMask := p.inclusionMasks[n%maxMod] |
| // Compute the fMask inline in the rules below, as it is relatively rare. |
| // fMask := p.inclusionMasks[f%maxMod] |
| vMask := p.inclusionMasks[v%maxMod] |
| |
| // Do the matching |
| offset := p.langToIndex[index] |
| rules := p.rules[p.index[offset]:p.index[offset+1]] |
| for i := 0; i < len(rules); i++ { |
| rule := rules[i] |
| setBit := uint64(1 << rule.setID) |
| var skip bool |
| switch op := opID(rule.cat >> opShift); op { |
| case opI: // i = x |
| skip = n >= numN || nMask&setBit == 0 |
| |
| case opI | opNotEqual: // i != x |
| skip = n < numN && nMask&setBit != 0 |
| |
| case opI | opMod: // i % m = x |
| skip = nMask&setBit == 0 |
| |
| case opI | opMod | opNotEqual: // i % m != x |
| skip = nMask&setBit != 0 |
| |
| case opN: // n = x |
| skip = f != 0 || n >= numN || nMask&setBit == 0 |
| |
| case opN | opNotEqual: // n != x |
| skip = f == 0 && n < numN && nMask&setBit != 0 |
| |
| case opN | opMod: // n % m = x |
| skip = f != 0 || nMask&setBit == 0 |
| |
| case opN | opMod | opNotEqual: // n % m != x |
| skip = f == 0 && nMask&setBit != 0 |
| |
| case opF: // f = x |
| skip = f >= numN || p.inclusionMasks[f%maxMod]&setBit == 0 |
| |
| case opF | opNotEqual: // f != x |
| skip = f < numN && p.inclusionMasks[f%maxMod]&setBit != 0 |
| |
| case opF | opMod: // f % m = x |
| skip = p.inclusionMasks[f%maxMod]&setBit == 0 |
| |
| case opF | opMod | opNotEqual: // f % m != x |
| skip = p.inclusionMasks[f%maxMod]&setBit != 0 |
| |
| case opV: // v = x |
| skip = v < numN && vMask&setBit == 0 |
| |
| case opV | opNotEqual: // v != x |
| skip = v < numN && vMask&setBit != 0 |
| |
| case opW: // w == 0 |
| skip = f != 0 |
| |
| case opW | opNotEqual: // w != 0 |
| skip = f == 0 |
| |
| // Hard-wired rules that cannot be handled by our algorithm. |
| |
| case opBretonM: |
| skip = f != 0 || n == 0 || n%1000000 != 0 |
| |
| case opAzerbaijan00s: |
| // 100,200,300,400,500,600,700,800,900 |
| skip = n == 0 || n >= 1000 || n%100 != 0 |
| |
| case opItalian800: |
| skip = (f != 0 || n >= numN || nMask&setBit == 0) && n != 800 |
| } |
| if skip { |
| // advance over AND entries. |
| for ; i < len(rules) && rules[i].cat&formMask == andNext; i++ { |
| } |
| continue |
| } |
| // return if we have a final entry. |
| if cat := rule.cat & formMask; cat != andNext { |
| return Form(cat) |
| } |
| } |
| return Other |
| } |
| |
| func tagToID(t language.Tag) compact.ID { |
| id, _ := compact.RegionalID(compact.Tag(t)) |
| return id |
| } |