// 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 pluralimport ()// Rules defines the plural rules for all languages for a certain plural type.//// This package is UNDER CONSTRUCTION and its API may change.typeRulesstruct { 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 == 12300func getIntApprox( []byte, , , , int) ( int) {// Leading 0 digits just result in 0. := if < 0 { = 0 }// Range only over the part for which we have digits. := if >= len() { = len() }// Check digits more significant that nMod.if := - ; > 0 {if > { = }for ; < ; ++ {if [] != 0 {return } } }for ; < ; ++ { = 10* + int([]) }// Multiply for trailing zeros.for ; < ; ++ { *= 10 }return}// 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 3func ( *Rules) ( language.Tag, []byte, , int) Form { := tagToID()// Differentiate up to including mod 1000000 for the integer part. := getIntApprox(, 0, , 6, 1000000)// Differentiate up to including mod 100 for the fractional part. := getIntApprox(, , +, 2, 100)returnmatchPlural(, , , , )}func ( *Rules) ( language.Tag, *number.Digits) (Form, int) { := getIntApprox(.Digits, 0, int(.Exp), 6, 1000000)return .MatchDigits(, .Digits, int(.Exp), .NumFracDigits()), }func validForms( *Rules, language.Tag) ( []Form) { := .langToIndex[tagToID()] := .rules[.index[]:.index[+1]] = append(, Other) := Otherfor , := range {if := Form(.cat & formMask); != andNext && != { = append(, ) = } }return}func ( *Rules) ( language.Tag, , , int) Form {returnmatchPlural(, tagToID(), , , )}// 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 ( *Rules) ( language.Tag, , , , , int) Form {returnmatchPlural(, tagToID(), , , )}func matchPlural( *Rules, compact.ID, , , int) Form { := .inclusionMasks[%maxMod]// Compute the fMask inline in the rules below, as it is relatively rare. // fMask := p.inclusionMasks[f%maxMod] := .inclusionMasks[%maxMod]// Do the matching := .langToIndex[] := .rules[.index[]:.index[+1]]for := 0; < len(); ++ { := [] := uint64(1 << .setID)varboolswitch := opID(.cat >> opShift); {caseopI: // i = x = >= numN || & == 0caseopI | opNotEqual: // i != x = < numN && & != 0caseopI | opMod: // i % m = x = & == 0caseopI | opMod | opNotEqual: // i % m != x = & != 0caseopN: // n = x = != 0 || >= numN || & == 0caseopN | opNotEqual: // n != x = == 0 && < numN && & != 0caseopN | opMod: // n % m = x = != 0 || & == 0caseopN | opMod | opNotEqual: // n % m != x = == 0 && & != 0caseopF: // f = x = >= numN || .inclusionMasks[%maxMod]& == 0caseopF | opNotEqual: // f != x = < numN && .inclusionMasks[%maxMod]& != 0caseopF | opMod: // f % m = x = .inclusionMasks[%maxMod]& == 0caseopF | opMod | opNotEqual: // f % m != x = .inclusionMasks[%maxMod]& != 0caseopV: // v = x = < numN && & == 0caseopV | opNotEqual: // v != x = < numN && & != 0caseopW: // w == 0 = != 0caseopW | opNotEqual: // w != 0 = == 0// Hard-wired rules that cannot be handled by our algorithm.caseopBretonM: = != 0 || == 0 || %1000000 != 0caseopAzerbaijan00s:// 100,200,300,400,500,600,700,800,900 = == 0 || >= 1000 || %100 != 0caseopItalian800: = ( != 0 || >= numN || & == 0) && != 800 }if {// advance over AND entries.for ; < len() && [].cat&formMask == andNext; ++ { }continue }// return if we have a final entry.if := .cat & formMask; != andNext {returnForm() } }returnOther}func tagToID( language.Tag) compact.ID { , := compact.RegionalID(compact.Tag())return}
The pages are generated with Goldsv0.8.2. (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 @zigo_101 (reachable from the left QR code) to get the latest news of Golds.