mirror of
				https://github.com/jesseduffield/lazygit.git
				synced 2025-10-30 23:57:43 +02:00 
			
		
		
		
	bump dependencies for i18n
This commit is contained in:
		
							
								
								
									
										27
									
								
								Gopkg.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										27
									
								
								Gopkg.lock
									
									
									
										generated
									
									
									
								
							| @@ -9,6 +9,14 @@ | ||||
|   revision = "3e01752db0189b9157070a0e1668a620f9a85da2" | ||||
|   version = "v1.0.6" | ||||
|  | ||||
| [[projects]] | ||||
|   branch = "master" | ||||
|   digest = "1:cd7ba2b29e93e2a8384e813dfc80ebb0f85d9214762e6ca89bb55a58092eab87" | ||||
|   name = "github.com/cloudfoundry/jibber_jabber" | ||||
|   packages = ["."] | ||||
|   pruneopts = "NUT" | ||||
|   revision = "bcc4c8345a21301bf47c032ff42dd1aae2fe3027" | ||||
|  | ||||
| [[projects]] | ||||
|   digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39" | ||||
|   name = "github.com/davecgh/go-spew" | ||||
| @@ -112,6 +120,18 @@ | ||||
|   pruneopts = "NUT" | ||||
|   revision = "58046073cbffe2f25d425fe1331102f55cf719de" | ||||
|  | ||||
| [[projects]] | ||||
|   digest = "1:2c34c77bf3ec848da26e48af58fc511ed52750961fa848399d122882b8890928" | ||||
|   name = "github.com/nicksnyder/go-i18n" | ||||
|   packages = [ | ||||
|     "v2/i18n", | ||||
|     "v2/internal", | ||||
|     "v2/internal/plural", | ||||
|   ] | ||||
|   pruneopts = "NUT" | ||||
|   revision = "a16b91a3ba80db3a2301c70d1d302d42251c9079" | ||||
|   version = "v2.0.0-beta.5" | ||||
|  | ||||
| [[projects]] | ||||
|   branch = "master" | ||||
|   digest = "1:34d9354c2c5d916c05864327553047df59fc10e86ff1f408e4136eba0a25a5ec" | ||||
| @@ -211,12 +231,14 @@ | ||||
|   revision = "98c5dad5d1a0e8a73845ecc8897d0bd56586511d" | ||||
|  | ||||
| [[projects]] | ||||
|   digest = "1:8029e9743749d4be5bc9f7d42ea1659471767860f0cdc34d37c3111bd308a295" | ||||
|   digest = "1:a95288ef1ef4dfad6cba7fe30843e1683f71bc28c912ca1ba3f6a539d44db739" | ||||
|   name = "golang.org/x/text" | ||||
|   packages = [ | ||||
|     "internal/gen", | ||||
|     "internal/tag", | ||||
|     "internal/triegen", | ||||
|     "internal/ucd", | ||||
|     "language", | ||||
|     "transform", | ||||
|     "unicode/cldr", | ||||
|     "unicode/norm", | ||||
| @@ -300,12 +322,15 @@ | ||||
|   analyzer-version = 1 | ||||
|   input-imports = [ | ||||
|     "github.com/Sirupsen/logrus", | ||||
|     "github.com/cloudfoundry/jibber_jabber", | ||||
|     "github.com/davecgh/go-spew/spew", | ||||
|     "github.com/fatih/color", | ||||
|     "github.com/golang-collections/collections/stack", | ||||
|     "github.com/jesseduffield/gocui", | ||||
|     "github.com/mgutz/str", | ||||
|     "github.com/nicksnyder/go-i18n/v2/i18n", | ||||
|     "github.com/tcnksm/go-gitconfig", | ||||
|     "golang.org/x/text/language", | ||||
|     "gopkg.in/src-d/go-git.v4", | ||||
|     "gopkg.in/src-d/go-git.v4/plumbing", | ||||
|   ] | ||||
|   | ||||
							
								
								
									
										201
									
								
								vendor/github.com/cloudfoundry/jibber_jabber/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								vendor/github.com/cloudfoundry/jibber_jabber/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,201 @@ | ||||
|                               Apache License | ||||
|                         Version 2.0, January 2004 | ||||
|                      http://www.apache.org/licenses/ | ||||
|  | ||||
| TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||
|  | ||||
| 1. Definitions. | ||||
|  | ||||
|    "License" shall mean the terms and conditions for use, reproduction, | ||||
|    and distribution as defined by Sections 1 through 9 of this document. | ||||
|  | ||||
|    "Licensor" shall mean the copyright owner or entity authorized by | ||||
|    the copyright owner that is granting the License. | ||||
|  | ||||
|    "Legal Entity" shall mean the union of the acting entity and all | ||||
|    other entities that control, are controlled by, or are under common | ||||
|    control with that entity. For the purposes of this definition, | ||||
|    "control" means (i) the power, direct or indirect, to cause the | ||||
|    direction or management of such entity, whether by contract or | ||||
|    otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||
|    outstanding shares, or (iii) beneficial ownership of such entity. | ||||
|  | ||||
|    "You" (or "Your") shall mean an individual or Legal Entity | ||||
|    exercising permissions granted by this License. | ||||
|  | ||||
|    "Source" form shall mean the preferred form for making modifications, | ||||
|    including but not limited to software source code, documentation | ||||
|    source, and configuration files. | ||||
|  | ||||
|    "Object" form shall mean any form resulting from mechanical | ||||
|    transformation or translation of a Source form, including but | ||||
|    not limited to compiled object code, generated documentation, | ||||
|    and conversions to other media types. | ||||
|  | ||||
|    "Work" shall mean the work of authorship, whether in Source or | ||||
|    Object form, made available under the License, as indicated by a | ||||
|    copyright notice that is included in or attached to the work | ||||
|    (an example is provided in the Appendix below). | ||||
|  | ||||
|    "Derivative Works" shall mean any work, whether in Source or Object | ||||
|    form, that is based on (or derived from) the Work and for which the | ||||
|    editorial revisions, annotations, elaborations, or other modifications | ||||
|    represent, as a whole, an original work of authorship. For the purposes | ||||
|    of this License, Derivative Works shall not include works that remain | ||||
|    separable from, or merely link (or bind by name) to the interfaces of, | ||||
|    the Work and Derivative Works thereof. | ||||
|  | ||||
|    "Contribution" shall mean any work of authorship, including | ||||
|    the original version of the Work and any modifications or additions | ||||
|    to that Work or Derivative Works thereof, that is intentionally | ||||
|    submitted to Licensor for inclusion in the Work by the copyright owner | ||||
|    or by an individual or Legal Entity authorized to submit on behalf of | ||||
|    the copyright owner. For the purposes of this definition, "submitted" | ||||
|    means any form of electronic, verbal, or written communication sent | ||||
|    to the Licensor or its representatives, including but not limited to | ||||
|    communication on electronic mailing lists, source code control systems, | ||||
|    and issue tracking systems that are managed by, or on behalf of, the | ||||
|    Licensor for the purpose of discussing and improving the Work, but | ||||
|    excluding communication that is conspicuously marked or otherwise | ||||
|    designated in writing by the copyright owner as "Not a Contribution." | ||||
|  | ||||
|    "Contributor" shall mean Licensor and any individual or Legal Entity | ||||
|    on behalf of whom a Contribution has been received by Licensor and | ||||
|    subsequently incorporated within the Work. | ||||
|  | ||||
| 2. Grant of Copyright License. Subject to the terms and conditions of | ||||
|    this License, each Contributor hereby grants to You a perpetual, | ||||
|    worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|    copyright license to reproduce, prepare Derivative Works of, | ||||
|    publicly display, publicly perform, sublicense, and distribute the | ||||
|    Work and such Derivative Works in Source or Object form. | ||||
|  | ||||
| 3. Grant of Patent License. Subject to the terms and conditions of | ||||
|    this License, each Contributor hereby grants to You a perpetual, | ||||
|    worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|    (except as stated in this section) patent license to make, have made, | ||||
|    use, offer to sell, sell, import, and otherwise transfer the Work, | ||||
|    where such license applies only to those patent claims licensable | ||||
|    by such Contributor that are necessarily infringed by their | ||||
|    Contribution(s) alone or by combination of their Contribution(s) | ||||
|    with the Work to which such Contribution(s) was submitted. If You | ||||
|    institute patent litigation against any entity (including a | ||||
|    cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||
|    or a Contribution incorporated within the Work constitutes direct | ||||
|    or contributory patent infringement, then any patent licenses | ||||
|    granted to You under this License for that Work shall terminate | ||||
|    as of the date such litigation is filed. | ||||
|  | ||||
| 4. Redistribution. You may reproduce and distribute copies of the | ||||
|    Work or Derivative Works thereof in any medium, with or without | ||||
|    modifications, and in Source or Object form, provided that You | ||||
|    meet the following conditions: | ||||
|  | ||||
|    (a) You must give any other recipients of the Work or | ||||
|        Derivative Works a copy of this License; and | ||||
|  | ||||
|    (b) You must cause any modified files to carry prominent notices | ||||
|        stating that You changed the files; and | ||||
|  | ||||
|    (c) You must retain, in the Source form of any Derivative Works | ||||
|        that You distribute, all copyright, patent, trademark, and | ||||
|        attribution notices from the Source form of the Work, | ||||
|        excluding those notices that do not pertain to any part of | ||||
|        the Derivative Works; and | ||||
|  | ||||
|    (d) If the Work includes a "NOTICE" text file as part of its | ||||
|        distribution, then any Derivative Works that You distribute must | ||||
|        include a readable copy of the attribution notices contained | ||||
|        within such NOTICE file, excluding those notices that do not | ||||
|        pertain to any part of the Derivative Works, in at least one | ||||
|        of the following places: within a NOTICE text file distributed | ||||
|        as part of the Derivative Works; within the Source form or | ||||
|        documentation, if provided along with the Derivative Works; or, | ||||
|        within a display generated by the Derivative Works, if and | ||||
|        wherever such third-party notices normally appear. The contents | ||||
|        of the NOTICE file are for informational purposes only and | ||||
|        do not modify the License. You may add Your own attribution | ||||
|        notices within Derivative Works that You distribute, alongside | ||||
|        or as an addendum to the NOTICE text from the Work, provided | ||||
|        that such additional attribution notices cannot be construed | ||||
|        as modifying the License. | ||||
|  | ||||
|    You may add Your own copyright statement to Your modifications and | ||||
|    may provide additional or different license terms and conditions | ||||
|    for use, reproduction, or distribution of Your modifications, or | ||||
|    for any such Derivative Works as a whole, provided Your use, | ||||
|    reproduction, and distribution of the Work otherwise complies with | ||||
|    the conditions stated in this License. | ||||
|  | ||||
| 5. Submission of Contributions. Unless You explicitly state otherwise, | ||||
|    any Contribution intentionally submitted for inclusion in the Work | ||||
|    by You to the Licensor shall be under the terms and conditions of | ||||
|    this License, without any additional terms or conditions. | ||||
|    Notwithstanding the above, nothing herein shall supersede or modify | ||||
|    the terms of any separate license agreement you may have executed | ||||
|    with Licensor regarding such Contributions. | ||||
|  | ||||
| 6. Trademarks. This License does not grant permission to use the trade | ||||
|    names, trademarks, service marks, or product names of the Licensor, | ||||
|    except as required for reasonable and customary use in describing the | ||||
|    origin of the Work and reproducing the content of the NOTICE file. | ||||
|  | ||||
| 7. Disclaimer of Warranty. Unless required by applicable law or | ||||
|    agreed to in writing, Licensor provides the Work (and each | ||||
|    Contributor provides its Contributions) on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||
|    implied, including, without limitation, any warranties or conditions | ||||
|    of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||
|    PARTICULAR PURPOSE. You are solely responsible for determining the | ||||
|    appropriateness of using or redistributing the Work and assume any | ||||
|    risks associated with Your exercise of permissions under this License. | ||||
|  | ||||
| 8. Limitation of Liability. In no event and under no legal theory, | ||||
|    whether in tort (including negligence), contract, or otherwise, | ||||
|    unless required by applicable law (such as deliberate and grossly | ||||
|    negligent acts) or agreed to in writing, shall any Contributor be | ||||
|    liable to You for damages, including any direct, indirect, special, | ||||
|    incidental, or consequential damages of any character arising as a | ||||
|    result of this License or out of the use or inability to use the | ||||
|    Work (including but not limited to damages for loss of goodwill, | ||||
|    work stoppage, computer failure or malfunction, or any and all | ||||
|    other commercial damages or losses), even if such Contributor | ||||
|    has been advised of the possibility of such damages. | ||||
|  | ||||
| 9. Accepting Warranty or Additional Liability. While redistributing | ||||
|    the Work or Derivative Works thereof, You may choose to offer, | ||||
|    and charge a fee for, acceptance of support, warranty, indemnity, | ||||
|    or other liability obligations and/or rights consistent with this | ||||
|    License. However, in accepting such obligations, You may act only | ||||
|    on Your own behalf and on Your sole responsibility, not on behalf | ||||
|    of any other Contributor, and only if You agree to indemnify, | ||||
|    defend, and hold each Contributor harmless for any liability | ||||
|    incurred by, or claims asserted against, such Contributor by reason | ||||
|    of your accepting any such warranty or additional liability. | ||||
|  | ||||
| END OF TERMS AND CONDITIONS | ||||
|  | ||||
| APPENDIX: How to apply the Apache License to your work. | ||||
|  | ||||
|    To apply the Apache License to your work, attach the following | ||||
|    boilerplate notice, with the fields enclosed by brackets "[]" | ||||
|    replaced with your own identifying information. (Don't include | ||||
|    the brackets!)  The text should be enclosed in the appropriate | ||||
|    comment syntax for the file format. We also recommend that a | ||||
|    file or class name and description of purpose be included on the | ||||
|    same "printed page" as the copyright notice for easier | ||||
|    identification within third-party archives. | ||||
|  | ||||
| Copyright 2014 Pivotal | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|  | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
							
								
								
									
										22
									
								
								vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| package jibber_jabber | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE = "Could not detect Language" | ||||
| ) | ||||
|  | ||||
| func splitLocale(locale string) (string, string) { | ||||
| 	formattedLocale := strings.Split(locale, ".")[0] | ||||
| 	formattedLocale = strings.Replace(formattedLocale, "-", "_", -1) | ||||
|  | ||||
| 	pieces := strings.Split(formattedLocale, "_") | ||||
| 	language := pieces[0] | ||||
| 	territory := "" | ||||
| 	if len(pieces) > 1 { | ||||
| 		territory = strings.Split(formattedLocale, "_")[1] | ||||
| 	} | ||||
| 	return language, territory | ||||
| } | ||||
							
								
								
									
										57
									
								
								vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_unix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| // +build darwin freebsd linux netbsd openbsd | ||||
|  | ||||
| package jibber_jabber | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| func getLangFromEnv() (locale string) { | ||||
| 	locale = os.Getenv("LC_ALL") | ||||
| 	if locale == "" { | ||||
| 		locale = os.Getenv("LANG") | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func getUnixLocale() (unix_locale string, err error) { | ||||
| 	unix_locale = getLangFromEnv() | ||||
| 	if unix_locale == "" { | ||||
| 		err = errors.New(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE) | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func DetectIETF() (locale string, err error) { | ||||
| 	unix_locale, err := getUnixLocale() | ||||
| 	if err == nil { | ||||
| 		language, territory := splitLocale(unix_locale) | ||||
| 		locale = language | ||||
| 		if territory != "" { | ||||
| 			locale = strings.Join([]string{language, territory}, "-") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func DetectLanguage() (language string, err error) { | ||||
| 	unix_locale, err := getUnixLocale() | ||||
| 	if err == nil { | ||||
| 		language, _ = splitLocale(unix_locale) | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func DetectTerritory() (territory string, err error) { | ||||
| 	unix_locale, err := getUnixLocale() | ||||
| 	if err == nil { | ||||
| 		_, territory = splitLocale(unix_locale) | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										114
									
								
								vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								vendor/github.com/cloudfoundry/jibber_jabber/jibber_jabber_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | ||||
| // +build windows | ||||
|  | ||||
| package jibber_jabber | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"syscall" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| const LOCALE_NAME_MAX_LENGTH uint32 = 85 | ||||
|  | ||||
| var SUPPORTED_LOCALES = map[uintptr]string{ | ||||
| 	0x0407: "de-DE", | ||||
| 	0x0409: "en-US", | ||||
| 	0x0c0a: "es-ES", //or is it 0x040a | ||||
| 	0x040c: "fr-FR", | ||||
| 	0x0410: "it-IT", | ||||
| 	0x0411: "ja-JA", | ||||
| 	0x0412: "ko_KR", | ||||
| 	0x0416: "pt-BR", | ||||
| 	//0x0419: "ru_RU", - Will add support for Russian when nicksnyder/go-i18n supports Russian | ||||
| 	0x0804: "zh-CN", | ||||
| 	0x0c04: "zh-HK", | ||||
| 	0x0404: "zh-TW", | ||||
| } | ||||
|  | ||||
| func getWindowsLocaleFrom(sysCall string) (locale string, err error) { | ||||
| 	buffer := make([]uint16, LOCALE_NAME_MAX_LENGTH) | ||||
|  | ||||
| 	dll := syscall.MustLoadDLL("kernel32") | ||||
| 	proc := dll.MustFindProc(sysCall) | ||||
| 	r, _, dllError := proc.Call(uintptr(unsafe.Pointer(&buffer[0])), uintptr(LOCALE_NAME_MAX_LENGTH)) | ||||
| 	if r == 0 { | ||||
| 		err = errors.New(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE + ":\n" + dllError.Error()) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	locale = syscall.UTF16ToString(buffer) | ||||
|  | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func getAllWindowsLocaleFrom(sysCall string) (string, error) { | ||||
| 	dll, err := syscall.LoadDLL("kernel32") | ||||
| 	if err != nil { | ||||
| 		return "", errors.New("Could not find kernel32 dll") | ||||
| 	} | ||||
|  | ||||
| 	proc, err := dll.FindProc(sysCall) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	locale, _, dllError := proc.Call() | ||||
| 	if locale == 0 { | ||||
| 		return "", errors.New(COULD_NOT_DETECT_PACKAGE_ERROR_MESSAGE + ":\n" + dllError.Error()) | ||||
| 	} | ||||
|  | ||||
| 	return SUPPORTED_LOCALES[locale], nil | ||||
| } | ||||
|  | ||||
| func getWindowsLocale() (locale string, err error) { | ||||
| 	dll, err := syscall.LoadDLL("kernel32") | ||||
| 	if err != nil { | ||||
| 		return "", errors.New("Could not find kernel32 dll") | ||||
| 	} | ||||
|  | ||||
| 	proc, err := dll.FindProc("GetVersion") | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	v, _, _ := proc.Call() | ||||
| 	windowsVersion := byte(v) | ||||
| 	isVistaOrGreater := (windowsVersion >= 6) | ||||
|  | ||||
| 	if isVistaOrGreater { | ||||
| 		locale, err = getWindowsLocaleFrom("GetUserDefaultLocaleName") | ||||
| 		if err != nil { | ||||
| 			locale, err = getWindowsLocaleFrom("GetSystemDefaultLocaleName") | ||||
| 		} | ||||
| 	} else if !isVistaOrGreater { | ||||
| 		locale, err = getAllWindowsLocaleFrom("GetUserDefaultLCID") | ||||
| 		if err != nil { | ||||
| 			locale, err = getAllWindowsLocaleFrom("GetSystemDefaultLCID") | ||||
| 		} | ||||
| 	} else { | ||||
| 		panic(v) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| func DetectIETF() (locale string, err error) { | ||||
| 	locale, err = getWindowsLocale() | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func DetectLanguage() (language string, err error) { | ||||
| 	windows_locale, err := getWindowsLocale() | ||||
| 	if err == nil { | ||||
| 		language, _ = splitLocale(windows_locale) | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func DetectTerritory() (territory string, err error) { | ||||
| 	windows_locale, err := getWindowsLocale() | ||||
| 	if err == nil { | ||||
| 		_, territory = splitLocale(windows_locale) | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										19
									
								
								vendor/github.com/nicksnyder/go-i18n/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/nicksnyder/go-i18n/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| Copyright (c) 2014 Nick Snyder https://github.com/nicksnyder | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in | ||||
| all copies or substantial portions of the Software. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
| THE SOFTWARE. | ||||
							
								
								
									
										129
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/i18n/bundle.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/i18n/bundle.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | ||||
| package i18n | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
|  | ||||
| 	"github.com/nicksnyder/go-i18n/v2/internal" | ||||
| 	"github.com/nicksnyder/go-i18n/v2/internal/plural" | ||||
|  | ||||
| 	"golang.org/x/text/language" | ||||
| ) | ||||
|  | ||||
| // UnmarshalFunc unmarshals data into v. | ||||
| type UnmarshalFunc = internal.UnmarshalFunc | ||||
|  | ||||
| // Bundle stores a set of messages and pluralization rules. | ||||
| // Most applications only need a single bundle | ||||
| // that is initialized early in the application's lifecycle. | ||||
| type Bundle struct { | ||||
| 	// DefaultLanguage is the default language of the bundle. | ||||
| 	DefaultLanguage language.Tag | ||||
|  | ||||
| 	// UnmarshalFuncs is a map of file extensions to UnmarshalFuncs. | ||||
| 	UnmarshalFuncs map[string]UnmarshalFunc | ||||
|  | ||||
| 	messageTemplates map[language.Tag]map[string]*internal.MessageTemplate | ||||
| 	pluralRules      plural.Rules | ||||
| 	tags             []language.Tag | ||||
| 	matcher          language.Matcher | ||||
| } | ||||
|  | ||||
| func (b *Bundle) init() { | ||||
| 	if b.pluralRules == nil { | ||||
| 		b.pluralRules = plural.DefaultRules() | ||||
| 	} | ||||
| 	b.addTag(b.DefaultLanguage) | ||||
| } | ||||
|  | ||||
| // RegisterUnmarshalFunc registers an UnmarshalFunc for format. | ||||
| func (b *Bundle) RegisterUnmarshalFunc(format string, unmarshalFunc UnmarshalFunc) { | ||||
| 	if b.UnmarshalFuncs == nil { | ||||
| 		b.UnmarshalFuncs = make(map[string]UnmarshalFunc) | ||||
| 	} | ||||
| 	b.UnmarshalFuncs[format] = unmarshalFunc | ||||
| } | ||||
|  | ||||
| // LoadMessageFile loads the bytes from path | ||||
| // and then calls ParseMessageFileBytes. | ||||
| func (b *Bundle) LoadMessageFile(path string) (*MessageFile, error) { | ||||
| 	buf, err := ioutil.ReadFile(path) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return b.ParseMessageFileBytes(buf, path) | ||||
| } | ||||
|  | ||||
| // MustLoadMessageFile is similar to LoadTranslationFile | ||||
| // except it panics if an error happens. | ||||
| func (b *Bundle) MustLoadMessageFile(path string) { | ||||
| 	if _, err := b.LoadMessageFile(path); err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // MessageFile represents a parsed message file. | ||||
| type MessageFile = internal.MessageFile | ||||
|  | ||||
| // ParseMessageFileBytes parses the bytes in buf to add translations to the bundle. | ||||
| // | ||||
| // The format of the file is everything after the last ".". | ||||
| // | ||||
| // The language tag of the file is everything after the second to last "." or after the last path separator, but before the format. | ||||
| func (b *Bundle) ParseMessageFileBytes(buf []byte, path string) (*MessageFile, error) { | ||||
| 	messageFile, err := internal.ParseMessageFileBytes(buf, path, b.UnmarshalFuncs) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if err := b.AddMessages(messageFile.Tag, messageFile.Messages...); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return messageFile, nil | ||||
| } | ||||
|  | ||||
| // MustParseMessageFileBytes is similar to ParseMessageFileBytes | ||||
| // except it panics if an error happens. | ||||
| func (b *Bundle) MustParseMessageFileBytes(buf []byte, path string) { | ||||
| 	if _, err := b.ParseMessageFileBytes(buf, path); err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // AddMessages adds messages for a language. | ||||
| // It is useful if your messages are in a format not supported by ParseMessageFileBytes. | ||||
| func (b *Bundle) AddMessages(tag language.Tag, messages ...*Message) error { | ||||
| 	b.init() | ||||
| 	pluralRule := b.pluralRules.Rule(tag) | ||||
| 	if pluralRule == nil { | ||||
| 		return fmt.Errorf("no plural rule registered for %s", tag) | ||||
| 	} | ||||
| 	if b.messageTemplates == nil { | ||||
| 		b.messageTemplates = map[language.Tag]map[string]*internal.MessageTemplate{} | ||||
| 	} | ||||
| 	if b.messageTemplates[tag] == nil { | ||||
| 		b.messageTemplates[tag] = map[string]*internal.MessageTemplate{} | ||||
| 		b.addTag(tag) | ||||
| 	} | ||||
| 	for _, m := range messages { | ||||
| 		b.messageTemplates[tag][m.ID] = internal.NewMessageTemplate(m) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // MustAddMessages is similar to AddMessages except it panics if an error happens. | ||||
| func (b *Bundle) MustAddMessages(tag language.Tag, messages ...*Message) { | ||||
| 	if err := b.AddMessages(tag, messages...); err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (b *Bundle) addTag(tag language.Tag) { | ||||
| 	for _, t := range b.tags { | ||||
| 		if t == tag { | ||||
| 			// Tag already exists | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	b.tags = append(b.tags, tag) | ||||
| 	b.matcher = language.NewMatcher(b.tags) | ||||
| } | ||||
							
								
								
									
										21
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/i18n/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/i18n/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| // Package i18n provides support for looking up messages | ||||
| // according to a set of locale preferences. | ||||
| // | ||||
| // Create a Bundle to use for the lifetime of your application. | ||||
| //     bundle := &i18n.Bundle{DefaultLanguage: language.English} | ||||
| // | ||||
| // Create a Localizer to use for a set of language preferences. | ||||
| //     func(w http.ResponseWriter, r *http.Request) { | ||||
| //         lang := r.FormValue("lang") | ||||
| //         accept := r.Header.Get("Accept-Language") | ||||
| //         localizer := i18n.NewLocalizer(bundle, lang, accept) | ||||
| //     } | ||||
| // | ||||
| // Use the Localizer to lookup messages. | ||||
| //     localizer.MustLocalize(&i18n.LocalizeConfig{ | ||||
| // 	        DefaultMessage: &i18n.Message{ | ||||
| // 	            ID: "HelloWorld", | ||||
| // 	            Other: "Hello World!", | ||||
| // 	        }, | ||||
| //     }) | ||||
| package i18n | ||||
							
								
								
									
										198
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/i18n/localizer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/i18n/localizer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,198 @@ | ||||
| package i18n | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"text/template" | ||||
|  | ||||
| 	"github.com/nicksnyder/go-i18n/v2/internal" | ||||
| 	"github.com/nicksnyder/go-i18n/v2/internal/plural" | ||||
| 	"golang.org/x/text/language" | ||||
| ) | ||||
|  | ||||
| // Localizer provides Localize and MustLocalize methods that return localized messages. | ||||
| type Localizer struct { | ||||
| 	// bundle contains the messages that can be returned by the Localizer. | ||||
| 	bundle *Bundle | ||||
|  | ||||
| 	// tags is the list of language tags that the Localizer checks | ||||
| 	// in order when localizing a message. | ||||
| 	tags []language.Tag | ||||
| } | ||||
|  | ||||
| // NewLocalizer returns a new Localizer that looks up messages | ||||
| // in the bundle according to the language preferences in langs. | ||||
| // It can parse Accept-Language headers as defined in http://www.ietf.org/rfc/rfc2616.txt. | ||||
| func NewLocalizer(bundle *Bundle, langs ...string) *Localizer { | ||||
| 	bundle.init() | ||||
| 	return &Localizer{ | ||||
| 		bundle: bundle, | ||||
| 		tags:   parseTags(langs), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func parseTags(langs []string) []language.Tag { | ||||
| 	tags := []language.Tag{} | ||||
| 	for _, lang := range langs { | ||||
| 		t, _, err := language.ParseAcceptLanguage(lang) | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		tags = append(tags, t...) | ||||
| 	} | ||||
| 	return tags | ||||
| } | ||||
|  | ||||
| // LocalizeConfig configures a call to the Localize method on Localizer. | ||||
| type LocalizeConfig struct { | ||||
| 	// MessageID is the id of the message to lookup. | ||||
| 	// This field is ignored if DefaultMessage is set. | ||||
| 	MessageID string | ||||
|  | ||||
| 	// TemplateData is the data passed when executing the message's template. | ||||
| 	// If TemplateData is nil and PluralCount is not nil, then the message template | ||||
| 	// will be executed with data that contains the plural count. | ||||
| 	TemplateData interface{} | ||||
|  | ||||
| 	// PluralCount determines which plural form of the message is used. | ||||
| 	PluralCount interface{} | ||||
|  | ||||
| 	// DefaultMessage is used if the message is not found in any message files. | ||||
| 	DefaultMessage *Message | ||||
|  | ||||
| 	// Funcs is used to extend the Go template engines built in functions | ||||
| 	Funcs template.FuncMap | ||||
| } | ||||
|  | ||||
| type invalidPluralCountErr struct { | ||||
| 	messageID   string | ||||
| 	pluralCount interface{} | ||||
| 	err         error | ||||
| } | ||||
|  | ||||
| func (e *invalidPluralCountErr) Error() string { | ||||
| 	return fmt.Sprintf("invalid plural count %#v for message id %q: %s", e.pluralCount, e.messageID, e.err) | ||||
| } | ||||
|  | ||||
| type messageNotFoundErr struct { | ||||
| 	messageID string | ||||
| } | ||||
|  | ||||
| func (e *messageNotFoundErr) Error() string { | ||||
| 	return fmt.Sprintf("message %q not found", e.messageID) | ||||
| } | ||||
|  | ||||
| type pluralizeErr struct { | ||||
| 	messageID string | ||||
| 	tag       language.Tag | ||||
| } | ||||
|  | ||||
| func (e *pluralizeErr) Error() string { | ||||
| 	return fmt.Sprintf("unable to pluralize %q because there no plural rule for %q", e.messageID, e.tag) | ||||
| } | ||||
|  | ||||
| // Localize returns a localized message. | ||||
| func (l *Localizer) Localize(lc *LocalizeConfig) (string, error) { | ||||
| 	messageID := lc.MessageID | ||||
| 	if lc.DefaultMessage != nil { | ||||
| 		messageID = lc.DefaultMessage.ID | ||||
| 	} | ||||
|  | ||||
| 	var operands *plural.Operands | ||||
| 	templateData := lc.TemplateData | ||||
| 	if lc.PluralCount != nil { | ||||
| 		var err error | ||||
| 		operands, err = plural.NewOperands(lc.PluralCount) | ||||
| 		if err != nil { | ||||
| 			return "", &invalidPluralCountErr{messageID: messageID, pluralCount: lc.PluralCount, err: err} | ||||
| 		} | ||||
| 		if templateData == nil { | ||||
| 			templateData = map[string]interface{}{ | ||||
| 				"PluralCount": lc.PluralCount, | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	tag, template := l.getTemplate(messageID, lc.DefaultMessage) | ||||
| 	if template == nil { | ||||
| 		return "", &messageNotFoundErr{messageID: messageID} | ||||
| 	} | ||||
| 	pluralForm := l.pluralForm(tag, operands) | ||||
| 	if pluralForm == plural.Invalid { | ||||
| 		return "", &pluralizeErr{messageID: messageID, tag: tag} | ||||
| 	} | ||||
| 	return template.Execute(pluralForm, templateData, lc.Funcs) | ||||
| } | ||||
|  | ||||
| func (l *Localizer) getTemplate(id string, defaultMessage *Message) (language.Tag, *internal.MessageTemplate) { | ||||
| 	// Fast path. | ||||
| 	// Optimistically assume this message id is defined in each language. | ||||
| 	fastTag, template := l.matchTemplate(id, l.bundle.matcher, l.bundle.tags) | ||||
| 	if template != nil { | ||||
| 		return fastTag, template | ||||
| 	} | ||||
| 	if fastTag == l.bundle.DefaultLanguage { | ||||
| 		if defaultMessage == nil { | ||||
| 			return fastTag, nil | ||||
| 		} | ||||
| 		return fastTag, internal.NewMessageTemplate(defaultMessage) | ||||
| 	} | ||||
| 	if len(l.bundle.tags) > 1 { | ||||
| 		// Slow path. | ||||
| 		// We didn't find a translation for the tag suggested by the default matcher | ||||
| 		// so we need to create a new matcher that contains only the tags in the bundle | ||||
| 		// that have this message. | ||||
| 		foundTags := make([]language.Tag, 0, len(l.bundle.messageTemplates)) | ||||
| 		if l.bundle.DefaultLanguage != fastTag { | ||||
| 			foundTags = append(foundTags, l.bundle.DefaultLanguage) | ||||
| 		} | ||||
| 		for t, templates := range l.bundle.messageTemplates { | ||||
| 			if t == fastTag { | ||||
| 				// We already tried this tag in the fast path | ||||
| 				continue | ||||
| 			} | ||||
| 			template := templates[id] | ||||
| 			if template == nil || template.Other == "" { | ||||
| 				continue | ||||
| 			} | ||||
| 			foundTags = append(foundTags, t) | ||||
| 		} | ||||
| 		tag, template := l.matchTemplate(id, language.NewMatcher(foundTags), foundTags) | ||||
| 		if template != nil { | ||||
| 			return tag, template | ||||
| 		} | ||||
| 	} | ||||
| 	if defaultMessage == nil { | ||||
| 		return l.bundle.DefaultLanguage, nil | ||||
| 	} | ||||
| 	return l.bundle.DefaultLanguage, internal.NewMessageTemplate(defaultMessage) | ||||
| } | ||||
|  | ||||
| func (l *Localizer) matchTemplate(id string, matcher language.Matcher, tags []language.Tag) (language.Tag, *internal.MessageTemplate) { | ||||
| 	_, i, _ := matcher.Match(l.tags...) | ||||
| 	tag := tags[i] | ||||
| 	templates := l.bundle.messageTemplates[tag] | ||||
| 	if templates != nil && templates[id] != nil { | ||||
| 		return tag, templates[id] | ||||
| 	} | ||||
| 	return tag, nil | ||||
| } | ||||
|  | ||||
| func (l *Localizer) pluralForm(tag language.Tag, operands *plural.Operands) plural.Form { | ||||
| 	if operands == nil { | ||||
| 		return plural.Other | ||||
| 	} | ||||
| 	pluralRule := l.bundle.pluralRules.Rule(tag) | ||||
| 	if pluralRule == nil { | ||||
| 		return plural.Invalid | ||||
| 	} | ||||
| 	return pluralRule.PluralFormFunc(operands) | ||||
| } | ||||
|  | ||||
| // MustLocalize is similar to Localize, except it panics if an error happens. | ||||
| func (l *Localizer) MustLocalize(lc *LocalizeConfig) string { | ||||
| 	localized, err := l.Localize(lc) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return localized | ||||
| } | ||||
							
								
								
									
										6
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/i18n/message.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/i18n/message.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| package i18n | ||||
|  | ||||
| import "github.com/nicksnyder/go-i18n/v2/internal" | ||||
|  | ||||
| // Message is a string that can be localized. | ||||
| type Message = internal.Message | ||||
							
								
								
									
										164
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/message.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/message.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,164 @@ | ||||
| package internal | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // Message is a string that can be localized. | ||||
| type Message struct { | ||||
| 	// ID uniquely identifies the message. | ||||
| 	ID string | ||||
|  | ||||
| 	// Hash uniquely identifies the content of the message | ||||
| 	// that this message was translated from. | ||||
| 	Hash string | ||||
|  | ||||
| 	// Description describes the message to give additional | ||||
| 	// context to translators that may be relevant for translation. | ||||
| 	Description string | ||||
|  | ||||
| 	// LeftDelim is the left Go template delimiter. | ||||
| 	LeftDelim string | ||||
|  | ||||
| 	// RightDelim is the right Go template delimiter.`` | ||||
| 	RightDelim string | ||||
|  | ||||
| 	// Zero is the content of the message for the CLDR plural form "zero". | ||||
| 	Zero string | ||||
|  | ||||
| 	// One is the content of the message for the CLDR plural form "one". | ||||
| 	One string | ||||
|  | ||||
| 	// Two is the content of the message for the CLDR plural form "two". | ||||
| 	Two string | ||||
|  | ||||
| 	// Few is the content of the message for the CLDR plural form "few". | ||||
| 	Few string | ||||
|  | ||||
| 	// Many is the content of the message for the CLDR plural form "many". | ||||
| 	Many string | ||||
|  | ||||
| 	// Other is the content of the message for the CLDR plural form "other". | ||||
| 	Other string | ||||
| } | ||||
|  | ||||
| // NewMessage parses data and returns a new message. | ||||
| func NewMessage(data interface{}) (*Message, error) { | ||||
| 	m := &Message{} | ||||
| 	if err := m.unmarshalInterface(data); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return m, nil | ||||
| } | ||||
|  | ||||
| // MustNewMessage is similar to NewMessage except it panics if an error happens. | ||||
| func MustNewMessage(data interface{}) *Message { | ||||
| 	m, err := NewMessage(data) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return m | ||||
| } | ||||
|  | ||||
| // unmarshalInterface unmarshals a message from data. | ||||
| func (m *Message) unmarshalInterface(v interface{}) error { | ||||
| 	strdata, err := stringMap(v) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	for k, v := range strdata { | ||||
| 		switch strings.ToLower(k) { | ||||
| 		case "id": | ||||
| 			m.ID = v | ||||
| 		case "description": | ||||
| 			m.Description = v | ||||
| 		case "hash": | ||||
| 			m.Hash = v | ||||
| 		case "leftDelim": | ||||
| 			m.LeftDelim = v | ||||
| 		case "rightDelim": | ||||
| 			m.RightDelim = v | ||||
| 		case "zero": | ||||
| 			m.Zero = v | ||||
| 		case "one": | ||||
| 			m.One = v | ||||
| 		case "two": | ||||
| 			m.Two = v | ||||
| 		case "few": | ||||
| 			m.Few = v | ||||
| 		case "many": | ||||
| 			m.Many = v | ||||
| 		case "other": | ||||
| 			m.Other = v | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func stringMap(v interface{}) (map[string]string, error) { | ||||
| 	switch value := v.(type) { | ||||
| 	case string: | ||||
| 		return map[string]string{ | ||||
| 			"other": value, | ||||
| 		}, nil | ||||
| 	case map[string]string: | ||||
| 		return value, nil | ||||
| 	case map[string]interface{}: | ||||
| 		strdata := map[string]string{} | ||||
| 		for k, v := range value { | ||||
| 			if k == "translation" { | ||||
| 				switch vt := v.(type) { | ||||
| 				case string: | ||||
| 					strdata["other"] = vt | ||||
| 				default: | ||||
| 					v1Message, err := stringMap(v) | ||||
| 					if err != nil { | ||||
| 						return nil, err | ||||
| 					} | ||||
| 					for kk, vv := range v1Message { | ||||
| 						strdata[kk] = vv | ||||
| 					} | ||||
| 				} | ||||
| 				continue | ||||
| 			} | ||||
| 			vstr, ok := v.(string) | ||||
| 			if !ok { | ||||
| 				return nil, fmt.Errorf("expected value for key %q be a string but got %#v", k, v) | ||||
| 			} | ||||
| 			strdata[k] = vstr | ||||
| 		} | ||||
| 		return strdata, nil | ||||
| 	case map[interface{}]interface{}: | ||||
| 		strdata := map[string]string{} | ||||
| 		for k, v := range value { | ||||
| 			kstr, ok := k.(string) | ||||
| 			if !ok { | ||||
| 				return nil, fmt.Errorf("expected key to be a string but got %#v", k) | ||||
| 			} | ||||
| 			if kstr == "translation" { | ||||
| 				switch vt := v.(type) { | ||||
| 				case string: | ||||
| 					strdata["other"] = vt | ||||
| 				default: | ||||
| 					v1Message, err := stringMap(v) | ||||
| 					if err != nil { | ||||
| 						return nil, err | ||||
| 					} | ||||
| 					for kk, vv := range v1Message { | ||||
| 						strdata[kk] = vv | ||||
| 					} | ||||
| 				} | ||||
| 				continue | ||||
| 			} | ||||
| 			vstr, ok := v.(string) | ||||
| 			if !ok { | ||||
| 				return nil, fmt.Errorf("expected value for key %q be a string but got %#v", k, v) | ||||
| 			} | ||||
| 			strdata[kstr] = vstr | ||||
| 		} | ||||
| 		return strdata, nil | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("unsupported type %#v", value) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										55
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/message_template.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/message_template.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| package internal | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
|  | ||||
| 	"text/template" | ||||
|  | ||||
| 	"github.com/nicksnyder/go-i18n/v2/internal/plural" | ||||
| ) | ||||
|  | ||||
| // MessageTemplate is an executable template for a message. | ||||
| type MessageTemplate struct { | ||||
| 	*Message | ||||
| 	PluralTemplates map[plural.Form]*Template | ||||
| } | ||||
|  | ||||
| // NewMessageTemplate returns a new message template. | ||||
| func NewMessageTemplate(m *Message) *MessageTemplate { | ||||
| 	pluralTemplates := map[plural.Form]*Template{} | ||||
| 	setPluralTemplate(pluralTemplates, plural.Zero, m.Zero) | ||||
| 	setPluralTemplate(pluralTemplates, plural.One, m.One) | ||||
| 	setPluralTemplate(pluralTemplates, plural.Two, m.Two) | ||||
| 	setPluralTemplate(pluralTemplates, plural.Few, m.Few) | ||||
| 	setPluralTemplate(pluralTemplates, plural.Many, m.Many) | ||||
| 	setPluralTemplate(pluralTemplates, plural.Other, m.Other) | ||||
| 	if len(pluralTemplates) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return &MessageTemplate{ | ||||
| 		Message:         m, | ||||
| 		PluralTemplates: pluralTemplates, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func setPluralTemplate(pluralTemplates map[plural.Form]*Template, pluralForm plural.Form, src string) { | ||||
| 	if src != "" { | ||||
| 		pluralTemplates[pluralForm] = &Template{Src: src} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Execute executes the template for the plural form and template data. | ||||
| func (mt *MessageTemplate) Execute(pluralForm plural.Form, data interface{}, funcs template.FuncMap) (string, error) { | ||||
| 	t := mt.PluralTemplates[pluralForm] | ||||
| 	if err := t.parse(mt.LeftDelim, mt.RightDelim, funcs); err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	if t.Template == nil { | ||||
| 		return t.Src, nil | ||||
| 	} | ||||
| 	var buf bytes.Buffer | ||||
| 	if err := t.Template.Execute(&buf, data); err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	return buf.String(), nil | ||||
| } | ||||
							
								
								
									
										112
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/parse.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/parse.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | ||||
| package internal | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
|  | ||||
| 	"golang.org/x/text/language" | ||||
| ) | ||||
|  | ||||
| // UnmarshalFunc unmarshals data into v. | ||||
| type UnmarshalFunc func(data []byte, v interface{}) error | ||||
|  | ||||
| // MessageFile represents a parsed message file. | ||||
| type MessageFile struct { | ||||
| 	Path     string | ||||
| 	Tag      language.Tag | ||||
| 	Format   string | ||||
| 	Messages []*Message | ||||
| } | ||||
|  | ||||
| // ParseMessageFileBytes returns the messages parsed from file. | ||||
| func ParseMessageFileBytes(buf []byte, path string, unmarshalFuncs map[string]UnmarshalFunc) (*MessageFile, error) { | ||||
| 	lang, format := parsePath(path) | ||||
| 	tag := language.Make(lang) | ||||
| 	messageFile := &MessageFile{ | ||||
| 		Path:   path, | ||||
| 		Tag:    tag, | ||||
| 		Format: format, | ||||
| 	} | ||||
| 	if len(buf) == 0 { | ||||
| 		return messageFile, nil | ||||
| 	} | ||||
| 	unmarshalFunc := unmarshalFuncs[messageFile.Format] | ||||
| 	if unmarshalFunc == nil { | ||||
| 		if messageFile.Format == "json" { | ||||
| 			unmarshalFunc = json.Unmarshal | ||||
| 		} else { | ||||
| 			return nil, fmt.Errorf("no unmarshaler registered for %s", messageFile.Format) | ||||
| 		} | ||||
| 	} | ||||
| 	var raw interface{} | ||||
| 	if err := unmarshalFunc(buf, &raw); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	switch data := raw.(type) { | ||||
| 	case map[string]interface{}: | ||||
| 		messageFile.Messages = make([]*Message, 0, len(data)) | ||||
| 		for id, data := range data { | ||||
| 			m, err := NewMessage(data) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			m.ID = id | ||||
| 			messageFile.Messages = append(messageFile.Messages, m) | ||||
| 		} | ||||
| 	case map[interface{}]interface{}: | ||||
| 		messageFile.Messages = make([]*Message, 0, len(data)) | ||||
| 		for id, data := range data { | ||||
| 			strid, ok := id.(string) | ||||
| 			if !ok { | ||||
| 				return nil, fmt.Errorf("expected key to be string but got %#v", id) | ||||
| 			} | ||||
| 			m, err := NewMessage(data) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			m.ID = strid | ||||
| 			messageFile.Messages = append(messageFile.Messages, m) | ||||
| 		} | ||||
| 	case []interface{}: | ||||
| 		// Backward compatibility for v1 file format. | ||||
| 		messageFile.Messages = make([]*Message, 0, len(data)) | ||||
| 		for _, data := range data { | ||||
| 			m, err := NewMessage(data) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			messageFile.Messages = append(messageFile.Messages, m) | ||||
| 		} | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("unsupported file format %T", raw) | ||||
| 	} | ||||
| 	return messageFile, nil | ||||
| } | ||||
|  | ||||
| func parsePath(path string) (langTag, format string) { | ||||
| 	formatStartIdx := -1 | ||||
| 	for i := len(path) - 1; i >= 0; i-- { | ||||
| 		c := path[i] | ||||
| 		if os.IsPathSeparator(c) { | ||||
| 			if formatStartIdx != -1 { | ||||
| 				langTag = path[i+1 : formatStartIdx] | ||||
| 			} | ||||
| 			return | ||||
| 		} | ||||
| 		if path[i] == '.' { | ||||
| 			if formatStartIdx != -1 { | ||||
| 				langTag = path[i+1 : formatStartIdx] | ||||
| 				return | ||||
| 			} | ||||
| 			if formatStartIdx == -1 { | ||||
| 				format = path[i+1:] | ||||
| 				formatStartIdx = i | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if formatStartIdx != -1 { | ||||
| 		langTag = path[:formatStartIdx] | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										3
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| // Package plural provides support for pluralizing messages | ||||
| // according to CLDR rules http://cldr.unicode.org/index/cldr-spec/plural-rules | ||||
| package plural | ||||
							
								
								
									
										16
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/form.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/form.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| package plural | ||||
|  | ||||
| // Form represents a language pluralization form as defined here: | ||||
| // http://cldr.unicode.org/index/cldr-spec/plural-rules | ||||
| type Form string | ||||
|  | ||||
| // All defined plural forms. | ||||
| const ( | ||||
| 	Invalid Form = "" | ||||
| 	Zero    Form = "zero" | ||||
| 	One     Form = "one" | ||||
| 	Two     Form = "two" | ||||
| 	Few     Form = "few" | ||||
| 	Many    Form = "many" | ||||
| 	Other   Form = "other" | ||||
| ) | ||||
							
								
								
									
										120
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/operands.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/operands.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| package plural | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // Operands is a representation of http://unicode.org/reports/tr35/tr35-numbers.html#Operands | ||||
| type Operands struct { | ||||
| 	N float64 // absolute value of the source number (integer and decimals) | ||||
| 	I int64   // integer digits of n | ||||
| 	V int64   // number of visible fraction digits in n, with trailing zeros | ||||
| 	W int64   // number of visible fraction digits in n, without trailing zeros | ||||
| 	F int64   // visible fractional digits in n, with trailing zeros | ||||
| 	T int64   // visible fractional digits in n, without trailing zeros | ||||
| } | ||||
|  | ||||
| // NEqualsAny returns true if o represents an integer equal to any of the arguments. | ||||
| func (o *Operands) NEqualsAny(any ...int64) bool { | ||||
| 	for _, i := range any { | ||||
| 		if o.I == i && o.T == 0 { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // NModEqualsAny returns true if o represents an integer equal to any of the arguments modulo mod. | ||||
| func (o *Operands) NModEqualsAny(mod int64, any ...int64) bool { | ||||
| 	modI := o.I % mod | ||||
| 	for _, i := range any { | ||||
| 		if modI == i && o.T == 0 { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // NInRange returns true if o represents an integer in the closed interval [from, to]. | ||||
| func (o *Operands) NInRange(from, to int64) bool { | ||||
| 	return o.T == 0 && from <= o.I && o.I <= to | ||||
| } | ||||
|  | ||||
| // NModInRange returns true if o represents an integer in the closed interval [from, to] modulo mod. | ||||
| func (o *Operands) NModInRange(mod, from, to int64) bool { | ||||
| 	modI := o.I % mod | ||||
| 	return o.T == 0 && from <= modI && modI <= to | ||||
| } | ||||
|  | ||||
| // NewOperands returns the operands for number. | ||||
| func NewOperands(number interface{}) (*Operands, error) { | ||||
| 	switch number := number.(type) { | ||||
| 	case int: | ||||
| 		return newOperandsInt64(int64(number)), nil | ||||
| 	case int8: | ||||
| 		return newOperandsInt64(int64(number)), nil | ||||
| 	case int16: | ||||
| 		return newOperandsInt64(int64(number)), nil | ||||
| 	case int32: | ||||
| 		return newOperandsInt64(int64(number)), nil | ||||
| 	case int64: | ||||
| 		return newOperandsInt64(number), nil | ||||
| 	case string: | ||||
| 		return newOperandsString(number) | ||||
| 	case float32, float64: | ||||
| 		return nil, fmt.Errorf("floats should be formatted into a string") | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("invalid type %T; expected integer or string", number) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func newOperandsInt64(i int64) *Operands { | ||||
| 	if i < 0 { | ||||
| 		i = -i | ||||
| 	} | ||||
| 	return &Operands{float64(i), i, 0, 0, 0, 0} | ||||
| } | ||||
|  | ||||
| func newOperandsString(s string) (*Operands, error) { | ||||
| 	if s[0] == '-' { | ||||
| 		s = s[1:] | ||||
| 	} | ||||
| 	n, err := strconv.ParseFloat(s, 64) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	ops := &Operands{N: n} | ||||
| 	parts := strings.SplitN(s, ".", 2) | ||||
| 	ops.I, err = strconv.ParseInt(parts[0], 10, 64) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(parts) == 1 { | ||||
| 		return ops, nil | ||||
| 	} | ||||
| 	fraction := parts[1] | ||||
| 	ops.V = int64(len(fraction)) | ||||
| 	for i := ops.V - 1; i >= 0; i-- { | ||||
| 		if fraction[i] != '0' { | ||||
| 			ops.W = i + 1 | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if ops.V > 0 { | ||||
| 		f, err := strconv.ParseInt(fraction, 10, 0) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		ops.F = f | ||||
| 	} | ||||
| 	if ops.W > 0 { | ||||
| 		t, err := strconv.ParseInt(fraction[:ops.W], 10, 0) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		ops.T = t | ||||
| 	} | ||||
| 	return ops, nil | ||||
| } | ||||
							
								
								
									
										44
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/rule.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/rule.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| package plural | ||||
|  | ||||
| import ( | ||||
| 	"golang.org/x/text/language" | ||||
| ) | ||||
|  | ||||
| // Rule defines the CLDR plural rules for a language. | ||||
| // http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html | ||||
| // http://unicode.org/reports/tr35/tr35-numbers.html#Operands | ||||
| type Rule struct { | ||||
| 	PluralForms    map[Form]struct{} | ||||
| 	PluralFormFunc func(*Operands) Form | ||||
| } | ||||
|  | ||||
| func addPluralRules(rules Rules, ids []string, ps *Rule) { | ||||
| 	for _, id := range ids { | ||||
| 		if id == "root" { | ||||
| 			continue | ||||
| 		} | ||||
| 		tag := language.MustParse(id) | ||||
| 		rules[tag] = ps | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func newPluralFormSet(pluralForms ...Form) map[Form]struct{} { | ||||
| 	set := make(map[Form]struct{}, len(pluralForms)) | ||||
| 	for _, plural := range pluralForms { | ||||
| 		set[plural] = struct{}{} | ||||
| 	} | ||||
| 	return set | ||||
| } | ||||
|  | ||||
| func intInRange(i, from, to int64) bool { | ||||
| 	return from <= i && i <= to | ||||
| } | ||||
|  | ||||
| func intEqualsAny(i int64, any ...int64) bool { | ||||
| 	for _, a := range any { | ||||
| 		if i == a { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
							
								
								
									
										561
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/rule_gen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										561
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/rule_gen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,561 @@ | ||||
| // This file is generated by i18n/plural/codegen/generate.sh; DO NOT EDIT | ||||
|  | ||||
| package plural | ||||
|  | ||||
| // DefaultRules returns a map of Rules generated from CLDR language data. | ||||
| func DefaultRules() Rules { | ||||
| 	rules := Rules{} | ||||
|  | ||||
| 	addPluralRules(rules, []string{"bm", "bo", "dz", "id", "ig", "ii", "in", "ja", "jbo", "jv", "jw", "kde", "kea", "km", "ko", "lkt", "lo", "ms", "my", "nqo", "root", "sah", "ses", "sg", "th", "to", "vi", "wo", "yo", "yue", "zh"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"am", "as", "bn", "fa", "gu", "hi", "kn", "mr", "zu"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// i = 0 or n = 1 | ||||
| 			if intEqualsAny(ops.I, 0) || | ||||
| 				ops.NEqualsAny(1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"ff", "fr", "hy", "kab"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// i = 0,1 | ||||
| 			if intEqualsAny(ops.I, 0, 1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"pt"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// i = 0..1 | ||||
| 			if intInRange(ops.I, 0, 1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"ast", "ca", "de", "en", "et", "fi", "fy", "gl", "io", "it", "ji", "nl", "pt_PT", "scn", "sv", "sw", "ur", "yi"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// i = 1 and v = 0 | ||||
| 			if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) { | ||||
| 				return One | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"si"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// n = 0,1 or i = 0 and f = 1 | ||||
| 			if ops.NEqualsAny(0, 1) || | ||||
| 				intEqualsAny(ops.I, 0) && intEqualsAny(ops.F, 1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"ak", "bh", "guw", "ln", "mg", "nso", "pa", "ti", "wa"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// n = 0..1 | ||||
| 			if ops.NInRange(0, 1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"tzm"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// n = 0..1 or n = 11..99 | ||||
| 			if ops.NInRange(0, 1) || | ||||
| 				ops.NInRange(11, 99) { | ||||
| 				return One | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"af", "asa", "az", "bem", "bez", "bg", "brx", "ce", "cgg", "chr", "ckb", "dv", "ee", "el", "eo", "es", "eu", "fo", "fur", "gsw", "ha", "haw", "hu", "jgo", "jmc", "ka", "kaj", "kcg", "kk", "kkj", "kl", "ks", "ksb", "ku", "ky", "lb", "lg", "mas", "mgo", "ml", "mn", "nah", "nb", "nd", "ne", "nn", "nnh", "no", "nr", "ny", "nyn", "om", "or", "os", "pap", "ps", "rm", "rof", "rwk", "saq", "sd", "sdh", "seh", "sn", "so", "sq", "ss", "ssy", "st", "syr", "ta", "te", "teo", "tig", "tk", "tn", "tr", "ts", "ug", "uz", "ve", "vo", "vun", "wae", "xh", "xog"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// n = 1 | ||||
| 			if ops.NEqualsAny(1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"da"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// n = 1 or t != 0 and i = 0,1 | ||||
| 			if ops.NEqualsAny(1) || | ||||
| 				!intEqualsAny(ops.T, 0) && intEqualsAny(ops.I, 0, 1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"is"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// t = 0 and i % 10 = 1 and i % 100 != 11 or t != 0 | ||||
| 			if intEqualsAny(ops.T, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) || | ||||
| 				!intEqualsAny(ops.T, 0) { | ||||
| 				return One | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"mk"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11 | ||||
| 			if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) || | ||||
| 				intEqualsAny(ops.F%10, 1) && !intEqualsAny(ops.F%100, 11) { | ||||
| 				return One | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"fil", "tl"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9 | ||||
| 			if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I, 1, 2, 3) || | ||||
| 				intEqualsAny(ops.V, 0) && !intEqualsAny(ops.I%10, 4, 6, 9) || | ||||
| 				!intEqualsAny(ops.V, 0) && !intEqualsAny(ops.F%10, 4, 6, 9) { | ||||
| 				return One | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"lv", "prg"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(Zero, One, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19 | ||||
| 			if ops.NModEqualsAny(10, 0) || | ||||
| 				ops.NModInRange(100, 11, 19) || | ||||
| 				intEqualsAny(ops.V, 2) && intInRange(ops.F%100, 11, 19) { | ||||
| 				return Zero | ||||
| 			} | ||||
| 			// n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1 | ||||
| 			if ops.NModEqualsAny(10, 1) && !ops.NModEqualsAny(100, 11) || | ||||
| 				intEqualsAny(ops.V, 2) && intEqualsAny(ops.F%10, 1) && !intEqualsAny(ops.F%100, 11) || | ||||
| 				!intEqualsAny(ops.V, 2) && intEqualsAny(ops.F%10, 1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"lag"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(Zero, One, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// n = 0 | ||||
| 			if ops.NEqualsAny(0) { | ||||
| 				return Zero | ||||
| 			} | ||||
| 			// i = 0,1 and n != 0 | ||||
| 			if intEqualsAny(ops.I, 0, 1) && !ops.NEqualsAny(0) { | ||||
| 				return One | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"ksh"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(Zero, One, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// n = 0 | ||||
| 			if ops.NEqualsAny(0) { | ||||
| 				return Zero | ||||
| 			} | ||||
| 			// n = 1 | ||||
| 			if ops.NEqualsAny(1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"iu", "kw", "naq", "se", "sma", "smi", "smj", "smn", "sms"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Two, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// n = 1 | ||||
| 			if ops.NEqualsAny(1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// n = 2 | ||||
| 			if ops.NEqualsAny(2) { | ||||
| 				return Two | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"shi"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Few, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// i = 0 or n = 1 | ||||
| 			if intEqualsAny(ops.I, 0) || | ||||
| 				ops.NEqualsAny(1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// n = 2..10 | ||||
| 			if ops.NInRange(2, 10) { | ||||
| 				return Few | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"mo", "ro"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Few, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// i = 1 and v = 0 | ||||
| 			if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// v != 0 or n = 0 or n != 1 and n % 100 = 1..19 | ||||
| 			if !intEqualsAny(ops.V, 0) || | ||||
| 				ops.NEqualsAny(0) || | ||||
| 				!ops.NEqualsAny(1) && ops.NModInRange(100, 1, 19) { | ||||
| 				return Few | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"bs", "hr", "sh", "sr"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Few, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11 | ||||
| 			if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) || | ||||
| 				intEqualsAny(ops.F%10, 1) && !intEqualsAny(ops.F%100, 11) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14 | ||||
| 			if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) || | ||||
| 				intInRange(ops.F%10, 2, 4) && !intInRange(ops.F%100, 12, 14) { | ||||
| 				return Few | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"gd"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Two, Few, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// n = 1,11 | ||||
| 			if ops.NEqualsAny(1, 11) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// n = 2,12 | ||||
| 			if ops.NEqualsAny(2, 12) { | ||||
| 				return Two | ||||
| 			} | ||||
| 			// n = 3..10,13..19 | ||||
| 			if ops.NInRange(3, 10) || ops.NInRange(13, 19) { | ||||
| 				return Few | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"sl"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Two, Few, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// v = 0 and i % 100 = 1 | ||||
| 			if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// v = 0 and i % 100 = 2 | ||||
| 			if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 2) { | ||||
| 				return Two | ||||
| 			} | ||||
| 			// v = 0 and i % 100 = 3..4 or v != 0 | ||||
| 			if intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 3, 4) || | ||||
| 				!intEqualsAny(ops.V, 0) { | ||||
| 				return Few | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"dsb", "hsb"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Two, Few, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// v = 0 and i % 100 = 1 or f % 100 = 1 | ||||
| 			if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 1) || | ||||
| 				intEqualsAny(ops.F%100, 1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// v = 0 and i % 100 = 2 or f % 100 = 2 | ||||
| 			if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 2) || | ||||
| 				intEqualsAny(ops.F%100, 2) { | ||||
| 				return Two | ||||
| 			} | ||||
| 			// v = 0 and i % 100 = 3..4 or f % 100 = 3..4 | ||||
| 			if intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 3, 4) || | ||||
| 				intInRange(ops.F%100, 3, 4) { | ||||
| 				return Few | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"he", "iw"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Two, Many, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// i = 1 and v = 0 | ||||
| 			if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// i = 2 and v = 0 | ||||
| 			if intEqualsAny(ops.I, 2) && intEqualsAny(ops.V, 0) { | ||||
| 				return Two | ||||
| 			} | ||||
| 			// v = 0 and n != 0..10 and n % 10 = 0 | ||||
| 			if intEqualsAny(ops.V, 0) && !ops.NInRange(0, 10) && ops.NModEqualsAny(10, 0) { | ||||
| 				return Many | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"cs", "sk"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Few, Many, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// i = 1 and v = 0 | ||||
| 			if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// i = 2..4 and v = 0 | ||||
| 			if intInRange(ops.I, 2, 4) && intEqualsAny(ops.V, 0) { | ||||
| 				return Few | ||||
| 			} | ||||
| 			// v != 0 | ||||
| 			if !intEqualsAny(ops.V, 0) { | ||||
| 				return Many | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"pl"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Few, Many, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// i = 1 and v = 0 | ||||
| 			if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// v = 0 and i % 10 = 2..4 and i % 100 != 12..14 | ||||
| 			if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) { | ||||
| 				return Few | ||||
| 			} | ||||
| 			// v = 0 and i != 1 and i % 10 = 0..1 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 12..14 | ||||
| 			if intEqualsAny(ops.V, 0) && !intEqualsAny(ops.I, 1) && intInRange(ops.I%10, 0, 1) || | ||||
| 				intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 5, 9) || | ||||
| 				intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 12, 14) { | ||||
| 				return Many | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"be"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Few, Many, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// n % 10 = 1 and n % 100 != 11 | ||||
| 			if ops.NModEqualsAny(10, 1) && !ops.NModEqualsAny(100, 11) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// n % 10 = 2..4 and n % 100 != 12..14 | ||||
| 			if ops.NModInRange(10, 2, 4) && !ops.NModInRange(100, 12, 14) { | ||||
| 				return Few | ||||
| 			} | ||||
| 			// n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14 | ||||
| 			if ops.NModEqualsAny(10, 0) || | ||||
| 				ops.NModInRange(10, 5, 9) || | ||||
| 				ops.NModInRange(100, 11, 14) { | ||||
| 				return Many | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"lt"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Few, Many, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// n % 10 = 1 and n % 100 != 11..19 | ||||
| 			if ops.NModEqualsAny(10, 1) && !ops.NModInRange(100, 11, 19) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// n % 10 = 2..9 and n % 100 != 11..19 | ||||
| 			if ops.NModInRange(10, 2, 9) && !ops.NModInRange(100, 11, 19) { | ||||
| 				return Few | ||||
| 			} | ||||
| 			// f != 0 | ||||
| 			if !intEqualsAny(ops.F, 0) { | ||||
| 				return Many | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"mt"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Few, Many, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// n = 1 | ||||
| 			if ops.NEqualsAny(1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// n = 0 or n % 100 = 2..10 | ||||
| 			if ops.NEqualsAny(0) || | ||||
| 				ops.NModInRange(100, 2, 10) { | ||||
| 				return Few | ||||
| 			} | ||||
| 			// n % 100 = 11..19 | ||||
| 			if ops.NModInRange(100, 11, 19) { | ||||
| 				return Many | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"ru", "uk"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Few, Many, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// v = 0 and i % 10 = 1 and i % 100 != 11 | ||||
| 			if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// v = 0 and i % 10 = 2..4 and i % 100 != 12..14 | ||||
| 			if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) { | ||||
| 				return Few | ||||
| 			} | ||||
| 			// v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14 | ||||
| 			if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 0) || | ||||
| 				intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 5, 9) || | ||||
| 				intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 11, 14) { | ||||
| 				return Many | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"br"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Two, Few, Many, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// n % 10 = 1 and n % 100 != 11,71,91 | ||||
| 			if ops.NModEqualsAny(10, 1) && !ops.NModEqualsAny(100, 11, 71, 91) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// n % 10 = 2 and n % 100 != 12,72,92 | ||||
| 			if ops.NModEqualsAny(10, 2) && !ops.NModEqualsAny(100, 12, 72, 92) { | ||||
| 				return Two | ||||
| 			} | ||||
| 			// n % 10 = 3..4,9 and n % 100 != 10..19,70..79,90..99 | ||||
| 			if (ops.NModInRange(10, 3, 4) || ops.NModEqualsAny(10, 9)) && !(ops.NModInRange(100, 10, 19) || ops.NModInRange(100, 70, 79) || ops.NModInRange(100, 90, 99)) { | ||||
| 				return Few | ||||
| 			} | ||||
| 			// n != 0 and n % 1000000 = 0 | ||||
| 			if !ops.NEqualsAny(0) && ops.NModEqualsAny(1000000, 0) { | ||||
| 				return Many | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"ga"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Two, Few, Many, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// n = 1 | ||||
| 			if ops.NEqualsAny(1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// n = 2 | ||||
| 			if ops.NEqualsAny(2) { | ||||
| 				return Two | ||||
| 			} | ||||
| 			// n = 3..6 | ||||
| 			if ops.NInRange(3, 6) { | ||||
| 				return Few | ||||
| 			} | ||||
| 			// n = 7..10 | ||||
| 			if ops.NInRange(7, 10) { | ||||
| 				return Many | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"gv"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(One, Two, Few, Many, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// v = 0 and i % 10 = 1 | ||||
| 			if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// v = 0 and i % 10 = 2 | ||||
| 			if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 2) { | ||||
| 				return Two | ||||
| 			} | ||||
| 			// v = 0 and i % 100 = 0,20,40,60,80 | ||||
| 			if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 0, 20, 40, 60, 80) { | ||||
| 				return Few | ||||
| 			} | ||||
| 			// v != 0 | ||||
| 			if !intEqualsAny(ops.V, 0) { | ||||
| 				return Many | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"ar", "ars"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(Zero, One, Two, Few, Many, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// n = 0 | ||||
| 			if ops.NEqualsAny(0) { | ||||
| 				return Zero | ||||
| 			} | ||||
| 			// n = 1 | ||||
| 			if ops.NEqualsAny(1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// n = 2 | ||||
| 			if ops.NEqualsAny(2) { | ||||
| 				return Two | ||||
| 			} | ||||
| 			// n % 100 = 3..10 | ||||
| 			if ops.NModInRange(100, 3, 10) { | ||||
| 				return Few | ||||
| 			} | ||||
| 			// n % 100 = 11..99 | ||||
| 			if ops.NModInRange(100, 11, 99) { | ||||
| 				return Many | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
| 	addPluralRules(rules, []string{"cy"}, &Rule{ | ||||
| 		PluralForms: newPluralFormSet(Zero, One, Two, Few, Many, Other), | ||||
| 		PluralFormFunc: func(ops *Operands) Form { | ||||
| 			// n = 0 | ||||
| 			if ops.NEqualsAny(0) { | ||||
| 				return Zero | ||||
| 			} | ||||
| 			// n = 1 | ||||
| 			if ops.NEqualsAny(1) { | ||||
| 				return One | ||||
| 			} | ||||
| 			// n = 2 | ||||
| 			if ops.NEqualsAny(2) { | ||||
| 				return Two | ||||
| 			} | ||||
| 			// n = 3 | ||||
| 			if ops.NEqualsAny(3) { | ||||
| 				return Few | ||||
| 			} | ||||
| 			// n = 6 | ||||
| 			if ops.NEqualsAny(6) { | ||||
| 				return Many | ||||
| 			} | ||||
| 			return Other | ||||
| 		}, | ||||
| 	}) | ||||
|  | ||||
| 	return rules | ||||
| } | ||||
							
								
								
									
										24
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/rules.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/rules.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| package plural | ||||
|  | ||||
| import "golang.org/x/text/language" | ||||
|  | ||||
| // Rules is a set of plural rules by language tag. | ||||
| type Rules map[language.Tag]*Rule | ||||
|  | ||||
| // Rule returns the closest matching plural rule for the language tag | ||||
| // or nil if no rule could be found. | ||||
| func (r Rules) Rule(tag language.Tag) *Rule { | ||||
| 	t := tag | ||||
| 	for { | ||||
| 		if rule := r[t]; rule != nil { | ||||
| 			return rule | ||||
| 		} | ||||
| 		t = t.Parent() | ||||
| 		if t.IsRoot() { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	base, _ := tag.Base() | ||||
| 	baseTag, _ := language.Parse(base.String()) | ||||
| 	return r[baseTag] | ||||
| } | ||||
							
								
								
									
										26
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/template.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/nicksnyder/go-i18n/v2/internal/template.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| package internal | ||||
|  | ||||
| import ( | ||||
| 	"strings" | ||||
| 	gotemplate "text/template" | ||||
| ) | ||||
|  | ||||
| // Template stores the template for a string. | ||||
| type Template struct { | ||||
| 	Src      string | ||||
| 	Template *gotemplate.Template | ||||
| 	ParseErr *error | ||||
| } | ||||
|  | ||||
| func (t *Template) parse(leftDelim, rightDelim string, funcs gotemplate.FuncMap) error { | ||||
| 	if t.ParseErr == nil { | ||||
| 		if strings.Contains(t.Src, leftDelim) { | ||||
| 			gt, err := gotemplate.New("").Funcs(funcs).Delims(leftDelim, rightDelim).Parse(t.Src) | ||||
| 			t.Template = gt | ||||
| 			t.ParseErr = &err | ||||
| 		} else { | ||||
| 			t.ParseErr = new(error) | ||||
| 		} | ||||
| 	} | ||||
| 	return *t.ParseErr | ||||
| } | ||||
							
								
								
									
										100
									
								
								vendor/golang.org/x/text/internal/tag/tag.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								vendor/golang.org/x/text/internal/tag/tag.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| // Copyright 2015 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. | ||||
|  | ||||
| // Package tag contains functionality handling tags and related data. | ||||
| package tag // import "golang.org/x/text/internal/tag" | ||||
|  | ||||
| import "sort" | ||||
|  | ||||
| // An Index converts tags to a compact numeric value. | ||||
| // | ||||
| // All elements are of size 4. Tags may be up to 4 bytes long. Excess bytes can | ||||
| // be used to store additional information about the tag. | ||||
| type Index string | ||||
|  | ||||
| // Elem returns the element data at the given index. | ||||
| func (s Index) Elem(x int) string { | ||||
| 	return string(s[x*4 : x*4+4]) | ||||
| } | ||||
|  | ||||
| // Index reports the index of the given key or -1 if it could not be found. | ||||
| // Only the first len(key) bytes from the start of the 4-byte entries will be | ||||
| // considered for the search and the first match in Index will be returned. | ||||
| func (s Index) Index(key []byte) int { | ||||
| 	n := len(key) | ||||
| 	// search the index of the first entry with an equal or higher value than | ||||
| 	// key in s. | ||||
| 	index := sort.Search(len(s)/4, func(i int) bool { | ||||
| 		return cmp(s[i*4:i*4+n], key) != -1 | ||||
| 	}) | ||||
| 	i := index * 4 | ||||
| 	if cmp(s[i:i+len(key)], key) != 0 { | ||||
| 		return -1 | ||||
| 	} | ||||
| 	return index | ||||
| } | ||||
|  | ||||
| // Next finds the next occurrence of key after index x, which must have been | ||||
| // obtained from a call to Index using the same key. It returns x+1 or -1. | ||||
| func (s Index) Next(key []byte, x int) int { | ||||
| 	if x++; x*4 < len(s) && cmp(s[x*4:x*4+len(key)], key) == 0 { | ||||
| 		return x | ||||
| 	} | ||||
| 	return -1 | ||||
| } | ||||
|  | ||||
| // cmp returns an integer comparing a and b lexicographically. | ||||
| func cmp(a Index, b []byte) int { | ||||
| 	n := len(a) | ||||
| 	if len(b) < n { | ||||
| 		n = len(b) | ||||
| 	} | ||||
| 	for i, c := range b[:n] { | ||||
| 		switch { | ||||
| 		case a[i] > c: | ||||
| 			return 1 | ||||
| 		case a[i] < c: | ||||
| 			return -1 | ||||
| 		} | ||||
| 	} | ||||
| 	switch { | ||||
| 	case len(a) < len(b): | ||||
| 		return -1 | ||||
| 	case len(a) > len(b): | ||||
| 		return 1 | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| // Compare returns an integer comparing a and b lexicographically. | ||||
| func Compare(a string, b []byte) int { | ||||
| 	return cmp(Index(a), b) | ||||
| } | ||||
|  | ||||
| // FixCase reformats b to the same pattern of cases as form. | ||||
| // If returns false if string b is malformed. | ||||
| func FixCase(form string, b []byte) bool { | ||||
| 	if len(form) != len(b) { | ||||
| 		return false | ||||
| 	} | ||||
| 	for i, c := range b { | ||||
| 		if form[i] <= 'Z' { | ||||
| 			if c >= 'a' { | ||||
| 				c -= 'z' - 'Z' | ||||
| 			} | ||||
| 			if c < 'A' || 'Z' < c { | ||||
| 				return false | ||||
| 			} | ||||
| 		} else { | ||||
| 			if c <= 'Z' { | ||||
| 				c += 'z' - 'Z' | ||||
| 			} | ||||
| 			if c < 'a' || 'z' < c { | ||||
| 				return false | ||||
| 			} | ||||
| 		} | ||||
| 		b[i] = c | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
							
								
								
									
										16
									
								
								vendor/golang.org/x/text/language/common.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								vendor/golang.org/x/text/language/common.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. | ||||
|  | ||||
| package language | ||||
|  | ||||
| // This file contains code common to the maketables.go and the package code. | ||||
|  | ||||
| // langAliasType is the type of an alias in langAliasMap. | ||||
| type langAliasType int8 | ||||
|  | ||||
| const ( | ||||
| 	langDeprecated langAliasType = iota | ||||
| 	langMacro | ||||
| 	langLegacy | ||||
|  | ||||
| 	langAliasTypeUnknown langAliasType = -1 | ||||
| ) | ||||
							
								
								
									
										197
									
								
								vendor/golang.org/x/text/language/coverage.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								vendor/golang.org/x/text/language/coverage.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,197 @@ | ||||
| // Copyright 2014 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. | ||||
|  | ||||
| package language | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"sort" | ||||
| ) | ||||
|  | ||||
| // The Coverage interface is used to define the level of coverage of an | ||||
| // internationalization service. Note that not all types are supported by all | ||||
| // services. As lists may be generated on the fly, it is recommended that users | ||||
| // of a Coverage cache the results. | ||||
| type Coverage interface { | ||||
| 	// Tags returns the list of supported tags. | ||||
| 	Tags() []Tag | ||||
|  | ||||
| 	// BaseLanguages returns the list of supported base languages. | ||||
| 	BaseLanguages() []Base | ||||
|  | ||||
| 	// Scripts returns the list of supported scripts. | ||||
| 	Scripts() []Script | ||||
|  | ||||
| 	// Regions returns the list of supported regions. | ||||
| 	Regions() []Region | ||||
| } | ||||
|  | ||||
| var ( | ||||
| 	// Supported defines a Coverage that lists all supported subtags. Tags | ||||
| 	// always returns nil. | ||||
| 	Supported Coverage = allSubtags{} | ||||
| ) | ||||
|  | ||||
| // TODO: | ||||
| // - Support Variants, numbering systems. | ||||
| // - CLDR coverage levels. | ||||
| // - Set of common tags defined in this package. | ||||
|  | ||||
| type allSubtags struct{} | ||||
|  | ||||
| // Regions returns the list of supported regions. As all regions are in a | ||||
| // consecutive range, it simply returns a slice of numbers in increasing order. | ||||
| // The "undefined" region is not returned. | ||||
| func (s allSubtags) Regions() []Region { | ||||
| 	reg := make([]Region, numRegions) | ||||
| 	for i := range reg { | ||||
| 		reg[i] = Region{regionID(i + 1)} | ||||
| 	} | ||||
| 	return reg | ||||
| } | ||||
|  | ||||
| // Scripts returns the list of supported scripts. As all scripts are in a | ||||
| // consecutive range, it simply returns a slice of numbers in increasing order. | ||||
| // The "undefined" script is not returned. | ||||
| func (s allSubtags) Scripts() []Script { | ||||
| 	scr := make([]Script, numScripts) | ||||
| 	for i := range scr { | ||||
| 		scr[i] = Script{scriptID(i + 1)} | ||||
| 	} | ||||
| 	return scr | ||||
| } | ||||
|  | ||||
| // BaseLanguages returns the list of all supported base languages. It generates | ||||
| // the list by traversing the internal structures. | ||||
| func (s allSubtags) BaseLanguages() []Base { | ||||
| 	base := make([]Base, 0, numLanguages) | ||||
| 	for i := 0; i < langNoIndexOffset; i++ { | ||||
| 		// We included "und" already for the value 0. | ||||
| 		if i != nonCanonicalUnd { | ||||
| 			base = append(base, Base{langID(i)}) | ||||
| 		} | ||||
| 	} | ||||
| 	i := langNoIndexOffset | ||||
| 	for _, v := range langNoIndex { | ||||
| 		for k := 0; k < 8; k++ { | ||||
| 			if v&1 == 1 { | ||||
| 				base = append(base, Base{langID(i)}) | ||||
| 			} | ||||
| 			v >>= 1 | ||||
| 			i++ | ||||
| 		} | ||||
| 	} | ||||
| 	return base | ||||
| } | ||||
|  | ||||
| // Tags always returns nil. | ||||
| func (s allSubtags) Tags() []Tag { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // coverage is used used by NewCoverage which is used as a convenient way for | ||||
| // creating Coverage implementations for partially defined data. Very often a | ||||
| // package will only need to define a subset of slices. coverage provides a | ||||
| // convenient way to do this. Moreover, packages using NewCoverage, instead of | ||||
| // their own implementation, will not break if later new slice types are added. | ||||
| type coverage struct { | ||||
| 	tags    func() []Tag | ||||
| 	bases   func() []Base | ||||
| 	scripts func() []Script | ||||
| 	regions func() []Region | ||||
| } | ||||
|  | ||||
| func (s *coverage) Tags() []Tag { | ||||
| 	if s.tags == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return s.tags() | ||||
| } | ||||
|  | ||||
| // bases implements sort.Interface and is used to sort base languages. | ||||
| type bases []Base | ||||
|  | ||||
| func (b bases) Len() int { | ||||
| 	return len(b) | ||||
| } | ||||
|  | ||||
| func (b bases) Swap(i, j int) { | ||||
| 	b[i], b[j] = b[j], b[i] | ||||
| } | ||||
|  | ||||
| func (b bases) Less(i, j int) bool { | ||||
| 	return b[i].langID < b[j].langID | ||||
| } | ||||
|  | ||||
| // BaseLanguages returns the result from calling s.bases if it is specified or | ||||
| // otherwise derives the set of supported base languages from tags. | ||||
| func (s *coverage) BaseLanguages() []Base { | ||||
| 	if s.bases == nil { | ||||
| 		tags := s.Tags() | ||||
| 		if len(tags) == 0 { | ||||
| 			return nil | ||||
| 		} | ||||
| 		a := make([]Base, len(tags)) | ||||
| 		for i, t := range tags { | ||||
| 			a[i] = Base{langID(t.lang)} | ||||
| 		} | ||||
| 		sort.Sort(bases(a)) | ||||
| 		k := 0 | ||||
| 		for i := 1; i < len(a); i++ { | ||||
| 			if a[k] != a[i] { | ||||
| 				k++ | ||||
| 				a[k] = a[i] | ||||
| 			} | ||||
| 		} | ||||
| 		return a[:k+1] | ||||
| 	} | ||||
| 	return s.bases() | ||||
| } | ||||
|  | ||||
| func (s *coverage) Scripts() []Script { | ||||
| 	if s.scripts == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return s.scripts() | ||||
| } | ||||
|  | ||||
| func (s *coverage) Regions() []Region { | ||||
| 	if s.regions == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return s.regions() | ||||
| } | ||||
|  | ||||
| // NewCoverage returns a Coverage for the given lists. It is typically used by | ||||
| // packages providing internationalization services to define their level of | ||||
| // coverage. A list may be of type []T or func() []T, where T is either Tag, | ||||
| // Base, Script or Region. The returned Coverage derives the value for Bases | ||||
| // from Tags if no func or slice for []Base is specified. For other unspecified | ||||
| // types the returned Coverage will return nil for the respective methods. | ||||
| func NewCoverage(list ...interface{}) Coverage { | ||||
| 	s := &coverage{} | ||||
| 	for _, x := range list { | ||||
| 		switch v := x.(type) { | ||||
| 		case func() []Base: | ||||
| 			s.bases = v | ||||
| 		case func() []Script: | ||||
| 			s.scripts = v | ||||
| 		case func() []Region: | ||||
| 			s.regions = v | ||||
| 		case func() []Tag: | ||||
| 			s.tags = v | ||||
| 		case []Base: | ||||
| 			s.bases = func() []Base { return v } | ||||
| 		case []Script: | ||||
| 			s.scripts = func() []Script { return v } | ||||
| 		case []Region: | ||||
| 			s.regions = func() []Region { return v } | ||||
| 		case []Tag: | ||||
| 			s.tags = func() []Tag { return v } | ||||
| 		default: | ||||
| 			panic(fmt.Sprintf("language: unsupported set type %T", v)) | ||||
| 		} | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
							
								
								
									
										102
									
								
								vendor/golang.org/x/text/language/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								vendor/golang.org/x/text/language/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | ||||
| // Copyright 2017 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. | ||||
|  | ||||
| // Package language implements BCP 47 language tags and related functionality. | ||||
| // | ||||
| // The most important function of package language is to match a list of | ||||
| // user-preferred languages to a list of supported languages. | ||||
| // It alleviates the developer of dealing with the complexity of this process | ||||
| // and provides the user with the best experience | ||||
| // (see https://blog.golang.org/matchlang). | ||||
| // | ||||
| // | ||||
| // Matching preferred against supported languages | ||||
| // | ||||
| // A Matcher for an application that supports English, Australian English, | ||||
| // Danish, and standard Mandarin can be created as follows: | ||||
| // | ||||
| //    var matcher = language.NewMatcher([]language.Tag{ | ||||
| //        language.English,   // The first language is used as fallback. | ||||
| //        language.MustParse("en-AU"), | ||||
| //        language.Danish, | ||||
| //        language.Chinese, | ||||
| //    }) | ||||
| // | ||||
| // This list of supported languages is typically implied by the languages for | ||||
| // which there exists translations of the user interface. | ||||
| // | ||||
| // User-preferred languages usually come as a comma-separated list of BCP 47 | ||||
| // language tags. | ||||
| // The MatchString finds best matches for such strings: | ||||
| // | ||||
| //    handler(w http.ResponseWriter, r *http.Request) { | ||||
| //        lang, _ := r.Cookie("lang") | ||||
| //        accept := r.Header.Get("Accept-Language") | ||||
| //        tag, _ := language.MatchStrings(matcher, lang.String(), accept) | ||||
| // | ||||
| //        // tag should now be used for the initialization of any | ||||
| //        // locale-specific service. | ||||
| //    } | ||||
| // | ||||
| // The Matcher's Match method can be used to match Tags directly. | ||||
| // | ||||
| // Matchers are aware of the intricacies of equivalence between languages, such | ||||
| // as deprecated subtags, legacy tags, macro languages, mutual | ||||
| // intelligibility between scripts and languages, and transparently passing | ||||
| // BCP 47 user configuration. | ||||
| // For instance, it will know that a reader of Bokmål Danish can read Norwegian | ||||
| // and will know that Cantonese ("yue") is a good match for "zh-HK". | ||||
| // | ||||
| // | ||||
| // Using match results | ||||
| // | ||||
| // To guarantee a consistent user experience to the user it is important to | ||||
| // use the same language tag for the selection of any locale-specific services. | ||||
| // For example, it is utterly confusing to substitute spelled-out numbers | ||||
| // or dates in one language in text of another language. | ||||
| // More subtly confusing is using the wrong sorting order or casing | ||||
| // algorithm for a certain language. | ||||
| // | ||||
| //    All the packages in x/text that provide locale-specific services | ||||
| //    (e.g. collate, cases) should be initialized with the tag that was | ||||
| //    obtained at the start of an interaction with the user. | ||||
| // | ||||
| // Note that Tag that is returned by Match and MatchString may differ from any | ||||
| // of the supported languages, as it may contain carried over settings from | ||||
| // the user tags. | ||||
| // This may be inconvenient when your application has some additional | ||||
| // locale-specific data for your supported languages. | ||||
| // Match and MatchString both return the index of the matched supported tag | ||||
| // to simplify associating such data with the matched tag. | ||||
| // | ||||
| // | ||||
| // Canonicalization | ||||
| // | ||||
| // If one uses the Matcher to compare languages one does not need to | ||||
| // worry about canonicalization. | ||||
| // | ||||
| // The meaning of a Tag varies per application. The language package | ||||
| // therefore delays canonicalization and preserves information as much | ||||
| // as possible. The Matcher, however, will always take into account that | ||||
| // two different tags may represent the same language. | ||||
| // | ||||
| // By default, only legacy and deprecated tags are converted into their | ||||
| // canonical equivalent. All other information is preserved. This approach makes | ||||
| // the confidence scores more accurate and allows matchers to distinguish | ||||
| // between variants that are otherwise lost. | ||||
| // | ||||
| // As a consequence, two tags that should be treated as identical according to | ||||
| // BCP 47 or CLDR, like "en-Latn" and "en", will be represented differently. The | ||||
| // Matcher handles such distinctions, though, and is aware of the | ||||
| // equivalence relations. The CanonType type can be used to alter the | ||||
| // canonicalization form. | ||||
| // | ||||
| // References | ||||
| // | ||||
| // BCP 47 - Tags for Identifying Languages http://tools.ietf.org/html/bcp47 | ||||
| // | ||||
| package language // import "golang.org/x/text/language" | ||||
|  | ||||
| // TODO: explanation on how to match languages for your own locale-specific | ||||
| // service. | ||||
							
								
								
									
										1712
									
								
								vendor/golang.org/x/text/language/gen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1712
									
								
								vendor/golang.org/x/text/language/gen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										20
									
								
								vendor/golang.org/x/text/language/gen_common.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/golang.org/x/text/language/gen_common.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| // Copyright 2014 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. | ||||
|  | ||||
| // +build ignore | ||||
|  | ||||
| package main | ||||
|  | ||||
| // This file contains code common to the maketables.go and the package code. | ||||
|  | ||||
| // langAliasType is the type of an alias in langAliasMap. | ||||
| type langAliasType int8 | ||||
|  | ||||
| const ( | ||||
| 	langDeprecated langAliasType = iota | ||||
| 	langMacro | ||||
| 	langLegacy | ||||
|  | ||||
| 	langAliasTypeUnknown langAliasType = -1 | ||||
| ) | ||||
							
								
								
									
										162
									
								
								vendor/golang.org/x/text/language/gen_index.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								vendor/golang.org/x/text/language/gen_index.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | ||||
| // Copyright 2015 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. | ||||
|  | ||||
| // +build ignore | ||||
|  | ||||
| package main | ||||
|  | ||||
| // This file generates derivative tables based on the language package itself. | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"reflect" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
|  | ||||
| 	"golang.org/x/text/internal/gen" | ||||
| 	"golang.org/x/text/language" | ||||
| 	"golang.org/x/text/unicode/cldr" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	test = flag.Bool("test", false, | ||||
| 		"test existing tables; can be used to compare web data with package data.") | ||||
|  | ||||
| 	draft = flag.String("draft", | ||||
| 		"contributed", | ||||
| 		`Minimal draft requirements (approved, contributed, provisional, unconfirmed).`) | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	gen.Init() | ||||
|  | ||||
| 	// Read the CLDR zip file. | ||||
| 	r := gen.OpenCLDRCoreZip() | ||||
| 	defer r.Close() | ||||
|  | ||||
| 	d := &cldr.Decoder{} | ||||
| 	data, err := d.DecodeZip(r) | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("DecodeZip: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	w := gen.NewCodeWriter() | ||||
| 	defer func() { | ||||
| 		buf := &bytes.Buffer{} | ||||
|  | ||||
| 		if _, err = w.WriteGo(buf, "language", ""); err != nil { | ||||
| 			log.Fatalf("Error formatting file index.go: %v", err) | ||||
| 		} | ||||
|  | ||||
| 		// Since we're generating a table for our own package we need to rewrite | ||||
| 		// doing the equivalent of go fmt -r 'language.b -> b'. Using | ||||
| 		// bytes.Replace will do. | ||||
| 		out := bytes.Replace(buf.Bytes(), []byte("language."), nil, -1) | ||||
| 		if err := ioutil.WriteFile("index.go", out, 0600); err != nil { | ||||
| 			log.Fatalf("Could not create file index.go: %v", err) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	m := map[language.Tag]bool{} | ||||
| 	for _, lang := range data.Locales() { | ||||
| 		// We include all locales unconditionally to be consistent with en_US. | ||||
| 		// We want en_US, even though it has no data associated with it. | ||||
|  | ||||
| 		// TODO: put any of the languages for which no data exists at the end | ||||
| 		// of the index. This allows all components based on ICU to use that | ||||
| 		// as the cutoff point. | ||||
| 		// if x := data.RawLDML(lang); false || | ||||
| 		// 	x.LocaleDisplayNames != nil || | ||||
| 		// 	x.Characters != nil || | ||||
| 		// 	x.Delimiters != nil || | ||||
| 		// 	x.Measurement != nil || | ||||
| 		// 	x.Dates != nil || | ||||
| 		// 	x.Numbers != nil || | ||||
| 		// 	x.Units != nil || | ||||
| 		// 	x.ListPatterns != nil || | ||||
| 		// 	x.Collations != nil || | ||||
| 		// 	x.Segmentations != nil || | ||||
| 		// 	x.Rbnf != nil || | ||||
| 		// 	x.Annotations != nil || | ||||
| 		// 	x.Metadata != nil { | ||||
|  | ||||
| 		// TODO: support POSIX natively, albeit non-standard. | ||||
| 		tag := language.Make(strings.Replace(lang, "_POSIX", "-u-va-posix", 1)) | ||||
| 		m[tag] = true | ||||
| 		// } | ||||
| 	} | ||||
| 	// Include locales for plural rules, which uses a different structure. | ||||
| 	for _, plurals := range data.Supplemental().Plurals { | ||||
| 		for _, rules := range plurals.PluralRules { | ||||
| 			for _, lang := range strings.Split(rules.Locales, " ") { | ||||
| 				m[language.Make(lang)] = true | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var core, special []language.Tag | ||||
|  | ||||
| 	for t := range m { | ||||
| 		if x := t.Extensions(); len(x) != 0 && fmt.Sprint(x) != "[u-va-posix]" { | ||||
| 			log.Fatalf("Unexpected extension %v in %v", x, t) | ||||
| 		} | ||||
| 		if len(t.Variants()) == 0 && len(t.Extensions()) == 0 { | ||||
| 			core = append(core, t) | ||||
| 		} else { | ||||
| 			special = append(special, t) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	w.WriteComment(` | ||||
| 	NumCompactTags is the number of common tags. The maximum tag is | ||||
| 	NumCompactTags-1.`) | ||||
| 	w.WriteConst("NumCompactTags", len(core)+len(special)) | ||||
|  | ||||
| 	sort.Sort(byAlpha(special)) | ||||
| 	w.WriteVar("specialTags", special) | ||||
|  | ||||
| 	// TODO: order by frequency? | ||||
| 	sort.Sort(byAlpha(core)) | ||||
|  | ||||
| 	// Size computations are just an estimate. | ||||
| 	w.Size += int(reflect.TypeOf(map[uint32]uint16{}).Size()) | ||||
| 	w.Size += len(core) * 6 // size of uint32 and uint16 | ||||
|  | ||||
| 	fmt.Fprintln(w) | ||||
| 	fmt.Fprintln(w, "var coreTags = map[uint32]uint16{") | ||||
| 	fmt.Fprintln(w, "0x0: 0, // und") | ||||
| 	i := len(special) + 1 // Und and special tags already written. | ||||
| 	for _, t := range core { | ||||
| 		if t == language.Und { | ||||
| 			continue | ||||
| 		} | ||||
| 		fmt.Fprint(w.Hash, t, i) | ||||
| 		b, s, r := t.Raw() | ||||
| 		fmt.Fprintf(w, "0x%s%s%s: %d, // %s\n", | ||||
| 			getIndex(b, 3), // 3 is enough as it is guaranteed to be a compact number | ||||
| 			getIndex(s, 2), | ||||
| 			getIndex(r, 3), | ||||
| 			i, t) | ||||
| 		i++ | ||||
| 	} | ||||
| 	fmt.Fprintln(w, "}") | ||||
| } | ||||
|  | ||||
| // getIndex prints the subtag type and extracts its index of size nibble. | ||||
| // If the index is less than n nibbles, the result is prefixed with 0s. | ||||
| func getIndex(x interface{}, n int) string { | ||||
| 	s := fmt.Sprintf("%#v", x) // s is of form Type{typeID: 0x00} | ||||
| 	s = s[strings.Index(s, "0x")+2 : len(s)-1] | ||||
| 	return strings.Repeat("0", n-len(s)) + s | ||||
| } | ||||
|  | ||||
| type byAlpha []language.Tag | ||||
|  | ||||
| func (a byAlpha) Len() int           { return len(a) } | ||||
| func (a byAlpha) Swap(i, j int)      { a[i], a[j] = a[j], a[i] } | ||||
| func (a byAlpha) Less(i, j int) bool { return a[i].String() < a[j].String() } | ||||
							
								
								
									
										38
									
								
								vendor/golang.org/x/text/language/go1_1.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								vendor/golang.org/x/text/language/go1_1.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| // Copyright 2013 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. | ||||
|  | ||||
| // +build !go1.2 | ||||
|  | ||||
| package language | ||||
|  | ||||
| import "sort" | ||||
|  | ||||
| func sortStable(s sort.Interface) { | ||||
| 	ss := stableSort{ | ||||
| 		s:   s, | ||||
| 		pos: make([]int, s.Len()), | ||||
| 	} | ||||
| 	for i := range ss.pos { | ||||
| 		ss.pos[i] = i | ||||
| 	} | ||||
| 	sort.Sort(&ss) | ||||
| } | ||||
|  | ||||
| type stableSort struct { | ||||
| 	s   sort.Interface | ||||
| 	pos []int | ||||
| } | ||||
|  | ||||
| func (s *stableSort) Len() int { | ||||
| 	return len(s.pos) | ||||
| } | ||||
|  | ||||
| func (s *stableSort) Less(i, j int) bool { | ||||
| 	return s.s.Less(i, j) || !s.s.Less(j, i) && s.pos[i] < s.pos[j] | ||||
| } | ||||
|  | ||||
| func (s *stableSort) Swap(i, j int) { | ||||
| 	s.s.Swap(i, j) | ||||
| 	s.pos[i], s.pos[j] = s.pos[j], s.pos[i] | ||||
| } | ||||
							
								
								
									
										11
									
								
								vendor/golang.org/x/text/language/go1_2.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								vendor/golang.org/x/text/language/go1_2.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // Copyright 2013 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. | ||||
|  | ||||
| // +build go1.2 | ||||
|  | ||||
| package language | ||||
|  | ||||
| import "sort" | ||||
|  | ||||
| var sortStable = sort.Stable | ||||
							
								
								
									
										783
									
								
								vendor/golang.org/x/text/language/index.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										783
									
								
								vendor/golang.org/x/text/language/index.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,783 @@ | ||||
| // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. | ||||
|  | ||||
| package language | ||||
|  | ||||
| // NumCompactTags is the number of common tags. The maximum tag is | ||||
| // NumCompactTags-1. | ||||
| const NumCompactTags = 768 | ||||
|  | ||||
| var specialTags = []Tag{ // 2 elements | ||||
| 	0: {lang: 0xd7, region: 0x6e, script: 0x0, pVariant: 0x5, pExt: 0xe, str: "ca-ES-valencia"}, | ||||
| 	1: {lang: 0x139, region: 0x135, script: 0x0, pVariant: 0x5, pExt: 0x5, str: "en-US-u-va-posix"}, | ||||
| } // Size: 72 bytes | ||||
|  | ||||
| var coreTags = map[uint32]uint16{ | ||||
| 	0x0:        0,   // und | ||||
| 	0x01600000: 3,   // af | ||||
| 	0x016000d2: 4,   // af-NA | ||||
| 	0x01600161: 5,   // af-ZA | ||||
| 	0x01c00000: 6,   // agq | ||||
| 	0x01c00052: 7,   // agq-CM | ||||
| 	0x02100000: 8,   // ak | ||||
| 	0x02100080: 9,   // ak-GH | ||||
| 	0x02700000: 10,  // am | ||||
| 	0x0270006f: 11,  // am-ET | ||||
| 	0x03a00000: 12,  // ar | ||||
| 	0x03a00001: 13,  // ar-001 | ||||
| 	0x03a00023: 14,  // ar-AE | ||||
| 	0x03a00039: 15,  // ar-BH | ||||
| 	0x03a00062: 16,  // ar-DJ | ||||
| 	0x03a00067: 17,  // ar-DZ | ||||
| 	0x03a0006b: 18,  // ar-EG | ||||
| 	0x03a0006c: 19,  // ar-EH | ||||
| 	0x03a0006d: 20,  // ar-ER | ||||
| 	0x03a00097: 21,  // ar-IL | ||||
| 	0x03a0009b: 22,  // ar-IQ | ||||
| 	0x03a000a1: 23,  // ar-JO | ||||
| 	0x03a000a8: 24,  // ar-KM | ||||
| 	0x03a000ac: 25,  // ar-KW | ||||
| 	0x03a000b0: 26,  // ar-LB | ||||
| 	0x03a000b9: 27,  // ar-LY | ||||
| 	0x03a000ba: 28,  // ar-MA | ||||
| 	0x03a000c9: 29,  // ar-MR | ||||
| 	0x03a000e1: 30,  // ar-OM | ||||
| 	0x03a000ed: 31,  // ar-PS | ||||
| 	0x03a000f3: 32,  // ar-QA | ||||
| 	0x03a00108: 33,  // ar-SA | ||||
| 	0x03a0010b: 34,  // ar-SD | ||||
| 	0x03a00115: 35,  // ar-SO | ||||
| 	0x03a00117: 36,  // ar-SS | ||||
| 	0x03a0011c: 37,  // ar-SY | ||||
| 	0x03a00120: 38,  // ar-TD | ||||
| 	0x03a00128: 39,  // ar-TN | ||||
| 	0x03a0015e: 40,  // ar-YE | ||||
| 	0x04000000: 41,  // ars | ||||
| 	0x04300000: 42,  // as | ||||
| 	0x04300099: 43,  // as-IN | ||||
| 	0x04400000: 44,  // asa | ||||
| 	0x0440012f: 45,  // asa-TZ | ||||
| 	0x04800000: 46,  // ast | ||||
| 	0x0480006e: 47,  // ast-ES | ||||
| 	0x05800000: 48,  // az | ||||
| 	0x0581f000: 49,  // az-Cyrl | ||||
| 	0x0581f032: 50,  // az-Cyrl-AZ | ||||
| 	0x05857000: 51,  // az-Latn | ||||
| 	0x05857032: 52,  // az-Latn-AZ | ||||
| 	0x05e00000: 53,  // bas | ||||
| 	0x05e00052: 54,  // bas-CM | ||||
| 	0x07100000: 55,  // be | ||||
| 	0x07100047: 56,  // be-BY | ||||
| 	0x07500000: 57,  // bem | ||||
| 	0x07500162: 58,  // bem-ZM | ||||
| 	0x07900000: 59,  // bez | ||||
| 	0x0790012f: 60,  // bez-TZ | ||||
| 	0x07e00000: 61,  // bg | ||||
| 	0x07e00038: 62,  // bg-BG | ||||
| 	0x08200000: 63,  // bh | ||||
| 	0x0a000000: 64,  // bm | ||||
| 	0x0a0000c3: 65,  // bm-ML | ||||
| 	0x0a500000: 66,  // bn | ||||
| 	0x0a500035: 67,  // bn-BD | ||||
| 	0x0a500099: 68,  // bn-IN | ||||
| 	0x0a900000: 69,  // bo | ||||
| 	0x0a900053: 70,  // bo-CN | ||||
| 	0x0a900099: 71,  // bo-IN | ||||
| 	0x0b200000: 72,  // br | ||||
| 	0x0b200078: 73,  // br-FR | ||||
| 	0x0b500000: 74,  // brx | ||||
| 	0x0b500099: 75,  // brx-IN | ||||
| 	0x0b700000: 76,  // bs | ||||
| 	0x0b71f000: 77,  // bs-Cyrl | ||||
| 	0x0b71f033: 78,  // bs-Cyrl-BA | ||||
| 	0x0b757000: 79,  // bs-Latn | ||||
| 	0x0b757033: 80,  // bs-Latn-BA | ||||
| 	0x0d700000: 81,  // ca | ||||
| 	0x0d700022: 82,  // ca-AD | ||||
| 	0x0d70006e: 83,  // ca-ES | ||||
| 	0x0d700078: 84,  // ca-FR | ||||
| 	0x0d70009e: 85,  // ca-IT | ||||
| 	0x0db00000: 86,  // ccp | ||||
| 	0x0db00035: 87,  // ccp-BD | ||||
| 	0x0db00099: 88,  // ccp-IN | ||||
| 	0x0dc00000: 89,  // ce | ||||
| 	0x0dc00106: 90,  // ce-RU | ||||
| 	0x0df00000: 91,  // cgg | ||||
| 	0x0df00131: 92,  // cgg-UG | ||||
| 	0x0e500000: 93,  // chr | ||||
| 	0x0e500135: 94,  // chr-US | ||||
| 	0x0e900000: 95,  // ckb | ||||
| 	0x0e90009b: 96,  // ckb-IQ | ||||
| 	0x0e90009c: 97,  // ckb-IR | ||||
| 	0x0fa00000: 98,  // cs | ||||
| 	0x0fa0005e: 99,  // cs-CZ | ||||
| 	0x0fe00000: 100, // cu | ||||
| 	0x0fe00106: 101, // cu-RU | ||||
| 	0x10000000: 102, // cy | ||||
| 	0x1000007b: 103, // cy-GB | ||||
| 	0x10100000: 104, // da | ||||
| 	0x10100063: 105, // da-DK | ||||
| 	0x10100082: 106, // da-GL | ||||
| 	0x10800000: 107, // dav | ||||
| 	0x108000a4: 108, // dav-KE | ||||
| 	0x10d00000: 109, // de | ||||
| 	0x10d0002e: 110, // de-AT | ||||
| 	0x10d00036: 111, // de-BE | ||||
| 	0x10d0004e: 112, // de-CH | ||||
| 	0x10d00060: 113, // de-DE | ||||
| 	0x10d0009e: 114, // de-IT | ||||
| 	0x10d000b2: 115, // de-LI | ||||
| 	0x10d000b7: 116, // de-LU | ||||
| 	0x11700000: 117, // dje | ||||
| 	0x117000d4: 118, // dje-NE | ||||
| 	0x11f00000: 119, // dsb | ||||
| 	0x11f00060: 120, // dsb-DE | ||||
| 	0x12400000: 121, // dua | ||||
| 	0x12400052: 122, // dua-CM | ||||
| 	0x12800000: 123, // dv | ||||
| 	0x12b00000: 124, // dyo | ||||
| 	0x12b00114: 125, // dyo-SN | ||||
| 	0x12d00000: 126, // dz | ||||
| 	0x12d00043: 127, // dz-BT | ||||
| 	0x12f00000: 128, // ebu | ||||
| 	0x12f000a4: 129, // ebu-KE | ||||
| 	0x13000000: 130, // ee | ||||
| 	0x13000080: 131, // ee-GH | ||||
| 	0x13000122: 132, // ee-TG | ||||
| 	0x13600000: 133, // el | ||||
| 	0x1360005d: 134, // el-CY | ||||
| 	0x13600087: 135, // el-GR | ||||
| 	0x13900000: 136, // en | ||||
| 	0x13900001: 137, // en-001 | ||||
| 	0x1390001a: 138, // en-150 | ||||
| 	0x13900025: 139, // en-AG | ||||
| 	0x13900026: 140, // en-AI | ||||
| 	0x1390002d: 141, // en-AS | ||||
| 	0x1390002e: 142, // en-AT | ||||
| 	0x1390002f: 143, // en-AU | ||||
| 	0x13900034: 144, // en-BB | ||||
| 	0x13900036: 145, // en-BE | ||||
| 	0x1390003a: 146, // en-BI | ||||
| 	0x1390003d: 147, // en-BM | ||||
| 	0x13900042: 148, // en-BS | ||||
| 	0x13900046: 149, // en-BW | ||||
| 	0x13900048: 150, // en-BZ | ||||
| 	0x13900049: 151, // en-CA | ||||
| 	0x1390004a: 152, // en-CC | ||||
| 	0x1390004e: 153, // en-CH | ||||
| 	0x13900050: 154, // en-CK | ||||
| 	0x13900052: 155, // en-CM | ||||
| 	0x1390005c: 156, // en-CX | ||||
| 	0x1390005d: 157, // en-CY | ||||
| 	0x13900060: 158, // en-DE | ||||
| 	0x13900061: 159, // en-DG | ||||
| 	0x13900063: 160, // en-DK | ||||
| 	0x13900064: 161, // en-DM | ||||
| 	0x1390006d: 162, // en-ER | ||||
| 	0x13900072: 163, // en-FI | ||||
| 	0x13900073: 164, // en-FJ | ||||
| 	0x13900074: 165, // en-FK | ||||
| 	0x13900075: 166, // en-FM | ||||
| 	0x1390007b: 167, // en-GB | ||||
| 	0x1390007c: 168, // en-GD | ||||
| 	0x1390007f: 169, // en-GG | ||||
| 	0x13900080: 170, // en-GH | ||||
| 	0x13900081: 171, // en-GI | ||||
| 	0x13900083: 172, // en-GM | ||||
| 	0x1390008a: 173, // en-GU | ||||
| 	0x1390008c: 174, // en-GY | ||||
| 	0x1390008d: 175, // en-HK | ||||
| 	0x13900096: 176, // en-IE | ||||
| 	0x13900097: 177, // en-IL | ||||
| 	0x13900098: 178, // en-IM | ||||
| 	0x13900099: 179, // en-IN | ||||
| 	0x1390009a: 180, // en-IO | ||||
| 	0x1390009f: 181, // en-JE | ||||
| 	0x139000a0: 182, // en-JM | ||||
| 	0x139000a4: 183, // en-KE | ||||
| 	0x139000a7: 184, // en-KI | ||||
| 	0x139000a9: 185, // en-KN | ||||
| 	0x139000ad: 186, // en-KY | ||||
| 	0x139000b1: 187, // en-LC | ||||
| 	0x139000b4: 188, // en-LR | ||||
| 	0x139000b5: 189, // en-LS | ||||
| 	0x139000bf: 190, // en-MG | ||||
| 	0x139000c0: 191, // en-MH | ||||
| 	0x139000c6: 192, // en-MO | ||||
| 	0x139000c7: 193, // en-MP | ||||
| 	0x139000ca: 194, // en-MS | ||||
| 	0x139000cb: 195, // en-MT | ||||
| 	0x139000cc: 196, // en-MU | ||||
| 	0x139000ce: 197, // en-MW | ||||
| 	0x139000d0: 198, // en-MY | ||||
| 	0x139000d2: 199, // en-NA | ||||
| 	0x139000d5: 200, // en-NF | ||||
| 	0x139000d6: 201, // en-NG | ||||
| 	0x139000d9: 202, // en-NL | ||||
| 	0x139000dd: 203, // en-NR | ||||
| 	0x139000df: 204, // en-NU | ||||
| 	0x139000e0: 205, // en-NZ | ||||
| 	0x139000e6: 206, // en-PG | ||||
| 	0x139000e7: 207, // en-PH | ||||
| 	0x139000e8: 208, // en-PK | ||||
| 	0x139000eb: 209, // en-PN | ||||
| 	0x139000ec: 210, // en-PR | ||||
| 	0x139000f0: 211, // en-PW | ||||
| 	0x13900107: 212, // en-RW | ||||
| 	0x13900109: 213, // en-SB | ||||
| 	0x1390010a: 214, // en-SC | ||||
| 	0x1390010b: 215, // en-SD | ||||
| 	0x1390010c: 216, // en-SE | ||||
| 	0x1390010d: 217, // en-SG | ||||
| 	0x1390010e: 218, // en-SH | ||||
| 	0x1390010f: 219, // en-SI | ||||
| 	0x13900112: 220, // en-SL | ||||
| 	0x13900117: 221, // en-SS | ||||
| 	0x1390011b: 222, // en-SX | ||||
| 	0x1390011d: 223, // en-SZ | ||||
| 	0x1390011f: 224, // en-TC | ||||
| 	0x13900125: 225, // en-TK | ||||
| 	0x13900129: 226, // en-TO | ||||
| 	0x1390012c: 227, // en-TT | ||||
| 	0x1390012d: 228, // en-TV | ||||
| 	0x1390012f: 229, // en-TZ | ||||
| 	0x13900131: 230, // en-UG | ||||
| 	0x13900133: 231, // en-UM | ||||
| 	0x13900135: 232, // en-US | ||||
| 	0x13900139: 233, // en-VC | ||||
| 	0x1390013c: 234, // en-VG | ||||
| 	0x1390013d: 235, // en-VI | ||||
| 	0x1390013f: 236, // en-VU | ||||
| 	0x13900142: 237, // en-WS | ||||
| 	0x13900161: 238, // en-ZA | ||||
| 	0x13900162: 239, // en-ZM | ||||
| 	0x13900164: 240, // en-ZW | ||||
| 	0x13c00000: 241, // eo | ||||
| 	0x13c00001: 242, // eo-001 | ||||
| 	0x13e00000: 243, // es | ||||
| 	0x13e0001f: 244, // es-419 | ||||
| 	0x13e0002c: 245, // es-AR | ||||
| 	0x13e0003f: 246, // es-BO | ||||
| 	0x13e00041: 247, // es-BR | ||||
| 	0x13e00048: 248, // es-BZ | ||||
| 	0x13e00051: 249, // es-CL | ||||
| 	0x13e00054: 250, // es-CO | ||||
| 	0x13e00056: 251, // es-CR | ||||
| 	0x13e00059: 252, // es-CU | ||||
| 	0x13e00065: 253, // es-DO | ||||
| 	0x13e00068: 254, // es-EA | ||||
| 	0x13e00069: 255, // es-EC | ||||
| 	0x13e0006e: 256, // es-ES | ||||
| 	0x13e00086: 257, // es-GQ | ||||
| 	0x13e00089: 258, // es-GT | ||||
| 	0x13e0008f: 259, // es-HN | ||||
| 	0x13e00094: 260, // es-IC | ||||
| 	0x13e000cf: 261, // es-MX | ||||
| 	0x13e000d8: 262, // es-NI | ||||
| 	0x13e000e2: 263, // es-PA | ||||
| 	0x13e000e4: 264, // es-PE | ||||
| 	0x13e000e7: 265, // es-PH | ||||
| 	0x13e000ec: 266, // es-PR | ||||
| 	0x13e000f1: 267, // es-PY | ||||
| 	0x13e0011a: 268, // es-SV | ||||
| 	0x13e00135: 269, // es-US | ||||
| 	0x13e00136: 270, // es-UY | ||||
| 	0x13e0013b: 271, // es-VE | ||||
| 	0x14000000: 272, // et | ||||
| 	0x1400006a: 273, // et-EE | ||||
| 	0x14500000: 274, // eu | ||||
| 	0x1450006e: 275, // eu-ES | ||||
| 	0x14600000: 276, // ewo | ||||
| 	0x14600052: 277, // ewo-CM | ||||
| 	0x14800000: 278, // fa | ||||
| 	0x14800024: 279, // fa-AF | ||||
| 	0x1480009c: 280, // fa-IR | ||||
| 	0x14e00000: 281, // ff | ||||
| 	0x14e00052: 282, // ff-CM | ||||
| 	0x14e00084: 283, // ff-GN | ||||
| 	0x14e000c9: 284, // ff-MR | ||||
| 	0x14e00114: 285, // ff-SN | ||||
| 	0x15100000: 286, // fi | ||||
| 	0x15100072: 287, // fi-FI | ||||
| 	0x15300000: 288, // fil | ||||
| 	0x153000e7: 289, // fil-PH | ||||
| 	0x15800000: 290, // fo | ||||
| 	0x15800063: 291, // fo-DK | ||||
| 	0x15800076: 292, // fo-FO | ||||
| 	0x15e00000: 293, // fr | ||||
| 	0x15e00036: 294, // fr-BE | ||||
| 	0x15e00037: 295, // fr-BF | ||||
| 	0x15e0003a: 296, // fr-BI | ||||
| 	0x15e0003b: 297, // fr-BJ | ||||
| 	0x15e0003c: 298, // fr-BL | ||||
| 	0x15e00049: 299, // fr-CA | ||||
| 	0x15e0004b: 300, // fr-CD | ||||
| 	0x15e0004c: 301, // fr-CF | ||||
| 	0x15e0004d: 302, // fr-CG | ||||
| 	0x15e0004e: 303, // fr-CH | ||||
| 	0x15e0004f: 304, // fr-CI | ||||
| 	0x15e00052: 305, // fr-CM | ||||
| 	0x15e00062: 306, // fr-DJ | ||||
| 	0x15e00067: 307, // fr-DZ | ||||
| 	0x15e00078: 308, // fr-FR | ||||
| 	0x15e0007a: 309, // fr-GA | ||||
| 	0x15e0007e: 310, // fr-GF | ||||
| 	0x15e00084: 311, // fr-GN | ||||
| 	0x15e00085: 312, // fr-GP | ||||
| 	0x15e00086: 313, // fr-GQ | ||||
| 	0x15e00091: 314, // fr-HT | ||||
| 	0x15e000a8: 315, // fr-KM | ||||
| 	0x15e000b7: 316, // fr-LU | ||||
| 	0x15e000ba: 317, // fr-MA | ||||
| 	0x15e000bb: 318, // fr-MC | ||||
| 	0x15e000be: 319, // fr-MF | ||||
| 	0x15e000bf: 320, // fr-MG | ||||
| 	0x15e000c3: 321, // fr-ML | ||||
| 	0x15e000c8: 322, // fr-MQ | ||||
| 	0x15e000c9: 323, // fr-MR | ||||
| 	0x15e000cc: 324, // fr-MU | ||||
| 	0x15e000d3: 325, // fr-NC | ||||
| 	0x15e000d4: 326, // fr-NE | ||||
| 	0x15e000e5: 327, // fr-PF | ||||
| 	0x15e000ea: 328, // fr-PM | ||||
| 	0x15e00102: 329, // fr-RE | ||||
| 	0x15e00107: 330, // fr-RW | ||||
| 	0x15e0010a: 331, // fr-SC | ||||
| 	0x15e00114: 332, // fr-SN | ||||
| 	0x15e0011c: 333, // fr-SY | ||||
| 	0x15e00120: 334, // fr-TD | ||||
| 	0x15e00122: 335, // fr-TG | ||||
| 	0x15e00128: 336, // fr-TN | ||||
| 	0x15e0013f: 337, // fr-VU | ||||
| 	0x15e00140: 338, // fr-WF | ||||
| 	0x15e0015f: 339, // fr-YT | ||||
| 	0x16900000: 340, // fur | ||||
| 	0x1690009e: 341, // fur-IT | ||||
| 	0x16d00000: 342, // fy | ||||
| 	0x16d000d9: 343, // fy-NL | ||||
| 	0x16e00000: 344, // ga | ||||
| 	0x16e00096: 345, // ga-IE | ||||
| 	0x17e00000: 346, // gd | ||||
| 	0x17e0007b: 347, // gd-GB | ||||
| 	0x19000000: 348, // gl | ||||
| 	0x1900006e: 349, // gl-ES | ||||
| 	0x1a300000: 350, // gsw | ||||
| 	0x1a30004e: 351, // gsw-CH | ||||
| 	0x1a300078: 352, // gsw-FR | ||||
| 	0x1a3000b2: 353, // gsw-LI | ||||
| 	0x1a400000: 354, // gu | ||||
| 	0x1a400099: 355, // gu-IN | ||||
| 	0x1a900000: 356, // guw | ||||
| 	0x1ab00000: 357, // guz | ||||
| 	0x1ab000a4: 358, // guz-KE | ||||
| 	0x1ac00000: 359, // gv | ||||
| 	0x1ac00098: 360, // gv-IM | ||||
| 	0x1b400000: 361, // ha | ||||
| 	0x1b400080: 362, // ha-GH | ||||
| 	0x1b4000d4: 363, // ha-NE | ||||
| 	0x1b4000d6: 364, // ha-NG | ||||
| 	0x1b800000: 365, // haw | ||||
| 	0x1b800135: 366, // haw-US | ||||
| 	0x1bc00000: 367, // he | ||||
| 	0x1bc00097: 368, // he-IL | ||||
| 	0x1be00000: 369, // hi | ||||
| 	0x1be00099: 370, // hi-IN | ||||
| 	0x1d100000: 371, // hr | ||||
| 	0x1d100033: 372, // hr-BA | ||||
| 	0x1d100090: 373, // hr-HR | ||||
| 	0x1d200000: 374, // hsb | ||||
| 	0x1d200060: 375, // hsb-DE | ||||
| 	0x1d500000: 376, // hu | ||||
| 	0x1d500092: 377, // hu-HU | ||||
| 	0x1d700000: 378, // hy | ||||
| 	0x1d700028: 379, // hy-AM | ||||
| 	0x1e100000: 380, // id | ||||
| 	0x1e100095: 381, // id-ID | ||||
| 	0x1e700000: 382, // ig | ||||
| 	0x1e7000d6: 383, // ig-NG | ||||
| 	0x1ea00000: 384, // ii | ||||
| 	0x1ea00053: 385, // ii-CN | ||||
| 	0x1f500000: 386, // io | ||||
| 	0x1f800000: 387, // is | ||||
| 	0x1f80009d: 388, // is-IS | ||||
| 	0x1f900000: 389, // it | ||||
| 	0x1f90004e: 390, // it-CH | ||||
| 	0x1f90009e: 391, // it-IT | ||||
| 	0x1f900113: 392, // it-SM | ||||
| 	0x1f900138: 393, // it-VA | ||||
| 	0x1fa00000: 394, // iu | ||||
| 	0x20000000: 395, // ja | ||||
| 	0x200000a2: 396, // ja-JP | ||||
| 	0x20300000: 397, // jbo | ||||
| 	0x20700000: 398, // jgo | ||||
| 	0x20700052: 399, // jgo-CM | ||||
| 	0x20a00000: 400, // jmc | ||||
| 	0x20a0012f: 401, // jmc-TZ | ||||
| 	0x20e00000: 402, // jv | ||||
| 	0x21000000: 403, // ka | ||||
| 	0x2100007d: 404, // ka-GE | ||||
| 	0x21200000: 405, // kab | ||||
| 	0x21200067: 406, // kab-DZ | ||||
| 	0x21600000: 407, // kaj | ||||
| 	0x21700000: 408, // kam | ||||
| 	0x217000a4: 409, // kam-KE | ||||
| 	0x21f00000: 410, // kcg | ||||
| 	0x22300000: 411, // kde | ||||
| 	0x2230012f: 412, // kde-TZ | ||||
| 	0x22700000: 413, // kea | ||||
| 	0x2270005a: 414, // kea-CV | ||||
| 	0x23400000: 415, // khq | ||||
| 	0x234000c3: 416, // khq-ML | ||||
| 	0x23900000: 417, // ki | ||||
| 	0x239000a4: 418, // ki-KE | ||||
| 	0x24200000: 419, // kk | ||||
| 	0x242000ae: 420, // kk-KZ | ||||
| 	0x24400000: 421, // kkj | ||||
| 	0x24400052: 422, // kkj-CM | ||||
| 	0x24500000: 423, // kl | ||||
| 	0x24500082: 424, // kl-GL | ||||
| 	0x24600000: 425, // kln | ||||
| 	0x246000a4: 426, // kln-KE | ||||
| 	0x24a00000: 427, // km | ||||
| 	0x24a000a6: 428, // km-KH | ||||
| 	0x25100000: 429, // kn | ||||
| 	0x25100099: 430, // kn-IN | ||||
| 	0x25400000: 431, // ko | ||||
| 	0x254000aa: 432, // ko-KP | ||||
| 	0x254000ab: 433, // ko-KR | ||||
| 	0x25600000: 434, // kok | ||||
| 	0x25600099: 435, // kok-IN | ||||
| 	0x26a00000: 436, // ks | ||||
| 	0x26a00099: 437, // ks-IN | ||||
| 	0x26b00000: 438, // ksb | ||||
| 	0x26b0012f: 439, // ksb-TZ | ||||
| 	0x26d00000: 440, // ksf | ||||
| 	0x26d00052: 441, // ksf-CM | ||||
| 	0x26e00000: 442, // ksh | ||||
| 	0x26e00060: 443, // ksh-DE | ||||
| 	0x27400000: 444, // ku | ||||
| 	0x28100000: 445, // kw | ||||
| 	0x2810007b: 446, // kw-GB | ||||
| 	0x28a00000: 447, // ky | ||||
| 	0x28a000a5: 448, // ky-KG | ||||
| 	0x29100000: 449, // lag | ||||
| 	0x2910012f: 450, // lag-TZ | ||||
| 	0x29500000: 451, // lb | ||||
| 	0x295000b7: 452, // lb-LU | ||||
| 	0x2a300000: 453, // lg | ||||
| 	0x2a300131: 454, // lg-UG | ||||
| 	0x2af00000: 455, // lkt | ||||
| 	0x2af00135: 456, // lkt-US | ||||
| 	0x2b500000: 457, // ln | ||||
| 	0x2b50002a: 458, // ln-AO | ||||
| 	0x2b50004b: 459, // ln-CD | ||||
| 	0x2b50004c: 460, // ln-CF | ||||
| 	0x2b50004d: 461, // ln-CG | ||||
| 	0x2b800000: 462, // lo | ||||
| 	0x2b8000af: 463, // lo-LA | ||||
| 	0x2bf00000: 464, // lrc | ||||
| 	0x2bf0009b: 465, // lrc-IQ | ||||
| 	0x2bf0009c: 466, // lrc-IR | ||||
| 	0x2c000000: 467, // lt | ||||
| 	0x2c0000b6: 468, // lt-LT | ||||
| 	0x2c200000: 469, // lu | ||||
| 	0x2c20004b: 470, // lu-CD | ||||
| 	0x2c400000: 471, // luo | ||||
| 	0x2c4000a4: 472, // luo-KE | ||||
| 	0x2c500000: 473, // luy | ||||
| 	0x2c5000a4: 474, // luy-KE | ||||
| 	0x2c700000: 475, // lv | ||||
| 	0x2c7000b8: 476, // lv-LV | ||||
| 	0x2d100000: 477, // mas | ||||
| 	0x2d1000a4: 478, // mas-KE | ||||
| 	0x2d10012f: 479, // mas-TZ | ||||
| 	0x2e900000: 480, // mer | ||||
| 	0x2e9000a4: 481, // mer-KE | ||||
| 	0x2ed00000: 482, // mfe | ||||
| 	0x2ed000cc: 483, // mfe-MU | ||||
| 	0x2f100000: 484, // mg | ||||
| 	0x2f1000bf: 485, // mg-MG | ||||
| 	0x2f200000: 486, // mgh | ||||
| 	0x2f2000d1: 487, // mgh-MZ | ||||
| 	0x2f400000: 488, // mgo | ||||
| 	0x2f400052: 489, // mgo-CM | ||||
| 	0x2ff00000: 490, // mk | ||||
| 	0x2ff000c2: 491, // mk-MK | ||||
| 	0x30400000: 492, // ml | ||||
| 	0x30400099: 493, // ml-IN | ||||
| 	0x30b00000: 494, // mn | ||||
| 	0x30b000c5: 495, // mn-MN | ||||
| 	0x31b00000: 496, // mr | ||||
| 	0x31b00099: 497, // mr-IN | ||||
| 	0x31f00000: 498, // ms | ||||
| 	0x31f0003e: 499, // ms-BN | ||||
| 	0x31f000d0: 500, // ms-MY | ||||
| 	0x31f0010d: 501, // ms-SG | ||||
| 	0x32000000: 502, // mt | ||||
| 	0x320000cb: 503, // mt-MT | ||||
| 	0x32500000: 504, // mua | ||||
| 	0x32500052: 505, // mua-CM | ||||
| 	0x33100000: 506, // my | ||||
| 	0x331000c4: 507, // my-MM | ||||
| 	0x33a00000: 508, // mzn | ||||
| 	0x33a0009c: 509, // mzn-IR | ||||
| 	0x34100000: 510, // nah | ||||
| 	0x34500000: 511, // naq | ||||
| 	0x345000d2: 512, // naq-NA | ||||
| 	0x34700000: 513, // nb | ||||
| 	0x347000da: 514, // nb-NO | ||||
| 	0x34700110: 515, // nb-SJ | ||||
| 	0x34e00000: 516, // nd | ||||
| 	0x34e00164: 517, // nd-ZW | ||||
| 	0x35000000: 518, // nds | ||||
| 	0x35000060: 519, // nds-DE | ||||
| 	0x350000d9: 520, // nds-NL | ||||
| 	0x35100000: 521, // ne | ||||
| 	0x35100099: 522, // ne-IN | ||||
| 	0x351000db: 523, // ne-NP | ||||
| 	0x36700000: 524, // nl | ||||
| 	0x36700030: 525, // nl-AW | ||||
| 	0x36700036: 526, // nl-BE | ||||
| 	0x36700040: 527, // nl-BQ | ||||
| 	0x3670005b: 528, // nl-CW | ||||
| 	0x367000d9: 529, // nl-NL | ||||
| 	0x36700116: 530, // nl-SR | ||||
| 	0x3670011b: 531, // nl-SX | ||||
| 	0x36800000: 532, // nmg | ||||
| 	0x36800052: 533, // nmg-CM | ||||
| 	0x36a00000: 534, // nn | ||||
| 	0x36a000da: 535, // nn-NO | ||||
| 	0x36c00000: 536, // nnh | ||||
| 	0x36c00052: 537, // nnh-CM | ||||
| 	0x36f00000: 538, // no | ||||
| 	0x37500000: 539, // nqo | ||||
| 	0x37600000: 540, // nr | ||||
| 	0x37a00000: 541, // nso | ||||
| 	0x38000000: 542, // nus | ||||
| 	0x38000117: 543, // nus-SS | ||||
| 	0x38700000: 544, // ny | ||||
| 	0x38900000: 545, // nyn | ||||
| 	0x38900131: 546, // nyn-UG | ||||
| 	0x39000000: 547, // om | ||||
| 	0x3900006f: 548, // om-ET | ||||
| 	0x390000a4: 549, // om-KE | ||||
| 	0x39500000: 550, // or | ||||
| 	0x39500099: 551, // or-IN | ||||
| 	0x39800000: 552, // os | ||||
| 	0x3980007d: 553, // os-GE | ||||
| 	0x39800106: 554, // os-RU | ||||
| 	0x39d00000: 555, // pa | ||||
| 	0x39d05000: 556, // pa-Arab | ||||
| 	0x39d050e8: 557, // pa-Arab-PK | ||||
| 	0x39d33000: 558, // pa-Guru | ||||
| 	0x39d33099: 559, // pa-Guru-IN | ||||
| 	0x3a100000: 560, // pap | ||||
| 	0x3b300000: 561, // pl | ||||
| 	0x3b3000e9: 562, // pl-PL | ||||
| 	0x3bd00000: 563, // prg | ||||
| 	0x3bd00001: 564, // prg-001 | ||||
| 	0x3be00000: 565, // ps | ||||
| 	0x3be00024: 566, // ps-AF | ||||
| 	0x3c000000: 567, // pt | ||||
| 	0x3c00002a: 568, // pt-AO | ||||
| 	0x3c000041: 569, // pt-BR | ||||
| 	0x3c00004e: 570, // pt-CH | ||||
| 	0x3c00005a: 571, // pt-CV | ||||
| 	0x3c000086: 572, // pt-GQ | ||||
| 	0x3c00008b: 573, // pt-GW | ||||
| 	0x3c0000b7: 574, // pt-LU | ||||
| 	0x3c0000c6: 575, // pt-MO | ||||
| 	0x3c0000d1: 576, // pt-MZ | ||||
| 	0x3c0000ee: 577, // pt-PT | ||||
| 	0x3c000118: 578, // pt-ST | ||||
| 	0x3c000126: 579, // pt-TL | ||||
| 	0x3c400000: 580, // qu | ||||
| 	0x3c40003f: 581, // qu-BO | ||||
| 	0x3c400069: 582, // qu-EC | ||||
| 	0x3c4000e4: 583, // qu-PE | ||||
| 	0x3d400000: 584, // rm | ||||
| 	0x3d40004e: 585, // rm-CH | ||||
| 	0x3d900000: 586, // rn | ||||
| 	0x3d90003a: 587, // rn-BI | ||||
| 	0x3dc00000: 588, // ro | ||||
| 	0x3dc000bc: 589, // ro-MD | ||||
| 	0x3dc00104: 590, // ro-RO | ||||
| 	0x3de00000: 591, // rof | ||||
| 	0x3de0012f: 592, // rof-TZ | ||||
| 	0x3e200000: 593, // ru | ||||
| 	0x3e200047: 594, // ru-BY | ||||
| 	0x3e2000a5: 595, // ru-KG | ||||
| 	0x3e2000ae: 596, // ru-KZ | ||||
| 	0x3e2000bc: 597, // ru-MD | ||||
| 	0x3e200106: 598, // ru-RU | ||||
| 	0x3e200130: 599, // ru-UA | ||||
| 	0x3e500000: 600, // rw | ||||
| 	0x3e500107: 601, // rw-RW | ||||
| 	0x3e600000: 602, // rwk | ||||
| 	0x3e60012f: 603, // rwk-TZ | ||||
| 	0x3eb00000: 604, // sah | ||||
| 	0x3eb00106: 605, // sah-RU | ||||
| 	0x3ec00000: 606, // saq | ||||
| 	0x3ec000a4: 607, // saq-KE | ||||
| 	0x3f300000: 608, // sbp | ||||
| 	0x3f30012f: 609, // sbp-TZ | ||||
| 	0x3fa00000: 610, // sd | ||||
| 	0x3fa000e8: 611, // sd-PK | ||||
| 	0x3fc00000: 612, // sdh | ||||
| 	0x3fd00000: 613, // se | ||||
| 	0x3fd00072: 614, // se-FI | ||||
| 	0x3fd000da: 615, // se-NO | ||||
| 	0x3fd0010c: 616, // se-SE | ||||
| 	0x3ff00000: 617, // seh | ||||
| 	0x3ff000d1: 618, // seh-MZ | ||||
| 	0x40100000: 619, // ses | ||||
| 	0x401000c3: 620, // ses-ML | ||||
| 	0x40200000: 621, // sg | ||||
| 	0x4020004c: 622, // sg-CF | ||||
| 	0x40800000: 623, // shi | ||||
| 	0x40857000: 624, // shi-Latn | ||||
| 	0x408570ba: 625, // shi-Latn-MA | ||||
| 	0x408dc000: 626, // shi-Tfng | ||||
| 	0x408dc0ba: 627, // shi-Tfng-MA | ||||
| 	0x40c00000: 628, // si | ||||
| 	0x40c000b3: 629, // si-LK | ||||
| 	0x41200000: 630, // sk | ||||
| 	0x41200111: 631, // sk-SK | ||||
| 	0x41600000: 632, // sl | ||||
| 	0x4160010f: 633, // sl-SI | ||||
| 	0x41c00000: 634, // sma | ||||
| 	0x41d00000: 635, // smi | ||||
| 	0x41e00000: 636, // smj | ||||
| 	0x41f00000: 637, // smn | ||||
| 	0x41f00072: 638, // smn-FI | ||||
| 	0x42200000: 639, // sms | ||||
| 	0x42300000: 640, // sn | ||||
| 	0x42300164: 641, // sn-ZW | ||||
| 	0x42900000: 642, // so | ||||
| 	0x42900062: 643, // so-DJ | ||||
| 	0x4290006f: 644, // so-ET | ||||
| 	0x429000a4: 645, // so-KE | ||||
| 	0x42900115: 646, // so-SO | ||||
| 	0x43100000: 647, // sq | ||||
| 	0x43100027: 648, // sq-AL | ||||
| 	0x431000c2: 649, // sq-MK | ||||
| 	0x4310014d: 650, // sq-XK | ||||
| 	0x43200000: 651, // sr | ||||
| 	0x4321f000: 652, // sr-Cyrl | ||||
| 	0x4321f033: 653, // sr-Cyrl-BA | ||||
| 	0x4321f0bd: 654, // sr-Cyrl-ME | ||||
| 	0x4321f105: 655, // sr-Cyrl-RS | ||||
| 	0x4321f14d: 656, // sr-Cyrl-XK | ||||
| 	0x43257000: 657, // sr-Latn | ||||
| 	0x43257033: 658, // sr-Latn-BA | ||||
| 	0x432570bd: 659, // sr-Latn-ME | ||||
| 	0x43257105: 660, // sr-Latn-RS | ||||
| 	0x4325714d: 661, // sr-Latn-XK | ||||
| 	0x43700000: 662, // ss | ||||
| 	0x43a00000: 663, // ssy | ||||
| 	0x43b00000: 664, // st | ||||
| 	0x44400000: 665, // sv | ||||
| 	0x44400031: 666, // sv-AX | ||||
| 	0x44400072: 667, // sv-FI | ||||
| 	0x4440010c: 668, // sv-SE | ||||
| 	0x44500000: 669, // sw | ||||
| 	0x4450004b: 670, // sw-CD | ||||
| 	0x445000a4: 671, // sw-KE | ||||
| 	0x4450012f: 672, // sw-TZ | ||||
| 	0x44500131: 673, // sw-UG | ||||
| 	0x44e00000: 674, // syr | ||||
| 	0x45000000: 675, // ta | ||||
| 	0x45000099: 676, // ta-IN | ||||
| 	0x450000b3: 677, // ta-LK | ||||
| 	0x450000d0: 678, // ta-MY | ||||
| 	0x4500010d: 679, // ta-SG | ||||
| 	0x46100000: 680, // te | ||||
| 	0x46100099: 681, // te-IN | ||||
| 	0x46400000: 682, // teo | ||||
| 	0x464000a4: 683, // teo-KE | ||||
| 	0x46400131: 684, // teo-UG | ||||
| 	0x46700000: 685, // tg | ||||
| 	0x46700124: 686, // tg-TJ | ||||
| 	0x46b00000: 687, // th | ||||
| 	0x46b00123: 688, // th-TH | ||||
| 	0x46f00000: 689, // ti | ||||
| 	0x46f0006d: 690, // ti-ER | ||||
| 	0x46f0006f: 691, // ti-ET | ||||
| 	0x47100000: 692, // tig | ||||
| 	0x47600000: 693, // tk | ||||
| 	0x47600127: 694, // tk-TM | ||||
| 	0x48000000: 695, // tn | ||||
| 	0x48200000: 696, // to | ||||
| 	0x48200129: 697, // to-TO | ||||
| 	0x48a00000: 698, // tr | ||||
| 	0x48a0005d: 699, // tr-CY | ||||
| 	0x48a0012b: 700, // tr-TR | ||||
| 	0x48e00000: 701, // ts | ||||
| 	0x49400000: 702, // tt | ||||
| 	0x49400106: 703, // tt-RU | ||||
| 	0x4a400000: 704, // twq | ||||
| 	0x4a4000d4: 705, // twq-NE | ||||
| 	0x4a900000: 706, // tzm | ||||
| 	0x4a9000ba: 707, // tzm-MA | ||||
| 	0x4ac00000: 708, // ug | ||||
| 	0x4ac00053: 709, // ug-CN | ||||
| 	0x4ae00000: 710, // uk | ||||
| 	0x4ae00130: 711, // uk-UA | ||||
| 	0x4b400000: 712, // ur | ||||
| 	0x4b400099: 713, // ur-IN | ||||
| 	0x4b4000e8: 714, // ur-PK | ||||
| 	0x4bc00000: 715, // uz | ||||
| 	0x4bc05000: 716, // uz-Arab | ||||
| 	0x4bc05024: 717, // uz-Arab-AF | ||||
| 	0x4bc1f000: 718, // uz-Cyrl | ||||
| 	0x4bc1f137: 719, // uz-Cyrl-UZ | ||||
| 	0x4bc57000: 720, // uz-Latn | ||||
| 	0x4bc57137: 721, // uz-Latn-UZ | ||||
| 	0x4be00000: 722, // vai | ||||
| 	0x4be57000: 723, // vai-Latn | ||||
| 	0x4be570b4: 724, // vai-Latn-LR | ||||
| 	0x4bee3000: 725, // vai-Vaii | ||||
| 	0x4bee30b4: 726, // vai-Vaii-LR | ||||
| 	0x4c000000: 727, // ve | ||||
| 	0x4c300000: 728, // vi | ||||
| 	0x4c30013e: 729, // vi-VN | ||||
| 	0x4c900000: 730, // vo | ||||
| 	0x4c900001: 731, // vo-001 | ||||
| 	0x4cc00000: 732, // vun | ||||
| 	0x4cc0012f: 733, // vun-TZ | ||||
| 	0x4ce00000: 734, // wa | ||||
| 	0x4cf00000: 735, // wae | ||||
| 	0x4cf0004e: 736, // wae-CH | ||||
| 	0x4e500000: 737, // wo | ||||
| 	0x4e500114: 738, // wo-SN | ||||
| 	0x4f200000: 739, // xh | ||||
| 	0x4fb00000: 740, // xog | ||||
| 	0x4fb00131: 741, // xog-UG | ||||
| 	0x50900000: 742, // yav | ||||
| 	0x50900052: 743, // yav-CM | ||||
| 	0x51200000: 744, // yi | ||||
| 	0x51200001: 745, // yi-001 | ||||
| 	0x51800000: 746, // yo | ||||
| 	0x5180003b: 747, // yo-BJ | ||||
| 	0x518000d6: 748, // yo-NG | ||||
| 	0x51f00000: 749, // yue | ||||
| 	0x51f38000: 750, // yue-Hans | ||||
| 	0x51f38053: 751, // yue-Hans-CN | ||||
| 	0x51f39000: 752, // yue-Hant | ||||
| 	0x51f3908d: 753, // yue-Hant-HK | ||||
| 	0x52800000: 754, // zgh | ||||
| 	0x528000ba: 755, // zgh-MA | ||||
| 	0x52900000: 756, // zh | ||||
| 	0x52938000: 757, // zh-Hans | ||||
| 	0x52938053: 758, // zh-Hans-CN | ||||
| 	0x5293808d: 759, // zh-Hans-HK | ||||
| 	0x529380c6: 760, // zh-Hans-MO | ||||
| 	0x5293810d: 761, // zh-Hans-SG | ||||
| 	0x52939000: 762, // zh-Hant | ||||
| 	0x5293908d: 763, // zh-Hant-HK | ||||
| 	0x529390c6: 764, // zh-Hant-MO | ||||
| 	0x5293912e: 765, // zh-Hant-TW | ||||
| 	0x52f00000: 766, // zu | ||||
| 	0x52f00161: 767, // zu-ZA | ||||
| } | ||||
|  | ||||
| // Total table size 4676 bytes (4KiB); checksum: 17BE3673 | ||||
							
								
								
									
										907
									
								
								vendor/golang.org/x/text/language/language.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										907
									
								
								vendor/golang.org/x/text/language/language.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,907 @@ | ||||
| // Copyright 2013 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 -output tables.go | ||||
| //go:generate go run gen_index.go | ||||
|  | ||||
| package language | ||||
|  | ||||
| // TODO: Remove above NOTE after: | ||||
| // - verifying that tables are dropped correctly (most notably matcher tables). | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// maxCoreSize is the maximum size of a BCP 47 tag without variants and | ||||
| 	// extensions. Equals max lang (3) + script (4) + max reg (3) + 2 dashes. | ||||
| 	maxCoreSize = 12 | ||||
|  | ||||
| 	// max99thPercentileSize is a somewhat arbitrary buffer size that presumably | ||||
| 	// is large enough to hold at least 99% of the BCP 47 tags. | ||||
| 	max99thPercentileSize = 32 | ||||
|  | ||||
| 	// maxSimpleUExtensionSize is the maximum size of a -u extension with one | ||||
| 	// key-type pair. Equals len("-u-") + key (2) + dash + max value (8). | ||||
| 	maxSimpleUExtensionSize = 14 | ||||
| ) | ||||
|  | ||||
| // Tag represents a BCP 47 language tag. It is used to specify an instance of a | ||||
| // specific language or locale. All language tag values are guaranteed to be | ||||
| // well-formed. | ||||
| type Tag struct { | ||||
| 	lang   langID | ||||
| 	region regionID | ||||
| 	// TODO: we will soon run out of positions for script. Idea: instead of | ||||
| 	// storing lang, region, and script codes, store only the compact index and | ||||
| 	// have a lookup table from this code to its expansion. This greatly speeds | ||||
| 	// up table lookup, speed up common variant cases. | ||||
| 	// This will also immediately free up 3 extra bytes. Also, the pVariant | ||||
| 	// field can now be moved to the lookup table, as the compact index uniquely | ||||
| 	// determines the offset of a possible variant. | ||||
| 	script   scriptID | ||||
| 	pVariant byte   // offset in str, includes preceding '-' | ||||
| 	pExt     uint16 // offset of first extension, includes preceding '-' | ||||
|  | ||||
| 	// str is the string representation of the Tag. It will only be used if the | ||||
| 	// tag has variants or extensions. | ||||
| 	str string | ||||
| } | ||||
|  | ||||
| // Make is a convenience wrapper for Parse that omits the error. | ||||
| // In case of an error, a sensible default is returned. | ||||
| func Make(s string) Tag { | ||||
| 	return Default.Make(s) | ||||
| } | ||||
|  | ||||
| // Make is a convenience wrapper for c.Parse that omits the error. | ||||
| // In case of an error, a sensible default is returned. | ||||
| func (c CanonType) Make(s string) Tag { | ||||
| 	t, _ := c.Parse(s) | ||||
| 	return t | ||||
| } | ||||
|  | ||||
| // Raw returns the raw base language, script and region, without making an | ||||
| // attempt to infer their values. | ||||
| func (t Tag) Raw() (b Base, s Script, r Region) { | ||||
| 	return Base{t.lang}, Script{t.script}, Region{t.region} | ||||
| } | ||||
|  | ||||
| // equalTags compares language, script and region subtags only. | ||||
| func (t Tag) equalTags(a Tag) bool { | ||||
| 	return t.lang == a.lang && t.script == a.script && t.region == a.region | ||||
| } | ||||
|  | ||||
| // IsRoot returns true if t is equal to language "und". | ||||
| func (t Tag) IsRoot() bool { | ||||
| 	if int(t.pVariant) < len(t.str) { | ||||
| 		return false | ||||
| 	} | ||||
| 	return t.equalTags(und) | ||||
| } | ||||
|  | ||||
| // private reports whether the Tag consists solely of a private use tag. | ||||
| func (t Tag) private() bool { | ||||
| 	return t.str != "" && t.pVariant == 0 | ||||
| } | ||||
|  | ||||
| // CanonType can be used to enable or disable various types of canonicalization. | ||||
| type CanonType int | ||||
|  | ||||
| const ( | ||||
| 	// Replace deprecated base languages with their preferred replacements. | ||||
| 	DeprecatedBase CanonType = 1 << iota | ||||
| 	// Replace deprecated scripts with their preferred replacements. | ||||
| 	DeprecatedScript | ||||
| 	// Replace deprecated regions with their preferred replacements. | ||||
| 	DeprecatedRegion | ||||
| 	// Remove redundant scripts. | ||||
| 	SuppressScript | ||||
| 	// Normalize legacy encodings. This includes legacy languages defined in | ||||
| 	// CLDR as well as bibliographic codes defined in ISO-639. | ||||
| 	Legacy | ||||
| 	// Map the dominant language of a macro language group to the macro language | ||||
| 	// subtag. For example cmn -> zh. | ||||
| 	Macro | ||||
| 	// The CLDR flag should be used if full compatibility with CLDR is required. | ||||
| 	// There are a few cases where language.Tag may differ from CLDR. To follow all | ||||
| 	// of CLDR's suggestions, use All|CLDR. | ||||
| 	CLDR | ||||
|  | ||||
| 	// Raw can be used to Compose or Parse without Canonicalization. | ||||
| 	Raw CanonType = 0 | ||||
|  | ||||
| 	// Replace all deprecated tags with their preferred replacements. | ||||
| 	Deprecated = DeprecatedBase | DeprecatedScript | DeprecatedRegion | ||||
|  | ||||
| 	// All canonicalizations recommended by BCP 47. | ||||
| 	BCP47 = Deprecated | SuppressScript | ||||
|  | ||||
| 	// All canonicalizations. | ||||
| 	All = BCP47 | Legacy | Macro | ||||
|  | ||||
| 	// Default is the canonicalization used by Parse, Make and Compose. To | ||||
| 	// preserve as much information as possible, canonicalizations that remove | ||||
| 	// potentially valuable information are not included. The Matcher is | ||||
| 	// designed to recognize similar tags that would be the same if | ||||
| 	// they were canonicalized using All. | ||||
| 	Default = Deprecated | Legacy | ||||
|  | ||||
| 	canonLang = DeprecatedBase | Legacy | Macro | ||||
|  | ||||
| 	// TODO: LikelyScript, LikelyRegion: suppress similar to ICU. | ||||
| ) | ||||
|  | ||||
| // canonicalize returns the canonicalized equivalent of the tag and | ||||
| // whether there was any change. | ||||
| func (t Tag) canonicalize(c CanonType) (Tag, bool) { | ||||
| 	if c == Raw { | ||||
| 		return t, false | ||||
| 	} | ||||
| 	changed := false | ||||
| 	if c&SuppressScript != 0 { | ||||
| 		if t.lang < langNoIndexOffset && uint8(t.script) == suppressScript[t.lang] { | ||||
| 			t.script = 0 | ||||
| 			changed = true | ||||
| 		} | ||||
| 	} | ||||
| 	if c&canonLang != 0 { | ||||
| 		for { | ||||
| 			if l, aliasType := normLang(t.lang); l != t.lang { | ||||
| 				switch aliasType { | ||||
| 				case langLegacy: | ||||
| 					if c&Legacy != 0 { | ||||
| 						if t.lang == _sh && t.script == 0 { | ||||
| 							t.script = _Latn | ||||
| 						} | ||||
| 						t.lang = l | ||||
| 						changed = true | ||||
| 					} | ||||
| 				case langMacro: | ||||
| 					if c&Macro != 0 { | ||||
| 						// We deviate here from CLDR. The mapping "nb" -> "no" | ||||
| 						// qualifies as a typical Macro language mapping.  However, | ||||
| 						// for legacy reasons, CLDR maps "no", the macro language | ||||
| 						// code for Norwegian, to the dominant variant "nb". This | ||||
| 						// change is currently under consideration for CLDR as well. | ||||
| 						// See http://unicode.org/cldr/trac/ticket/2698 and also | ||||
| 						// http://unicode.org/cldr/trac/ticket/1790 for some of the | ||||
| 						// practical implications. TODO: this check could be removed | ||||
| 						// if CLDR adopts this change. | ||||
| 						if c&CLDR == 0 || t.lang != _nb { | ||||
| 							changed = true | ||||
| 							t.lang = l | ||||
| 						} | ||||
| 					} | ||||
| 				case langDeprecated: | ||||
| 					if c&DeprecatedBase != 0 { | ||||
| 						if t.lang == _mo && t.region == 0 { | ||||
| 							t.region = _MD | ||||
| 						} | ||||
| 						t.lang = l | ||||
| 						changed = true | ||||
| 						// Other canonicalization types may still apply. | ||||
| 						continue | ||||
| 					} | ||||
| 				} | ||||
| 			} else if c&Legacy != 0 && t.lang == _no && c&CLDR != 0 { | ||||
| 				t.lang = _nb | ||||
| 				changed = true | ||||
| 			} | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if c&DeprecatedScript != 0 { | ||||
| 		if t.script == _Qaai { | ||||
| 			changed = true | ||||
| 			t.script = _Zinh | ||||
| 		} | ||||
| 	} | ||||
| 	if c&DeprecatedRegion != 0 { | ||||
| 		if r := normRegion(t.region); r != 0 { | ||||
| 			changed = true | ||||
| 			t.region = r | ||||
| 		} | ||||
| 	} | ||||
| 	return t, changed | ||||
| } | ||||
|  | ||||
| // Canonicalize returns the canonicalized equivalent of the tag. | ||||
| func (c CanonType) Canonicalize(t Tag) (Tag, error) { | ||||
| 	t, changed := t.canonicalize(c) | ||||
| 	if changed { | ||||
| 		t.remakeString() | ||||
| 	} | ||||
| 	return t, nil | ||||
| } | ||||
|  | ||||
| // Confidence indicates the level of certainty for a given return value. | ||||
| // For example, Serbian may be written in Cyrillic or Latin script. | ||||
| // The confidence level indicates whether a value was explicitly specified, | ||||
| // whether it is typically the only possible value, or whether there is | ||||
| // an ambiguity. | ||||
| type Confidence int | ||||
|  | ||||
| const ( | ||||
| 	No    Confidence = iota // full confidence that there was no match | ||||
| 	Low                     // most likely value picked out of a set of alternatives | ||||
| 	High                    // value is generally assumed to be the correct match | ||||
| 	Exact                   // exact match or explicitly specified value | ||||
| ) | ||||
|  | ||||
| var confName = []string{"No", "Low", "High", "Exact"} | ||||
|  | ||||
| func (c Confidence) String() string { | ||||
| 	return confName[c] | ||||
| } | ||||
|  | ||||
| // remakeString is used to update t.str in case lang, script or region changed. | ||||
| // It is assumed that pExt and pVariant still point to the start of the | ||||
| // respective parts. | ||||
| func (t *Tag) remakeString() { | ||||
| 	if t.str == "" { | ||||
| 		return | ||||
| 	} | ||||
| 	extra := t.str[t.pVariant:] | ||||
| 	if t.pVariant > 0 { | ||||
| 		extra = extra[1:] | ||||
| 	} | ||||
| 	if t.equalTags(und) && strings.HasPrefix(extra, "x-") { | ||||
| 		t.str = extra | ||||
| 		t.pVariant = 0 | ||||
| 		t.pExt = 0 | ||||
| 		return | ||||
| 	} | ||||
| 	var buf [max99thPercentileSize]byte // avoid extra memory allocation in most cases. | ||||
| 	b := buf[:t.genCoreBytes(buf[:])] | ||||
| 	if extra != "" { | ||||
| 		diff := len(b) - int(t.pVariant) | ||||
| 		b = append(b, '-') | ||||
| 		b = append(b, extra...) | ||||
| 		t.pVariant = uint8(int(t.pVariant) + diff) | ||||
| 		t.pExt = uint16(int(t.pExt) + diff) | ||||
| 	} else { | ||||
| 		t.pVariant = uint8(len(b)) | ||||
| 		t.pExt = uint16(len(b)) | ||||
| 	} | ||||
| 	t.str = string(b) | ||||
| } | ||||
|  | ||||
| // genCoreBytes writes a string for the base languages, script and region tags | ||||
| // to the given buffer and returns the number of bytes written. It will never | ||||
| // write more than maxCoreSize bytes. | ||||
| func (t *Tag) genCoreBytes(buf []byte) int { | ||||
| 	n := t.lang.stringToBuf(buf[:]) | ||||
| 	if t.script != 0 { | ||||
| 		n += copy(buf[n:], "-") | ||||
| 		n += copy(buf[n:], t.script.String()) | ||||
| 	} | ||||
| 	if t.region != 0 { | ||||
| 		n += copy(buf[n:], "-") | ||||
| 		n += copy(buf[n:], t.region.String()) | ||||
| 	} | ||||
| 	return n | ||||
| } | ||||
|  | ||||
| // String returns the canonical string representation of the language tag. | ||||
| func (t Tag) String() string { | ||||
| 	if t.str != "" { | ||||
| 		return t.str | ||||
| 	} | ||||
| 	if t.script == 0 && t.region == 0 { | ||||
| 		return t.lang.String() | ||||
| 	} | ||||
| 	buf := [maxCoreSize]byte{} | ||||
| 	return string(buf[:t.genCoreBytes(buf[:])]) | ||||
| } | ||||
|  | ||||
| // MarshalText implements encoding.TextMarshaler. | ||||
| func (t Tag) MarshalText() (text []byte, err error) { | ||||
| 	if t.str != "" { | ||||
| 		text = append(text, t.str...) | ||||
| 	} else if t.script == 0 && t.region == 0 { | ||||
| 		text = append(text, t.lang.String()...) | ||||
| 	} else { | ||||
| 		buf := [maxCoreSize]byte{} | ||||
| 		text = buf[:t.genCoreBytes(buf[:])] | ||||
| 	} | ||||
| 	return text, nil | ||||
| } | ||||
|  | ||||
| // UnmarshalText implements encoding.TextUnmarshaler. | ||||
| func (t *Tag) UnmarshalText(text []byte) error { | ||||
| 	tag, err := Raw.Parse(string(text)) | ||||
| 	*t = tag | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // Base returns the base language of the language tag. If the base language is | ||||
| // unspecified, an attempt will be made to infer it from the context. | ||||
| // It uses a variant of CLDR's Add Likely Subtags algorithm. This is subject to change. | ||||
| func (t Tag) Base() (Base, Confidence) { | ||||
| 	if t.lang != 0 { | ||||
| 		return Base{t.lang}, Exact | ||||
| 	} | ||||
| 	c := High | ||||
| 	if t.script == 0 && !(Region{t.region}).IsCountry() { | ||||
| 		c = Low | ||||
| 	} | ||||
| 	if tag, err := addTags(t); err == nil && tag.lang != 0 { | ||||
| 		return Base{tag.lang}, c | ||||
| 	} | ||||
| 	return Base{0}, No | ||||
| } | ||||
|  | ||||
| // Script infers the script for the language tag. If it was not explicitly given, it will infer | ||||
| // a most likely candidate. | ||||
| // If more than one script is commonly used for a language, the most likely one | ||||
| // is returned with a low confidence indication. For example, it returns (Cyrl, Low) | ||||
| // for Serbian. | ||||
| // If a script cannot be inferred (Zzzz, No) is returned. We do not use Zyyy (undetermined) | ||||
| // as one would suspect from the IANA registry for BCP 47. In a Unicode context Zyyy marks | ||||
| // common characters (like 1, 2, 3, '.', etc.) and is therefore more like multiple scripts. | ||||
| // See http://www.unicode.org/reports/tr24/#Values for more details. Zzzz is also used for | ||||
| // unknown value in CLDR.  (Zzzz, Exact) is returned if Zzzz was explicitly specified. | ||||
| // Note that an inferred script is never guaranteed to be the correct one. Latin is | ||||
| // almost exclusively used for Afrikaans, but Arabic has been used for some texts | ||||
| // in the past.  Also, the script that is commonly used may change over time. | ||||
| // It uses a variant of CLDR's Add Likely Subtags algorithm. This is subject to change. | ||||
| func (t Tag) Script() (Script, Confidence) { | ||||
| 	if t.script != 0 { | ||||
| 		return Script{t.script}, Exact | ||||
| 	} | ||||
| 	sc, c := scriptID(_Zzzz), No | ||||
| 	if t.lang < langNoIndexOffset { | ||||
| 		if scr := scriptID(suppressScript[t.lang]); scr != 0 { | ||||
| 			// Note: it is not always the case that a language with a suppress | ||||
| 			// script value is only written in one script (e.g. kk, ms, pa). | ||||
| 			if t.region == 0 { | ||||
| 				return Script{scriptID(scr)}, High | ||||
| 			} | ||||
| 			sc, c = scr, High | ||||
| 		} | ||||
| 	} | ||||
| 	if tag, err := addTags(t); err == nil { | ||||
| 		if tag.script != sc { | ||||
| 			sc, c = tag.script, Low | ||||
| 		} | ||||
| 	} else { | ||||
| 		t, _ = (Deprecated | Macro).Canonicalize(t) | ||||
| 		if tag, err := addTags(t); err == nil && tag.script != sc { | ||||
| 			sc, c = tag.script, Low | ||||
| 		} | ||||
| 	} | ||||
| 	return Script{sc}, c | ||||
| } | ||||
|  | ||||
| // Region returns the region for the language tag. If it was not explicitly given, it will | ||||
| // infer a most likely candidate from the context. | ||||
| // It uses a variant of CLDR's Add Likely Subtags algorithm. This is subject to change. | ||||
| func (t Tag) Region() (Region, Confidence) { | ||||
| 	if t.region != 0 { | ||||
| 		return Region{t.region}, Exact | ||||
| 	} | ||||
| 	if t, err := addTags(t); err == nil { | ||||
| 		return Region{t.region}, Low // TODO: differentiate between high and low. | ||||
| 	} | ||||
| 	t, _ = (Deprecated | Macro).Canonicalize(t) | ||||
| 	if tag, err := addTags(t); err == nil { | ||||
| 		return Region{tag.region}, Low | ||||
| 	} | ||||
| 	return Region{_ZZ}, No // TODO: return world instead of undetermined? | ||||
| } | ||||
|  | ||||
| // Variant returns the variants specified explicitly for this language tag. | ||||
| // or nil if no variant was specified. | ||||
| func (t Tag) Variants() []Variant { | ||||
| 	v := []Variant{} | ||||
| 	if int(t.pVariant) < int(t.pExt) { | ||||
| 		for x, str := "", t.str[t.pVariant:t.pExt]; str != ""; { | ||||
| 			x, str = nextToken(str) | ||||
| 			v = append(v, Variant{x}) | ||||
| 		} | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| // Parent returns the CLDR parent of t. In CLDR, missing fields in data for a | ||||
| // specific language are substituted with fields from the parent language. | ||||
| // The parent for a language may change for newer versions of CLDR. | ||||
| func (t Tag) Parent() Tag { | ||||
| 	if t.str != "" { | ||||
| 		// Strip the variants and extensions. | ||||
| 		t, _ = Raw.Compose(t.Raw()) | ||||
| 		if t.region == 0 && t.script != 0 && t.lang != 0 { | ||||
| 			base, _ := addTags(Tag{lang: t.lang}) | ||||
| 			if base.script == t.script { | ||||
| 				return Tag{lang: t.lang} | ||||
| 			} | ||||
| 		} | ||||
| 		return t | ||||
| 	} | ||||
| 	if t.lang != 0 { | ||||
| 		if t.region != 0 { | ||||
| 			maxScript := t.script | ||||
| 			if maxScript == 0 { | ||||
| 				max, _ := addTags(t) | ||||
| 				maxScript = max.script | ||||
| 			} | ||||
|  | ||||
| 			for i := range parents { | ||||
| 				if langID(parents[i].lang) == t.lang && scriptID(parents[i].maxScript) == maxScript { | ||||
| 					for _, r := range parents[i].fromRegion { | ||||
| 						if regionID(r) == t.region { | ||||
| 							return Tag{ | ||||
| 								lang:   t.lang, | ||||
| 								script: scriptID(parents[i].script), | ||||
| 								region: regionID(parents[i].toRegion), | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Strip the script if it is the default one. | ||||
| 			base, _ := addTags(Tag{lang: t.lang}) | ||||
| 			if base.script != maxScript { | ||||
| 				return Tag{lang: t.lang, script: maxScript} | ||||
| 			} | ||||
| 			return Tag{lang: t.lang} | ||||
| 		} else if t.script != 0 { | ||||
| 			// The parent for an base-script pair with a non-default script is | ||||
| 			// "und" instead of the base language. | ||||
| 			base, _ := addTags(Tag{lang: t.lang}) | ||||
| 			if base.script != t.script { | ||||
| 				return und | ||||
| 			} | ||||
| 			return Tag{lang: t.lang} | ||||
| 		} | ||||
| 	} | ||||
| 	return und | ||||
| } | ||||
|  | ||||
| // returns token t and the rest of the string. | ||||
| func nextToken(s string) (t, tail string) { | ||||
| 	p := strings.Index(s[1:], "-") | ||||
| 	if p == -1 { | ||||
| 		return s[1:], "" | ||||
| 	} | ||||
| 	p++ | ||||
| 	return s[1:p], s[p:] | ||||
| } | ||||
|  | ||||
| // Extension is a single BCP 47 extension. | ||||
| type Extension struct { | ||||
| 	s string | ||||
| } | ||||
|  | ||||
| // String returns the string representation of the extension, including the | ||||
| // type tag. | ||||
| func (e Extension) String() string { | ||||
| 	return e.s | ||||
| } | ||||
|  | ||||
| // ParseExtension parses s as an extension and returns it on success. | ||||
| func ParseExtension(s string) (e Extension, err error) { | ||||
| 	scan := makeScannerString(s) | ||||
| 	var end int | ||||
| 	if n := len(scan.token); n != 1 { | ||||
| 		return Extension{}, errSyntax | ||||
| 	} | ||||
| 	scan.toLower(0, len(scan.b)) | ||||
| 	end = parseExtension(&scan) | ||||
| 	if end != len(s) { | ||||
| 		return Extension{}, errSyntax | ||||
| 	} | ||||
| 	return Extension{string(scan.b)}, nil | ||||
| } | ||||
|  | ||||
| // Type returns the one-byte extension type of e. It returns 0 for the zero | ||||
| // exception. | ||||
| func (e Extension) Type() byte { | ||||
| 	if e.s == "" { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	return e.s[0] | ||||
| } | ||||
|  | ||||
| // Tokens returns the list of tokens of e. | ||||
| func (e Extension) Tokens() []string { | ||||
| 	return strings.Split(e.s, "-") | ||||
| } | ||||
|  | ||||
| // Extension returns the extension of type x for tag t. It will return | ||||
| // false for ok if t does not have the requested extension. The returned | ||||
| // extension will be invalid in this case. | ||||
| func (t Tag) Extension(x byte) (ext Extension, ok bool) { | ||||
| 	for i := int(t.pExt); i < len(t.str)-1; { | ||||
| 		var ext string | ||||
| 		i, ext = getExtension(t.str, i) | ||||
| 		if ext[0] == x { | ||||
| 			return Extension{ext}, true | ||||
| 		} | ||||
| 	} | ||||
| 	return Extension{}, false | ||||
| } | ||||
|  | ||||
| // Extensions returns all extensions of t. | ||||
| func (t Tag) Extensions() []Extension { | ||||
| 	e := []Extension{} | ||||
| 	for i := int(t.pExt); i < len(t.str)-1; { | ||||
| 		var ext string | ||||
| 		i, ext = getExtension(t.str, i) | ||||
| 		e = append(e, Extension{ext}) | ||||
| 	} | ||||
| 	return e | ||||
| } | ||||
|  | ||||
| // TypeForKey returns the type associated with the given key, where key and type | ||||
| // are of the allowed values defined for the Unicode locale extension ('u') in | ||||
| // http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers. | ||||
| // TypeForKey will traverse the inheritance chain to get the correct value. | ||||
| func (t Tag) TypeForKey(key string) string { | ||||
| 	if start, end, _ := t.findTypeForKey(key); end != start { | ||||
| 		return t.str[start:end] | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| var ( | ||||
| 	errPrivateUse       = errors.New("cannot set a key on a private use tag") | ||||
| 	errInvalidArguments = errors.New("invalid key or type") | ||||
| ) | ||||
|  | ||||
| // SetTypeForKey returns a new Tag with the key set to type, where key and type | ||||
| // are of the allowed values defined for the Unicode locale extension ('u') in | ||||
| // http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers. | ||||
| // An empty value removes an existing pair with the same key. | ||||
| func (t Tag) SetTypeForKey(key, value string) (Tag, error) { | ||||
| 	if t.private() { | ||||
| 		return t, errPrivateUse | ||||
| 	} | ||||
| 	if len(key) != 2 { | ||||
| 		return t, errInvalidArguments | ||||
| 	} | ||||
|  | ||||
| 	// Remove the setting if value is "". | ||||
| 	if value == "" { | ||||
| 		start, end, _ := t.findTypeForKey(key) | ||||
| 		if start != end { | ||||
| 			// Remove key tag and leading '-'. | ||||
| 			start -= 4 | ||||
|  | ||||
| 			// Remove a possible empty extension. | ||||
| 			if (end == len(t.str) || t.str[end+2] == '-') && t.str[start-2] == '-' { | ||||
| 				start -= 2 | ||||
| 			} | ||||
| 			if start == int(t.pVariant) && end == len(t.str) { | ||||
| 				t.str = "" | ||||
| 				t.pVariant, t.pExt = 0, 0 | ||||
| 			} else { | ||||
| 				t.str = fmt.Sprintf("%s%s", t.str[:start], t.str[end:]) | ||||
| 			} | ||||
| 		} | ||||
| 		return t, nil | ||||
| 	} | ||||
|  | ||||
| 	if len(value) < 3 || len(value) > 8 { | ||||
| 		return t, errInvalidArguments | ||||
| 	} | ||||
|  | ||||
| 	var ( | ||||
| 		buf    [maxCoreSize + maxSimpleUExtensionSize]byte | ||||
| 		uStart int // start of the -u extension. | ||||
| 	) | ||||
|  | ||||
| 	// Generate the tag string if needed. | ||||
| 	if t.str == "" { | ||||
| 		uStart = t.genCoreBytes(buf[:]) | ||||
| 		buf[uStart] = '-' | ||||
| 		uStart++ | ||||
| 	} | ||||
|  | ||||
| 	// Create new key-type pair and parse it to verify. | ||||
| 	b := buf[uStart:] | ||||
| 	copy(b, "u-") | ||||
| 	copy(b[2:], key) | ||||
| 	b[4] = '-' | ||||
| 	b = b[:5+copy(b[5:], value)] | ||||
| 	scan := makeScanner(b) | ||||
| 	if parseExtensions(&scan); scan.err != nil { | ||||
| 		return t, scan.err | ||||
| 	} | ||||
|  | ||||
| 	// Assemble the replacement string. | ||||
| 	if t.str == "" { | ||||
| 		t.pVariant, t.pExt = byte(uStart-1), uint16(uStart-1) | ||||
| 		t.str = string(buf[:uStart+len(b)]) | ||||
| 	} else { | ||||
| 		s := t.str | ||||
| 		start, end, hasExt := t.findTypeForKey(key) | ||||
| 		if start == end { | ||||
| 			if hasExt { | ||||
| 				b = b[2:] | ||||
| 			} | ||||
| 			t.str = fmt.Sprintf("%s-%s%s", s[:start], b, s[end:]) | ||||
| 		} else { | ||||
| 			t.str = fmt.Sprintf("%s%s%s", s[:start], value, s[end:]) | ||||
| 		} | ||||
| 	} | ||||
| 	return t, nil | ||||
| } | ||||
|  | ||||
| // findKeyAndType returns the start and end position for the type corresponding | ||||
| // to key or the point at which to insert the key-value pair if the type | ||||
| // wasn't found. The hasExt return value reports whether an -u extension was present. | ||||
| // Note: the extensions are typically very small and are likely to contain | ||||
| // only one key-type pair. | ||||
| func (t Tag) findTypeForKey(key string) (start, end int, hasExt bool) { | ||||
| 	p := int(t.pExt) | ||||
| 	if len(key) != 2 || p == len(t.str) || p == 0 { | ||||
| 		return p, p, false | ||||
| 	} | ||||
| 	s := t.str | ||||
|  | ||||
| 	// Find the correct extension. | ||||
| 	for p++; s[p] != 'u'; p++ { | ||||
| 		if s[p] > 'u' { | ||||
| 			p-- | ||||
| 			return p, p, false | ||||
| 		} | ||||
| 		if p = nextExtension(s, p); p == len(s) { | ||||
| 			return len(s), len(s), false | ||||
| 		} | ||||
| 	} | ||||
| 	// Proceed to the hyphen following the extension name. | ||||
| 	p++ | ||||
|  | ||||
| 	// curKey is the key currently being processed. | ||||
| 	curKey := "" | ||||
|  | ||||
| 	// Iterate over keys until we get the end of a section. | ||||
| 	for { | ||||
| 		// p points to the hyphen preceding the current token. | ||||
| 		if p3 := p + 3; s[p3] == '-' { | ||||
| 			// Found a key. | ||||
| 			// Check whether we just processed the key that was requested. | ||||
| 			if curKey == key { | ||||
| 				return start, p, true | ||||
| 			} | ||||
| 			// Set to the next key and continue scanning type tokens. | ||||
| 			curKey = s[p+1 : p3] | ||||
| 			if curKey > key { | ||||
| 				return p, p, true | ||||
| 			} | ||||
| 			// Start of the type token sequence. | ||||
| 			start = p + 4 | ||||
| 			// A type is at least 3 characters long. | ||||
| 			p += 7 // 4 + 3 | ||||
| 		} else { | ||||
| 			// Attribute or type, which is at least 3 characters long. | ||||
| 			p += 4 | ||||
| 		} | ||||
| 		// p points past the third character of a type or attribute. | ||||
| 		max := p + 5 // maximum length of token plus hyphen. | ||||
| 		if len(s) < max { | ||||
| 			max = len(s) | ||||
| 		} | ||||
| 		for ; p < max && s[p] != '-'; p++ { | ||||
| 		} | ||||
| 		// Bail if we have exhausted all tokens or if the next token starts | ||||
| 		// a new extension. | ||||
| 		if p == len(s) || s[p+2] == '-' { | ||||
| 			if curKey == key { | ||||
| 				return start, p, true | ||||
| 			} | ||||
| 			return p, p, true | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // CompactIndex returns an index, where 0 <= index < NumCompactTags, for tags | ||||
| // for which data exists in the text repository. The index will change over time | ||||
| // and should not be stored in persistent storage. Extensions, except for the | ||||
| // 'va' type of the 'u' extension, are ignored. It will return 0, false if no | ||||
| // compact tag exists, where 0 is the index for the root language (Und). | ||||
| func CompactIndex(t Tag) (index int, ok bool) { | ||||
| 	// TODO: perhaps give more frequent tags a lower index. | ||||
| 	// TODO: we could make the indexes stable. This will excluded some | ||||
| 	//       possibilities for optimization, so don't do this quite yet. | ||||
| 	b, s, r := t.Raw() | ||||
| 	if len(t.str) > 0 { | ||||
| 		if strings.HasPrefix(t.str, "x-") { | ||||
| 			// We have no entries for user-defined tags. | ||||
| 			return 0, false | ||||
| 		} | ||||
| 		if uint16(t.pVariant) != t.pExt { | ||||
| 			// There are no tags with variants and an u-va type. | ||||
| 			if t.TypeForKey("va") != "" { | ||||
| 				return 0, false | ||||
| 			} | ||||
| 			t, _ = Raw.Compose(b, s, r, t.Variants()) | ||||
| 		} else if _, ok := t.Extension('u'); ok { | ||||
| 			// Strip all but the 'va' entry. | ||||
| 			variant := t.TypeForKey("va") | ||||
| 			t, _ = Raw.Compose(b, s, r) | ||||
| 			t, _ = t.SetTypeForKey("va", variant) | ||||
| 		} | ||||
| 		if len(t.str) > 0 { | ||||
| 			// We have some variants. | ||||
| 			for i, s := range specialTags { | ||||
| 				if s == t { | ||||
| 					return i + 1, true | ||||
| 				} | ||||
| 			} | ||||
| 			return 0, false | ||||
| 		} | ||||
| 	} | ||||
| 	// No variants specified: just compare core components. | ||||
| 	// The key has the form lllssrrr, where l, s, and r are nibbles for | ||||
| 	// respectively the langID, scriptID, and regionID. | ||||
| 	key := uint32(b.langID) << (8 + 12) | ||||
| 	key |= uint32(s.scriptID) << 12 | ||||
| 	key |= uint32(r.regionID) | ||||
| 	x, ok := coreTags[key] | ||||
| 	return int(x), ok | ||||
| } | ||||
|  | ||||
| // Base is an ISO 639 language code, used for encoding the base language | ||||
| // of a language tag. | ||||
| type Base struct { | ||||
| 	langID | ||||
| } | ||||
|  | ||||
| // ParseBase parses a 2- or 3-letter ISO 639 code. | ||||
| // It returns a ValueError if s is a well-formed but unknown language identifier | ||||
| // or another error if another error occurred. | ||||
| func ParseBase(s string) (Base, error) { | ||||
| 	if n := len(s); n < 2 || 3 < n { | ||||
| 		return Base{}, errSyntax | ||||
| 	} | ||||
| 	var buf [3]byte | ||||
| 	l, err := getLangID(buf[:copy(buf[:], s)]) | ||||
| 	return Base{l}, err | ||||
| } | ||||
|  | ||||
| // Script is a 4-letter ISO 15924 code for representing scripts. | ||||
| // It is idiomatically represented in title case. | ||||
| type Script struct { | ||||
| 	scriptID | ||||
| } | ||||
|  | ||||
| // ParseScript parses a 4-letter ISO 15924 code. | ||||
| // It returns a ValueError if s is a well-formed but unknown script identifier | ||||
| // or another error if another error occurred. | ||||
| func ParseScript(s string) (Script, error) { | ||||
| 	if len(s) != 4 { | ||||
| 		return Script{}, errSyntax | ||||
| 	} | ||||
| 	var buf [4]byte | ||||
| 	sc, err := getScriptID(script, buf[:copy(buf[:], s)]) | ||||
| 	return Script{sc}, err | ||||
| } | ||||
|  | ||||
| // Region is an ISO 3166-1 or UN M.49 code for representing countries and regions. | ||||
| type Region struct { | ||||
| 	regionID | ||||
| } | ||||
|  | ||||
| // EncodeM49 returns the Region for the given UN M.49 code. | ||||
| // It returns an error if r is not a valid code. | ||||
| func EncodeM49(r int) (Region, error) { | ||||
| 	rid, err := getRegionM49(r) | ||||
| 	return Region{rid}, err | ||||
| } | ||||
|  | ||||
| // ParseRegion parses a 2- or 3-letter ISO 3166-1 or a UN M.49 code. | ||||
| // It returns a ValueError if s is a well-formed but unknown region identifier | ||||
| // or another error if another error occurred. | ||||
| func ParseRegion(s string) (Region, error) { | ||||
| 	if n := len(s); n < 2 || 3 < n { | ||||
| 		return Region{}, errSyntax | ||||
| 	} | ||||
| 	var buf [3]byte | ||||
| 	r, err := getRegionID(buf[:copy(buf[:], s)]) | ||||
| 	return Region{r}, err | ||||
| } | ||||
|  | ||||
| // IsCountry returns whether this region is a country or autonomous area. This | ||||
| // includes non-standard definitions from CLDR. | ||||
| func (r Region) IsCountry() bool { | ||||
| 	if r.regionID == 0 || r.IsGroup() || r.IsPrivateUse() && r.regionID != _XK { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| // IsGroup returns whether this region defines a collection of regions. This | ||||
| // includes non-standard definitions from CLDR. | ||||
| func (r Region) IsGroup() bool { | ||||
| 	if r.regionID == 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	return int(regionInclusion[r.regionID]) < len(regionContainment) | ||||
| } | ||||
|  | ||||
| // Contains returns whether Region c is contained by Region r. It returns true | ||||
| // if c == r. | ||||
| func (r Region) Contains(c Region) bool { | ||||
| 	return r.regionID.contains(c.regionID) | ||||
| } | ||||
|  | ||||
| func (r regionID) contains(c regionID) bool { | ||||
| 	if r == c { | ||||
| 		return true | ||||
| 	} | ||||
| 	g := regionInclusion[r] | ||||
| 	if g >= nRegionGroups { | ||||
| 		return false | ||||
| 	} | ||||
| 	m := regionContainment[g] | ||||
|  | ||||
| 	d := regionInclusion[c] | ||||
| 	b := regionInclusionBits[d] | ||||
|  | ||||
| 	// A contained country may belong to multiple disjoint groups. Matching any | ||||
| 	// of these indicates containment. If the contained region is a group, it | ||||
| 	// must strictly be a subset. | ||||
| 	if d >= nRegionGroups { | ||||
| 		return b&m != 0 | ||||
| 	} | ||||
| 	return b&^m == 0 | ||||
| } | ||||
|  | ||||
| var errNoTLD = errors.New("language: region is not a valid ccTLD") | ||||
|  | ||||
| // TLD returns the country code top-level domain (ccTLD). UK is returned for GB. | ||||
| // In all other cases it returns either the region itself or an error. | ||||
| // | ||||
| // This method may return an error for a region for which there exists a | ||||
| // canonical form with a ccTLD. To get that ccTLD canonicalize r first. The | ||||
| // region will already be canonicalized it was obtained from a Tag that was | ||||
| // obtained using any of the default methods. | ||||
| func (r Region) TLD() (Region, error) { | ||||
| 	// See http://en.wikipedia.org/wiki/Country_code_top-level_domain for the | ||||
| 	// difference between ISO 3166-1 and IANA ccTLD. | ||||
| 	if r.regionID == _GB { | ||||
| 		r = Region{_UK} | ||||
| 	} | ||||
| 	if (r.typ() & ccTLD) == 0 { | ||||
| 		return Region{}, errNoTLD | ||||
| 	} | ||||
| 	return r, nil | ||||
| } | ||||
|  | ||||
| // Canonicalize returns the region or a possible replacement if the region is | ||||
| // deprecated. It will not return a replacement for deprecated regions that | ||||
| // are split into multiple regions. | ||||
| func (r Region) Canonicalize() Region { | ||||
| 	if cr := normRegion(r.regionID); cr != 0 { | ||||
| 		return Region{cr} | ||||
| 	} | ||||
| 	return r | ||||
| } | ||||
|  | ||||
| // Variant represents a registered variant of a language as defined by BCP 47. | ||||
| type Variant struct { | ||||
| 	variant string | ||||
| } | ||||
|  | ||||
| // ParseVariant parses and returns a Variant. An error is returned if s is not | ||||
| // a valid variant. | ||||
| func ParseVariant(s string) (Variant, error) { | ||||
| 	s = strings.ToLower(s) | ||||
| 	if _, ok := variantIndex[s]; ok { | ||||
| 		return Variant{s}, nil | ||||
| 	} | ||||
| 	return Variant{}, mkErrInvalid([]byte(s)) | ||||
| } | ||||
|  | ||||
| // String returns the string representation of the variant. | ||||
| func (v Variant) String() string { | ||||
| 	return v.variant | ||||
| } | ||||
							
								
								
									
										396
									
								
								vendor/golang.org/x/text/language/lookup.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										396
									
								
								vendor/golang.org/x/text/language/lookup.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,396 @@ | ||||
| // Copyright 2013 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. | ||||
|  | ||||
| package language | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
|  | ||||
| 	"golang.org/x/text/internal/tag" | ||||
| ) | ||||
|  | ||||
| // findIndex tries to find the given tag in idx and returns a standardized error | ||||
| // if it could not be found. | ||||
| func findIndex(idx tag.Index, key []byte, form string) (index int, err error) { | ||||
| 	if !tag.FixCase(form, key) { | ||||
| 		return 0, errSyntax | ||||
| 	} | ||||
| 	i := idx.Index(key) | ||||
| 	if i == -1 { | ||||
| 		return 0, mkErrInvalid(key) | ||||
| 	} | ||||
| 	return i, nil | ||||
| } | ||||
|  | ||||
| func searchUint(imap []uint16, key uint16) int { | ||||
| 	return sort.Search(len(imap), func(i int) bool { | ||||
| 		return imap[i] >= key | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| type langID uint16 | ||||
|  | ||||
| // getLangID returns the langID of s if s is a canonical subtag | ||||
| // or langUnknown if s is not a canonical subtag. | ||||
| func getLangID(s []byte) (langID, error) { | ||||
| 	if len(s) == 2 { | ||||
| 		return getLangISO2(s) | ||||
| 	} | ||||
| 	return getLangISO3(s) | ||||
| } | ||||
|  | ||||
| // mapLang returns the mapped langID of id according to mapping m. | ||||
| func normLang(id langID) (langID, langAliasType) { | ||||
| 	k := sort.Search(len(langAliasMap), func(i int) bool { | ||||
| 		return langAliasMap[i].from >= uint16(id) | ||||
| 	}) | ||||
| 	if k < len(langAliasMap) && langAliasMap[k].from == uint16(id) { | ||||
| 		return langID(langAliasMap[k].to), langAliasTypes[k] | ||||
| 	} | ||||
| 	return id, langAliasTypeUnknown | ||||
| } | ||||
|  | ||||
| // getLangISO2 returns the langID for the given 2-letter ISO language code | ||||
| // or unknownLang if this does not exist. | ||||
| func getLangISO2(s []byte) (langID, error) { | ||||
| 	if !tag.FixCase("zz", s) { | ||||
| 		return 0, errSyntax | ||||
| 	} | ||||
| 	if i := lang.Index(s); i != -1 && lang.Elem(i)[3] != 0 { | ||||
| 		return langID(i), nil | ||||
| 	} | ||||
| 	return 0, mkErrInvalid(s) | ||||
| } | ||||
|  | ||||
| const base = 'z' - 'a' + 1 | ||||
|  | ||||
| func strToInt(s []byte) uint { | ||||
| 	v := uint(0) | ||||
| 	for i := 0; i < len(s); i++ { | ||||
| 		v *= base | ||||
| 		v += uint(s[i] - 'a') | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| // converts the given integer to the original ASCII string passed to strToInt. | ||||
| // len(s) must match the number of characters obtained. | ||||
| func intToStr(v uint, s []byte) { | ||||
| 	for i := len(s) - 1; i >= 0; i-- { | ||||
| 		s[i] = byte(v%base) + 'a' | ||||
| 		v /= base | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // getLangISO3 returns the langID for the given 3-letter ISO language code | ||||
| // or unknownLang if this does not exist. | ||||
| func getLangISO3(s []byte) (langID, error) { | ||||
| 	if tag.FixCase("und", s) { | ||||
| 		// first try to match canonical 3-letter entries | ||||
| 		for i := lang.Index(s[:2]); i != -1; i = lang.Next(s[:2], i) { | ||||
| 			if e := lang.Elem(i); e[3] == 0 && e[2] == s[2] { | ||||
| 				// We treat "und" as special and always translate it to "unspecified". | ||||
| 				// Note that ZZ and Zzzz are private use and are not treated as | ||||
| 				// unspecified by default. | ||||
| 				id := langID(i) | ||||
| 				if id == nonCanonicalUnd { | ||||
| 					return 0, nil | ||||
| 				} | ||||
| 				return id, nil | ||||
| 			} | ||||
| 		} | ||||
| 		if i := altLangISO3.Index(s); i != -1 { | ||||
| 			return langID(altLangIndex[altLangISO3.Elem(i)[3]]), nil | ||||
| 		} | ||||
| 		n := strToInt(s) | ||||
| 		if langNoIndex[n/8]&(1<<(n%8)) != 0 { | ||||
| 			return langID(n) + langNoIndexOffset, nil | ||||
| 		} | ||||
| 		// Check for non-canonical uses of ISO3. | ||||
| 		for i := lang.Index(s[:1]); i != -1; i = lang.Next(s[:1], i) { | ||||
| 			if e := lang.Elem(i); e[2] == s[1] && e[3] == s[2] { | ||||
| 				return langID(i), nil | ||||
| 			} | ||||
| 		} | ||||
| 		return 0, mkErrInvalid(s) | ||||
| 	} | ||||
| 	return 0, errSyntax | ||||
| } | ||||
|  | ||||
| // stringToBuf writes the string to b and returns the number of bytes | ||||
| // written.  cap(b) must be >= 3. | ||||
| func (id langID) stringToBuf(b []byte) int { | ||||
| 	if id >= langNoIndexOffset { | ||||
| 		intToStr(uint(id)-langNoIndexOffset, b[:3]) | ||||
| 		return 3 | ||||
| 	} else if id == 0 { | ||||
| 		return copy(b, "und") | ||||
| 	} | ||||
| 	l := lang[id<<2:] | ||||
| 	if l[3] == 0 { | ||||
| 		return copy(b, l[:3]) | ||||
| 	} | ||||
| 	return copy(b, l[:2]) | ||||
| } | ||||
|  | ||||
| // String returns the BCP 47 representation of the langID. | ||||
| // Use b as variable name, instead of id, to ensure the variable | ||||
| // used is consistent with that of Base in which this type is embedded. | ||||
| func (b langID) String() string { | ||||
| 	if b == 0 { | ||||
| 		return "und" | ||||
| 	} else if b >= langNoIndexOffset { | ||||
| 		b -= langNoIndexOffset | ||||
| 		buf := [3]byte{} | ||||
| 		intToStr(uint(b), buf[:]) | ||||
| 		return string(buf[:]) | ||||
| 	} | ||||
| 	l := lang.Elem(int(b)) | ||||
| 	if l[3] == 0 { | ||||
| 		return l[:3] | ||||
| 	} | ||||
| 	return l[:2] | ||||
| } | ||||
|  | ||||
| // ISO3 returns the ISO 639-3 language code. | ||||
| func (b langID) ISO3() string { | ||||
| 	if b == 0 || b >= langNoIndexOffset { | ||||
| 		return b.String() | ||||
| 	} | ||||
| 	l := lang.Elem(int(b)) | ||||
| 	if l[3] == 0 { | ||||
| 		return l[:3] | ||||
| 	} else if l[2] == 0 { | ||||
| 		return altLangISO3.Elem(int(l[3]))[:3] | ||||
| 	} | ||||
| 	// This allocation will only happen for 3-letter ISO codes | ||||
| 	// that are non-canonical BCP 47 language identifiers. | ||||
| 	return l[0:1] + l[2:4] | ||||
| } | ||||
|  | ||||
| // IsPrivateUse reports whether this language code is reserved for private use. | ||||
| func (b langID) IsPrivateUse() bool { | ||||
| 	return langPrivateStart <= b && b <= langPrivateEnd | ||||
| } | ||||
|  | ||||
| type regionID uint16 | ||||
|  | ||||
| // getRegionID returns the region id for s if s is a valid 2-letter region code | ||||
| // or unknownRegion. | ||||
| func getRegionID(s []byte) (regionID, error) { | ||||
| 	if len(s) == 3 { | ||||
| 		if isAlpha(s[0]) { | ||||
| 			return getRegionISO3(s) | ||||
| 		} | ||||
| 		if i, err := strconv.ParseUint(string(s), 10, 10); err == nil { | ||||
| 			return getRegionM49(int(i)) | ||||
| 		} | ||||
| 	} | ||||
| 	return getRegionISO2(s) | ||||
| } | ||||
|  | ||||
| // getRegionISO2 returns the regionID for the given 2-letter ISO country code | ||||
| // or unknownRegion if this does not exist. | ||||
| func getRegionISO2(s []byte) (regionID, error) { | ||||
| 	i, err := findIndex(regionISO, s, "ZZ") | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
| 	return regionID(i) + isoRegionOffset, nil | ||||
| } | ||||
|  | ||||
| // getRegionISO3 returns the regionID for the given 3-letter ISO country code | ||||
| // or unknownRegion if this does not exist. | ||||
| func getRegionISO3(s []byte) (regionID, error) { | ||||
| 	if tag.FixCase("ZZZ", s) { | ||||
| 		for i := regionISO.Index(s[:1]); i != -1; i = regionISO.Next(s[:1], i) { | ||||
| 			if e := regionISO.Elem(i); e[2] == s[1] && e[3] == s[2] { | ||||
| 				return regionID(i) + isoRegionOffset, nil | ||||
| 			} | ||||
| 		} | ||||
| 		for i := 0; i < len(altRegionISO3); i += 3 { | ||||
| 			if tag.Compare(altRegionISO3[i:i+3], s) == 0 { | ||||
| 				return regionID(altRegionIDs[i/3]), nil | ||||
| 			} | ||||
| 		} | ||||
| 		return 0, mkErrInvalid(s) | ||||
| 	} | ||||
| 	return 0, errSyntax | ||||
| } | ||||
|  | ||||
| func getRegionM49(n int) (regionID, error) { | ||||
| 	if 0 < n && n <= 999 { | ||||
| 		const ( | ||||
| 			searchBits = 7 | ||||
| 			regionBits = 9 | ||||
| 			regionMask = 1<<regionBits - 1 | ||||
| 		) | ||||
| 		idx := n >> searchBits | ||||
| 		buf := fromM49[m49Index[idx]:m49Index[idx+1]] | ||||
| 		val := uint16(n) << regionBits // we rely on bits shifting out | ||||
| 		i := sort.Search(len(buf), func(i int) bool { | ||||
| 			return buf[i] >= val | ||||
| 		}) | ||||
| 		if r := fromM49[int(m49Index[idx])+i]; r&^regionMask == val { | ||||
| 			return regionID(r & regionMask), nil | ||||
| 		} | ||||
| 	} | ||||
| 	var e ValueError | ||||
| 	fmt.Fprint(bytes.NewBuffer([]byte(e.v[:])), n) | ||||
| 	return 0, e | ||||
| } | ||||
|  | ||||
| // normRegion returns a region if r is deprecated or 0 otherwise. | ||||
| // TODO: consider supporting BYS (-> BLR), CSK (-> 200 or CZ), PHI (-> PHL) and AFI (-> DJ). | ||||
| // TODO: consider mapping split up regions to new most populous one (like CLDR). | ||||
| func normRegion(r regionID) regionID { | ||||
| 	m := regionOldMap | ||||
| 	k := sort.Search(len(m), func(i int) bool { | ||||
| 		return m[i].from >= uint16(r) | ||||
| 	}) | ||||
| 	if k < len(m) && m[k].from == uint16(r) { | ||||
| 		return regionID(m[k].to) | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	iso3166UserAssigned = 1 << iota | ||||
| 	ccTLD | ||||
| 	bcp47Region | ||||
| ) | ||||
|  | ||||
| func (r regionID) typ() byte { | ||||
| 	return regionTypes[r] | ||||
| } | ||||
|  | ||||
| // String returns the BCP 47 representation for the region. | ||||
| // It returns "ZZ" for an unspecified region. | ||||
| func (r regionID) String() string { | ||||
| 	if r < isoRegionOffset { | ||||
| 		if r == 0 { | ||||
| 			return "ZZ" | ||||
| 		} | ||||
| 		return fmt.Sprintf("%03d", r.M49()) | ||||
| 	} | ||||
| 	r -= isoRegionOffset | ||||
| 	return regionISO.Elem(int(r))[:2] | ||||
| } | ||||
|  | ||||
| // ISO3 returns the 3-letter ISO code of r. | ||||
| // Note that not all regions have a 3-letter ISO code. | ||||
| // In such cases this method returns "ZZZ". | ||||
| func (r regionID) ISO3() string { | ||||
| 	if r < isoRegionOffset { | ||||
| 		return "ZZZ" | ||||
| 	} | ||||
| 	r -= isoRegionOffset | ||||
| 	reg := regionISO.Elem(int(r)) | ||||
| 	switch reg[2] { | ||||
| 	case 0: | ||||
| 		return altRegionISO3[reg[3]:][:3] | ||||
| 	case ' ': | ||||
| 		return "ZZZ" | ||||
| 	} | ||||
| 	return reg[0:1] + reg[2:4] | ||||
| } | ||||
|  | ||||
| // M49 returns the UN M.49 encoding of r, or 0 if this encoding | ||||
| // is not defined for r. | ||||
| func (r regionID) M49() int { | ||||
| 	return int(m49[r]) | ||||
| } | ||||
|  | ||||
| // IsPrivateUse reports whether r has the ISO 3166 User-assigned status. This | ||||
| // may include private-use tags that are assigned by CLDR and used in this | ||||
| // implementation. So IsPrivateUse and IsCountry can be simultaneously true. | ||||
| func (r regionID) IsPrivateUse() bool { | ||||
| 	return r.typ()&iso3166UserAssigned != 0 | ||||
| } | ||||
|  | ||||
| type scriptID uint8 | ||||
|  | ||||
| // getScriptID returns the script id for string s. It assumes that s | ||||
| // is of the format [A-Z][a-z]{3}. | ||||
| func getScriptID(idx tag.Index, s []byte) (scriptID, error) { | ||||
| 	i, err := findIndex(idx, s, "Zzzz") | ||||
| 	return scriptID(i), err | ||||
| } | ||||
|  | ||||
| // String returns the script code in title case. | ||||
| // It returns "Zzzz" for an unspecified script. | ||||
| func (s scriptID) String() string { | ||||
| 	if s == 0 { | ||||
| 		return "Zzzz" | ||||
| 	} | ||||
| 	return script.Elem(int(s)) | ||||
| } | ||||
|  | ||||
| // IsPrivateUse reports whether this script code is reserved for private use. | ||||
| func (s scriptID) IsPrivateUse() bool { | ||||
| 	return _Qaaa <= s && s <= _Qabx | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	maxAltTaglen = len("en-US-POSIX") | ||||
| 	maxLen       = maxAltTaglen | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// grandfatheredMap holds a mapping from legacy and grandfathered tags to | ||||
| 	// their base language or index to more elaborate tag. | ||||
| 	grandfatheredMap = map[[maxLen]byte]int16{ | ||||
| 		[maxLen]byte{'a', 'r', 't', '-', 'l', 'o', 'j', 'b', 'a', 'n'}: _jbo, // art-lojban | ||||
| 		[maxLen]byte{'i', '-', 'a', 'm', 'i'}:                          _ami, // i-ami | ||||
| 		[maxLen]byte{'i', '-', 'b', 'n', 'n'}:                          _bnn, // i-bnn | ||||
| 		[maxLen]byte{'i', '-', 'h', 'a', 'k'}:                          _hak, // i-hak | ||||
| 		[maxLen]byte{'i', '-', 'k', 'l', 'i', 'n', 'g', 'o', 'n'}:      _tlh, // i-klingon | ||||
| 		[maxLen]byte{'i', '-', 'l', 'u', 'x'}:                          _lb,  // i-lux | ||||
| 		[maxLen]byte{'i', '-', 'n', 'a', 'v', 'a', 'j', 'o'}:           _nv,  // i-navajo | ||||
| 		[maxLen]byte{'i', '-', 'p', 'w', 'n'}:                          _pwn, // i-pwn | ||||
| 		[maxLen]byte{'i', '-', 't', 'a', 'o'}:                          _tao, // i-tao | ||||
| 		[maxLen]byte{'i', '-', 't', 'a', 'y'}:                          _tay, // i-tay | ||||
| 		[maxLen]byte{'i', '-', 't', 's', 'u'}:                          _tsu, // i-tsu | ||||
| 		[maxLen]byte{'n', 'o', '-', 'b', 'o', 'k'}:                     _nb,  // no-bok | ||||
| 		[maxLen]byte{'n', 'o', '-', 'n', 'y', 'n'}:                     _nn,  // no-nyn | ||||
| 		[maxLen]byte{'s', 'g', 'n', '-', 'b', 'e', '-', 'f', 'r'}:      _sfb, // sgn-BE-FR | ||||
| 		[maxLen]byte{'s', 'g', 'n', '-', 'b', 'e', '-', 'n', 'l'}:      _vgt, // sgn-BE-NL | ||||
| 		[maxLen]byte{'s', 'g', 'n', '-', 'c', 'h', '-', 'd', 'e'}:      _sgg, // sgn-CH-DE | ||||
| 		[maxLen]byte{'z', 'h', '-', 'g', 'u', 'o', 'y', 'u'}:           _cmn, // zh-guoyu | ||||
| 		[maxLen]byte{'z', 'h', '-', 'h', 'a', 'k', 'k', 'a'}:           _hak, // zh-hakka | ||||
| 		[maxLen]byte{'z', 'h', '-', 'm', 'i', 'n', '-', 'n', 'a', 'n'}: _nan, // zh-min-nan | ||||
| 		[maxLen]byte{'z', 'h', '-', 'x', 'i', 'a', 'n', 'g'}:           _hsn, // zh-xiang | ||||
|  | ||||
| 		// Grandfathered tags with no modern replacement will be converted as | ||||
| 		// follows: | ||||
| 		[maxLen]byte{'c', 'e', 'l', '-', 'g', 'a', 'u', 'l', 'i', 's', 'h'}: -1, // cel-gaulish | ||||
| 		[maxLen]byte{'e', 'n', '-', 'g', 'b', '-', 'o', 'e', 'd'}:           -2, // en-GB-oed | ||||
| 		[maxLen]byte{'i', '-', 'd', 'e', 'f', 'a', 'u', 'l', 't'}:           -3, // i-default | ||||
| 		[maxLen]byte{'i', '-', 'e', 'n', 'o', 'c', 'h', 'i', 'a', 'n'}:      -4, // i-enochian | ||||
| 		[maxLen]byte{'i', '-', 'm', 'i', 'n', 'g', 'o'}:                     -5, // i-mingo | ||||
| 		[maxLen]byte{'z', 'h', '-', 'm', 'i', 'n'}:                          -6, // zh-min | ||||
|  | ||||
| 		// CLDR-specific tag. | ||||
| 		[maxLen]byte{'r', 'o', 'o', 't'}:                                    0,  // root | ||||
| 		[maxLen]byte{'e', 'n', '-', 'u', 's', '-', 'p', 'o', 's', 'i', 'x'}: -7, // en_US_POSIX" | ||||
| 	} | ||||
|  | ||||
| 	altTagIndex = [...]uint8{0, 17, 31, 45, 61, 74, 86, 102} | ||||
|  | ||||
| 	altTags = "xtg-x-cel-gaulishen-GB-oxendicten-x-i-defaultund-x-i-enochiansee-x-i-mingonan-x-zh-minen-US-u-va-posix" | ||||
| ) | ||||
|  | ||||
| func grandfathered(s [maxAltTaglen]byte) (t Tag, ok bool) { | ||||
| 	if v, ok := grandfatheredMap[s]; ok { | ||||
| 		if v < 0 { | ||||
| 			return Make(altTags[altTagIndex[-v-1]:altTagIndex[-v]]), true | ||||
| 		} | ||||
| 		t.lang = langID(v) | ||||
| 		return t, true | ||||
| 	} | ||||
| 	return t, false | ||||
| } | ||||
							
								
								
									
										933
									
								
								vendor/golang.org/x/text/language/match.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										933
									
								
								vendor/golang.org/x/text/language/match.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,933 @@ | ||||
| // Copyright 2013 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. | ||||
|  | ||||
| package language | ||||
|  | ||||
| import "errors" | ||||
|  | ||||
| // A MatchOption configures a Matcher. | ||||
| type MatchOption func(*matcher) | ||||
|  | ||||
| // PreferSameScript will, in the absence of a match, result in the first | ||||
| // preferred tag with the same script as a supported tag to match this supported | ||||
| // tag. The default is currently true, but this may change in the future. | ||||
| func PreferSameScript(preferSame bool) MatchOption { | ||||
| 	return func(m *matcher) { m.preferSameScript = preferSame } | ||||
| } | ||||
|  | ||||
| // TODO(v1.0.0): consider making Matcher a concrete type, instead of interface. | ||||
| // There doesn't seem to be too much need for multiple types. | ||||
| // Making it a concrete type allows MatchStrings to be a method, which will | ||||
| // improve its discoverability. | ||||
|  | ||||
| // MatchStrings parses and matches the given strings until one of them matches | ||||
| // the language in the Matcher. A string may be an Accept-Language header as | ||||
| // handled by ParseAcceptLanguage. The default language is returned if no | ||||
| // other language matched. | ||||
| func MatchStrings(m Matcher, lang ...string) (tag Tag, index int) { | ||||
| 	for _, accept := range lang { | ||||
| 		desired, _, err := ParseAcceptLanguage(accept) | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		if tag, index, conf := m.Match(desired...); conf != No { | ||||
| 			return tag, index | ||||
| 		} | ||||
| 	} | ||||
| 	tag, index, _ = m.Match() | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // Matcher is the interface that wraps the Match method. | ||||
| // | ||||
| // Match returns the best match for any of the given tags, along with | ||||
| // a unique index associated with the returned tag and a confidence | ||||
| // score. | ||||
| type Matcher interface { | ||||
| 	Match(t ...Tag) (tag Tag, index int, c Confidence) | ||||
| } | ||||
|  | ||||
| // Comprehends reports the confidence score for a speaker of a given language | ||||
| // to being able to comprehend the written form of an alternative language. | ||||
| func Comprehends(speaker, alternative Tag) Confidence { | ||||
| 	_, _, c := NewMatcher([]Tag{alternative}).Match(speaker) | ||||
| 	return c | ||||
| } | ||||
|  | ||||
| // NewMatcher returns a Matcher that matches an ordered list of preferred tags | ||||
| // against a list of supported tags based on written intelligibility, closeness | ||||
| // of dialect, equivalence of subtags and various other rules. It is initialized | ||||
| // with the list of supported tags. The first element is used as the default | ||||
| // value in case no match is found. | ||||
| // | ||||
| // Its Match method matches the first of the given Tags to reach a certain | ||||
| // confidence threshold. The tags passed to Match should therefore be specified | ||||
| // in order of preference. Extensions are ignored for matching. | ||||
| // | ||||
| // The index returned by the Match method corresponds to the index of the | ||||
| // matched tag in t, but is augmented with the Unicode extension ('u')of the | ||||
| // corresponding preferred tag. This allows user locale options to be passed | ||||
| // transparently. | ||||
| func NewMatcher(t []Tag, options ...MatchOption) Matcher { | ||||
| 	return newMatcher(t, options) | ||||
| } | ||||
|  | ||||
| func (m *matcher) Match(want ...Tag) (t Tag, index int, c Confidence) { | ||||
| 	match, w, c := m.getBest(want...) | ||||
| 	if match != nil { | ||||
| 		t, index = match.tag, match.index | ||||
| 	} else { | ||||
| 		// TODO: this should be an option | ||||
| 		t = m.default_.tag | ||||
| 		if m.preferSameScript { | ||||
| 		outer: | ||||
| 			for _, w := range want { | ||||
| 				script, _ := w.Script() | ||||
| 				if script.scriptID == 0 { | ||||
| 					// Don't do anything if there is no script, such as with | ||||
| 					// private subtags. | ||||
| 					continue | ||||
| 				} | ||||
| 				for i, h := range m.supported { | ||||
| 					if script.scriptID == h.maxScript { | ||||
| 						t, index = h.tag, i | ||||
| 						break outer | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		// TODO: select first language tag based on script. | ||||
| 	} | ||||
| 	if w.region != 0 && t.region != 0 && t.region.contains(w.region) { | ||||
| 		t, _ = Raw.Compose(t, Region{w.region}) | ||||
| 	} | ||||
| 	// Copy options from the user-provided tag into the result tag. This is hard | ||||
| 	// to do after the fact, so we do it here. | ||||
| 	// TODO: add in alternative variants to -u-va-. | ||||
| 	// TODO: add preferred region to -u-rg-. | ||||
| 	if e := w.Extensions(); len(e) > 0 { | ||||
| 		t, _ = Raw.Compose(t, e) | ||||
| 	} | ||||
| 	return t, index, c | ||||
| } | ||||
|  | ||||
| type scriptRegionFlags uint8 | ||||
|  | ||||
| const ( | ||||
| 	isList = 1 << iota | ||||
| 	scriptInFrom | ||||
| 	regionInFrom | ||||
| ) | ||||
|  | ||||
| func (t *Tag) setUndefinedLang(id langID) { | ||||
| 	if t.lang == 0 { | ||||
| 		t.lang = id | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (t *Tag) setUndefinedScript(id scriptID) { | ||||
| 	if t.script == 0 { | ||||
| 		t.script = id | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (t *Tag) setUndefinedRegion(id regionID) { | ||||
| 	if t.region == 0 || t.region.contains(id) { | ||||
| 		t.region = id | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ErrMissingLikelyTagsData indicates no information was available | ||||
| // to compute likely values of missing tags. | ||||
| var ErrMissingLikelyTagsData = errors.New("missing likely tags data") | ||||
|  | ||||
| // addLikelySubtags sets subtags to their most likely value, given the locale. | ||||
| // In most cases this means setting fields for unknown values, but in some | ||||
| // cases it may alter a value.  It returns an ErrMissingLikelyTagsData error | ||||
| // if the given locale cannot be expanded. | ||||
| func (t Tag) addLikelySubtags() (Tag, error) { | ||||
| 	id, err := addTags(t) | ||||
| 	if err != nil { | ||||
| 		return t, err | ||||
| 	} else if id.equalTags(t) { | ||||
| 		return t, nil | ||||
| 	} | ||||
| 	id.remakeString() | ||||
| 	return id, nil | ||||
| } | ||||
|  | ||||
| // specializeRegion attempts to specialize a group region. | ||||
| func specializeRegion(t *Tag) bool { | ||||
| 	if i := regionInclusion[t.region]; i < nRegionGroups { | ||||
| 		x := likelyRegionGroup[i] | ||||
| 		if langID(x.lang) == t.lang && scriptID(x.script) == t.script { | ||||
| 			t.region = regionID(x.region) | ||||
| 		} | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func addTags(t Tag) (Tag, error) { | ||||
| 	// We leave private use identifiers alone. | ||||
| 	if t.private() { | ||||
| 		return t, nil | ||||
| 	} | ||||
| 	if t.script != 0 && t.region != 0 { | ||||
| 		if t.lang != 0 { | ||||
| 			// already fully specified | ||||
| 			specializeRegion(&t) | ||||
| 			return t, nil | ||||
| 		} | ||||
| 		// Search matches for und-script-region. Note that for these cases | ||||
| 		// region will never be a group so there is no need to check for this. | ||||
| 		list := likelyRegion[t.region : t.region+1] | ||||
| 		if x := list[0]; x.flags&isList != 0 { | ||||
| 			list = likelyRegionList[x.lang : x.lang+uint16(x.script)] | ||||
| 		} | ||||
| 		for _, x := range list { | ||||
| 			// Deviating from the spec. See match_test.go for details. | ||||
| 			if scriptID(x.script) == t.script { | ||||
| 				t.setUndefinedLang(langID(x.lang)) | ||||
| 				return t, nil | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if t.lang != 0 { | ||||
| 		// Search matches for lang-script and lang-region, where lang != und. | ||||
| 		if t.lang < langNoIndexOffset { | ||||
| 			x := likelyLang[t.lang] | ||||
| 			if x.flags&isList != 0 { | ||||
| 				list := likelyLangList[x.region : x.region+uint16(x.script)] | ||||
| 				if t.script != 0 { | ||||
| 					for _, x := range list { | ||||
| 						if scriptID(x.script) == t.script && x.flags&scriptInFrom != 0 { | ||||
| 							t.setUndefinedRegion(regionID(x.region)) | ||||
| 							return t, nil | ||||
| 						} | ||||
| 					} | ||||
| 				} else if t.region != 0 { | ||||
| 					count := 0 | ||||
| 					goodScript := true | ||||
| 					tt := t | ||||
| 					for _, x := range list { | ||||
| 						// We visit all entries for which the script was not | ||||
| 						// defined, including the ones where the region was not | ||||
| 						// defined. This allows for proper disambiguation within | ||||
| 						// regions. | ||||
| 						if x.flags&scriptInFrom == 0 && t.region.contains(regionID(x.region)) { | ||||
| 							tt.region = regionID(x.region) | ||||
| 							tt.setUndefinedScript(scriptID(x.script)) | ||||
| 							goodScript = goodScript && tt.script == scriptID(x.script) | ||||
| 							count++ | ||||
| 						} | ||||
| 					} | ||||
| 					if count == 1 { | ||||
| 						return tt, nil | ||||
| 					} | ||||
| 					// Even if we fail to find a unique Region, we might have | ||||
| 					// an unambiguous script. | ||||
| 					if goodScript { | ||||
| 						t.script = tt.script | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		// Search matches for und-script. | ||||
| 		if t.script != 0 { | ||||
| 			x := likelyScript[t.script] | ||||
| 			if x.region != 0 { | ||||
| 				t.setUndefinedRegion(regionID(x.region)) | ||||
| 				t.setUndefinedLang(langID(x.lang)) | ||||
| 				return t, nil | ||||
| 			} | ||||
| 		} | ||||
| 		// Search matches for und-region. If und-script-region exists, it would | ||||
| 		// have been found earlier. | ||||
| 		if t.region != 0 { | ||||
| 			if i := regionInclusion[t.region]; i < nRegionGroups { | ||||
| 				x := likelyRegionGroup[i] | ||||
| 				if x.region != 0 { | ||||
| 					t.setUndefinedLang(langID(x.lang)) | ||||
| 					t.setUndefinedScript(scriptID(x.script)) | ||||
| 					t.region = regionID(x.region) | ||||
| 				} | ||||
| 			} else { | ||||
| 				x := likelyRegion[t.region] | ||||
| 				if x.flags&isList != 0 { | ||||
| 					x = likelyRegionList[x.lang] | ||||
| 				} | ||||
| 				if x.script != 0 && x.flags != scriptInFrom { | ||||
| 					t.setUndefinedLang(langID(x.lang)) | ||||
| 					t.setUndefinedScript(scriptID(x.script)) | ||||
| 					return t, nil | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Search matches for lang. | ||||
| 	if t.lang < langNoIndexOffset { | ||||
| 		x := likelyLang[t.lang] | ||||
| 		if x.flags&isList != 0 { | ||||
| 			x = likelyLangList[x.region] | ||||
| 		} | ||||
| 		if x.region != 0 { | ||||
| 			t.setUndefinedScript(scriptID(x.script)) | ||||
| 			t.setUndefinedRegion(regionID(x.region)) | ||||
| 		} | ||||
| 		specializeRegion(&t) | ||||
| 		if t.lang == 0 { | ||||
| 			t.lang = _en // default language | ||||
| 		} | ||||
| 		return t, nil | ||||
| 	} | ||||
| 	return t, ErrMissingLikelyTagsData | ||||
| } | ||||
|  | ||||
| func (t *Tag) setTagsFrom(id Tag) { | ||||
| 	t.lang = id.lang | ||||
| 	t.script = id.script | ||||
| 	t.region = id.region | ||||
| } | ||||
|  | ||||
| // minimize removes the region or script subtags from t such that | ||||
| // t.addLikelySubtags() == t.minimize().addLikelySubtags(). | ||||
| func (t Tag) minimize() (Tag, error) { | ||||
| 	t, err := minimizeTags(t) | ||||
| 	if err != nil { | ||||
| 		return t, err | ||||
| 	} | ||||
| 	t.remakeString() | ||||
| 	return t, nil | ||||
| } | ||||
|  | ||||
| // minimizeTags mimics the behavior of the ICU 51 C implementation. | ||||
| func minimizeTags(t Tag) (Tag, error) { | ||||
| 	if t.equalTags(und) { | ||||
| 		return t, nil | ||||
| 	} | ||||
| 	max, err := addTags(t) | ||||
| 	if err != nil { | ||||
| 		return t, err | ||||
| 	} | ||||
| 	for _, id := range [...]Tag{ | ||||
| 		{lang: t.lang}, | ||||
| 		{lang: t.lang, region: t.region}, | ||||
| 		{lang: t.lang, script: t.script}, | ||||
| 	} { | ||||
| 		if x, err := addTags(id); err == nil && max.equalTags(x) { | ||||
| 			t.setTagsFrom(id) | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return t, nil | ||||
| } | ||||
|  | ||||
| // Tag Matching | ||||
| // CLDR defines an algorithm for finding the best match between two sets of language | ||||
| // tags. The basic algorithm defines how to score a possible match and then find | ||||
| // the match with the best score | ||||
| // (see http://www.unicode.org/reports/tr35/#LanguageMatching). | ||||
| // Using scoring has several disadvantages. The scoring obfuscates the importance of | ||||
| // the various factors considered, making the algorithm harder to understand. Using | ||||
| // scoring also requires the full score to be computed for each pair of tags. | ||||
| // | ||||
| // We will use a different algorithm which aims to have the following properties: | ||||
| // - clarity on the precedence of the various selection factors, and | ||||
| // - improved performance by allowing early termination of a comparison. | ||||
| // | ||||
| // Matching algorithm (overview) | ||||
| // Input: | ||||
| //   - supported: a set of supported tags | ||||
| //   - default:   the default tag to return in case there is no match | ||||
| //   - desired:   list of desired tags, ordered by preference, starting with | ||||
| //                the most-preferred. | ||||
| // | ||||
| // Algorithm: | ||||
| //   1) Set the best match to the lowest confidence level | ||||
| //   2) For each tag in "desired": | ||||
| //     a) For each tag in "supported": | ||||
| //        1) compute the match between the two tags. | ||||
| //        2) if the match is better than the previous best match, replace it | ||||
| //           with the new match. (see next section) | ||||
| //     b) if the current best match is Exact and pin is true the result will be | ||||
| //        frozen to the language found thusfar, although better matches may | ||||
| //        still be found for the same language. | ||||
| //   3) If the best match so far is below a certain threshold, return "default". | ||||
| // | ||||
| // Ranking: | ||||
| // We use two phases to determine whether one pair of tags are a better match | ||||
| // than another pair of tags. First, we determine a rough confidence level. If the | ||||
| // levels are different, the one with the highest confidence wins. | ||||
| // Second, if the rough confidence levels are identical, we use a set of tie-breaker | ||||
| // rules. | ||||
| // | ||||
| // The confidence level of matching a pair of tags is determined by finding the | ||||
| // lowest confidence level of any matches of the corresponding subtags (the | ||||
| // result is deemed as good as its weakest link). | ||||
| // We define the following levels: | ||||
| //   Exact    - An exact match of a subtag, before adding likely subtags. | ||||
| //   MaxExact - An exact match of a subtag, after adding likely subtags. | ||||
| //              [See Note 2]. | ||||
| //   High     - High level of mutual intelligibility between different subtag | ||||
| //              variants. | ||||
| //   Low      - Low level of mutual intelligibility between different subtag | ||||
| //              variants. | ||||
| //   No       - No mutual intelligibility. | ||||
| // | ||||
| // The following levels can occur for each type of subtag: | ||||
| //   Base:    Exact, MaxExact, High, Low, No | ||||
| //   Script:  Exact, MaxExact [see Note 3], Low, No | ||||
| //   Region:  Exact, MaxExact, High | ||||
| //   Variant: Exact, High | ||||
| //   Private: Exact, No | ||||
| // | ||||
| // Any result with a confidence level of Low or higher is deemed a possible match. | ||||
| // Once a desired tag matches any of the supported tags with a level of MaxExact | ||||
| // or higher, the next desired tag is not considered (see Step 2.b). | ||||
| // Note that CLDR provides languageMatching data that defines close equivalence | ||||
| // classes for base languages, scripts and regions. | ||||
| // | ||||
| // Tie-breaking | ||||
| // If we get the same confidence level for two matches, we apply a sequence of | ||||
| // tie-breaking rules. The first that succeeds defines the result. The rules are | ||||
| // applied in the following order. | ||||
| //   1) Original language was defined and was identical. | ||||
| //   2) Original region was defined and was identical. | ||||
| //   3) Distance between two maximized regions was the smallest. | ||||
| //   4) Original script was defined and was identical. | ||||
| //   5) Distance from want tag to have tag using the parent relation [see Note 5.] | ||||
| // If there is still no winner after these rules are applied, the first match | ||||
| // found wins. | ||||
| // | ||||
| // Notes: | ||||
| // [2] In practice, as matching of Exact is done in a separate phase from | ||||
| //     matching the other levels, we reuse the Exact level to mean MaxExact in | ||||
| //     the second phase. As a consequence, we only need the levels defined by | ||||
| //     the Confidence type. The MaxExact confidence level is mapped to High in | ||||
| //     the public API. | ||||
| // [3] We do not differentiate between maximized script values that were derived | ||||
| //     from suppressScript versus most likely tag data. We determined that in | ||||
| //     ranking the two, one ranks just after the other. Moreover, the two cannot | ||||
| //     occur concurrently. As a consequence, they are identical for practical | ||||
| //     purposes. | ||||
| // [4] In case of deprecated, macro-equivalents and legacy mappings, we assign | ||||
| //     the MaxExact level to allow iw vs he to still be a closer match than | ||||
| //     en-AU vs en-US, for example. | ||||
| // [5] In CLDR a locale inherits fields that are unspecified for this locale | ||||
| //     from its parent. Therefore, if a locale is a parent of another locale, | ||||
| //     it is a strong measure for closeness, especially when no other tie | ||||
| //     breaker rule applies. One could also argue it is inconsistent, for | ||||
| //     example, when pt-AO matches pt (which CLDR equates with pt-BR), even | ||||
| //     though its parent is pt-PT according to the inheritance rules. | ||||
| // | ||||
| // Implementation Details: | ||||
| // There are several performance considerations worth pointing out. Most notably, | ||||
| // we preprocess as much as possible (within reason) at the time of creation of a | ||||
| // matcher. This includes: | ||||
| //   - creating a per-language map, which includes data for the raw base language | ||||
| //     and its canonicalized variant (if applicable), | ||||
| //   - expanding entries for the equivalence classes defined in CLDR's | ||||
| //     languageMatch data. | ||||
| // The per-language map ensures that typically only a very small number of tags | ||||
| // need to be considered. The pre-expansion of canonicalized subtags and | ||||
| // equivalence classes reduces the amount of map lookups that need to be done at | ||||
| // runtime. | ||||
|  | ||||
| // matcher keeps a set of supported language tags, indexed by language. | ||||
| type matcher struct { | ||||
| 	default_         *haveTag | ||||
| 	supported        []*haveTag | ||||
| 	index            map[langID]*matchHeader | ||||
| 	passSettings     bool | ||||
| 	preferSameScript bool | ||||
| } | ||||
|  | ||||
| // matchHeader has the lists of tags for exact matches and matches based on | ||||
| // maximized and canonicalized tags for a given language. | ||||
| type matchHeader struct { | ||||
| 	haveTags []*haveTag | ||||
| 	original bool | ||||
| } | ||||
|  | ||||
| // haveTag holds a supported Tag and its maximized script and region. The maximized | ||||
| // or canonicalized language is not stored as it is not needed during matching. | ||||
| type haveTag struct { | ||||
| 	tag Tag | ||||
|  | ||||
| 	// index of this tag in the original list of supported tags. | ||||
| 	index int | ||||
|  | ||||
| 	// conf is the maximum confidence that can result from matching this haveTag. | ||||
| 	// When conf < Exact this means it was inserted after applying a CLDR equivalence rule. | ||||
| 	conf Confidence | ||||
|  | ||||
| 	// Maximized region and script. | ||||
| 	maxRegion regionID | ||||
| 	maxScript scriptID | ||||
|  | ||||
| 	// altScript may be checked as an alternative match to maxScript. If altScript | ||||
| 	// matches, the confidence level for this match is Low. Theoretically there | ||||
| 	// could be multiple alternative scripts. This does not occur in practice. | ||||
| 	altScript scriptID | ||||
|  | ||||
| 	// nextMax is the index of the next haveTag with the same maximized tags. | ||||
| 	nextMax uint16 | ||||
| } | ||||
|  | ||||
| func makeHaveTag(tag Tag, index int) (haveTag, langID) { | ||||
| 	max := tag | ||||
| 	if tag.lang != 0 || tag.region != 0 || tag.script != 0 { | ||||
| 		max, _ = max.canonicalize(All) | ||||
| 		max, _ = addTags(max) | ||||
| 		max.remakeString() | ||||
| 	} | ||||
| 	return haveTag{tag, index, Exact, max.region, max.script, altScript(max.lang, max.script), 0}, max.lang | ||||
| } | ||||
|  | ||||
| // altScript returns an alternative script that may match the given script with | ||||
| // a low confidence.  At the moment, the langMatch data allows for at most one | ||||
| // script to map to another and we rely on this to keep the code simple. | ||||
| func altScript(l langID, s scriptID) scriptID { | ||||
| 	for _, alt := range matchScript { | ||||
| 		// TODO: also match cases where language is not the same. | ||||
| 		if (langID(alt.wantLang) == l || langID(alt.haveLang) == l) && | ||||
| 			scriptID(alt.haveScript) == s { | ||||
| 			return scriptID(alt.wantScript) | ||||
| 		} | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| // addIfNew adds a haveTag to the list of tags only if it is a unique tag. | ||||
| // Tags that have the same maximized values are linked by index. | ||||
| func (h *matchHeader) addIfNew(n haveTag, exact bool) { | ||||
| 	h.original = h.original || exact | ||||
| 	// Don't add new exact matches. | ||||
| 	for _, v := range h.haveTags { | ||||
| 		if v.tag.equalsRest(n.tag) { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	// Allow duplicate maximized tags, but create a linked list to allow quickly | ||||
| 	// comparing the equivalents and bail out. | ||||
| 	for i, v := range h.haveTags { | ||||
| 		if v.maxScript == n.maxScript && | ||||
| 			v.maxRegion == n.maxRegion && | ||||
| 			v.tag.variantOrPrivateTagStr() == n.tag.variantOrPrivateTagStr() { | ||||
| 			for h.haveTags[i].nextMax != 0 { | ||||
| 				i = int(h.haveTags[i].nextMax) | ||||
| 			} | ||||
| 			h.haveTags[i].nextMax = uint16(len(h.haveTags)) | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	h.haveTags = append(h.haveTags, &n) | ||||
| } | ||||
|  | ||||
| // header returns the matchHeader for the given language. It creates one if | ||||
| // it doesn't already exist. | ||||
| func (m *matcher) header(l langID) *matchHeader { | ||||
| 	if h := m.index[l]; h != nil { | ||||
| 		return h | ||||
| 	} | ||||
| 	h := &matchHeader{} | ||||
| 	m.index[l] = h | ||||
| 	return h | ||||
| } | ||||
|  | ||||
| func toConf(d uint8) Confidence { | ||||
| 	if d <= 10 { | ||||
| 		return High | ||||
| 	} | ||||
| 	if d < 30 { | ||||
| 		return Low | ||||
| 	} | ||||
| 	return No | ||||
| } | ||||
|  | ||||
| // newMatcher builds an index for the given supported tags and returns it as | ||||
| // a matcher. It also expands the index by considering various equivalence classes | ||||
| // for a given tag. | ||||
| func newMatcher(supported []Tag, options []MatchOption) *matcher { | ||||
| 	m := &matcher{ | ||||
| 		index:            make(map[langID]*matchHeader), | ||||
| 		preferSameScript: true, | ||||
| 	} | ||||
| 	for _, o := range options { | ||||
| 		o(m) | ||||
| 	} | ||||
| 	if len(supported) == 0 { | ||||
| 		m.default_ = &haveTag{} | ||||
| 		return m | ||||
| 	} | ||||
| 	// Add supported languages to the index. Add exact matches first to give | ||||
| 	// them precedence. | ||||
| 	for i, tag := range supported { | ||||
| 		pair, _ := makeHaveTag(tag, i) | ||||
| 		m.header(tag.lang).addIfNew(pair, true) | ||||
| 		m.supported = append(m.supported, &pair) | ||||
| 	} | ||||
| 	m.default_ = m.header(supported[0].lang).haveTags[0] | ||||
| 	// Keep these in two different loops to support the case that two equivalent | ||||
| 	// languages are distinguished, such as iw and he. | ||||
| 	for i, tag := range supported { | ||||
| 		pair, max := makeHaveTag(tag, i) | ||||
| 		if max != tag.lang { | ||||
| 			m.header(max).addIfNew(pair, true) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// update is used to add indexes in the map for equivalent languages. | ||||
| 	// update will only add entries to original indexes, thus not computing any | ||||
| 	// transitive relations. | ||||
| 	update := func(want, have uint16, conf Confidence) { | ||||
| 		if hh := m.index[langID(have)]; hh != nil { | ||||
| 			if !hh.original { | ||||
| 				return | ||||
| 			} | ||||
| 			hw := m.header(langID(want)) | ||||
| 			for _, ht := range hh.haveTags { | ||||
| 				v := *ht | ||||
| 				if conf < v.conf { | ||||
| 					v.conf = conf | ||||
| 				} | ||||
| 				v.nextMax = 0 // this value needs to be recomputed | ||||
| 				if v.altScript != 0 { | ||||
| 					v.altScript = altScript(langID(want), v.maxScript) | ||||
| 				} | ||||
| 				hw.addIfNew(v, conf == Exact && hh.original) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Add entries for languages with mutual intelligibility as defined by CLDR's | ||||
| 	// languageMatch data. | ||||
| 	for _, ml := range matchLang { | ||||
| 		update(ml.want, ml.have, toConf(ml.distance)) | ||||
| 		if !ml.oneway { | ||||
| 			update(ml.have, ml.want, toConf(ml.distance)) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Add entries for possible canonicalizations. This is an optimization to | ||||
| 	// ensure that only one map lookup needs to be done at runtime per desired tag. | ||||
| 	// First we match deprecated equivalents. If they are perfect equivalents | ||||
| 	// (their canonicalization simply substitutes a different language code, but | ||||
| 	// nothing else), the match confidence is Exact, otherwise it is High. | ||||
| 	for i, lm := range langAliasMap { | ||||
| 		// If deprecated codes match and there is no fiddling with the script or | ||||
| 		// or region, we consider it an exact match. | ||||
| 		conf := Exact | ||||
| 		if langAliasTypes[i] != langMacro { | ||||
| 			if !isExactEquivalent(langID(lm.from)) { | ||||
| 				conf = High | ||||
| 			} | ||||
| 			update(lm.to, lm.from, conf) | ||||
| 		} | ||||
| 		update(lm.from, lm.to, conf) | ||||
| 	} | ||||
| 	return m | ||||
| } | ||||
|  | ||||
| // getBest gets the best matching tag in m for any of the given tags, taking into | ||||
| // account the order of preference of the given tags. | ||||
| func (m *matcher) getBest(want ...Tag) (got *haveTag, orig Tag, c Confidence) { | ||||
| 	best := bestMatch{} | ||||
| 	for i, w := range want { | ||||
| 		var max Tag | ||||
| 		// Check for exact match first. | ||||
| 		h := m.index[w.lang] | ||||
| 		if w.lang != 0 { | ||||
| 			if h == nil { | ||||
| 				continue | ||||
| 			} | ||||
| 			// Base language is defined. | ||||
| 			max, _ = w.canonicalize(Legacy | Deprecated | Macro) | ||||
| 			// A region that is added through canonicalization is stronger than | ||||
| 			// a maximized region: set it in the original (e.g. mo -> ro-MD). | ||||
| 			if w.region != max.region { | ||||
| 				w.region = max.region | ||||
| 			} | ||||
| 			// TODO: should we do the same for scripts? | ||||
| 			// See test case: en, sr, nl ; sh ; sr | ||||
| 			max, _ = addTags(max) | ||||
| 		} else { | ||||
| 			// Base language is not defined. | ||||
| 			if h != nil { | ||||
| 				for i := range h.haveTags { | ||||
| 					have := h.haveTags[i] | ||||
| 					if have.tag.equalsRest(w) { | ||||
| 						return have, w, Exact | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			if w.script == 0 && w.region == 0 { | ||||
| 				// We skip all tags matching und for approximate matching, including | ||||
| 				// private tags. | ||||
| 				continue | ||||
| 			} | ||||
| 			max, _ = addTags(w) | ||||
| 			if h = m.index[max.lang]; h == nil { | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
| 		pin := true | ||||
| 		for _, t := range want[i+1:] { | ||||
| 			if w.lang == t.lang { | ||||
| 				pin = false | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		// Check for match based on maximized tag. | ||||
| 		for i := range h.haveTags { | ||||
| 			have := h.haveTags[i] | ||||
| 			best.update(have, w, max.script, max.region, pin) | ||||
| 			if best.conf == Exact { | ||||
| 				for have.nextMax != 0 { | ||||
| 					have = h.haveTags[have.nextMax] | ||||
| 					best.update(have, w, max.script, max.region, pin) | ||||
| 				} | ||||
| 				return best.have, best.want, best.conf | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if best.conf <= No { | ||||
| 		if len(want) != 0 { | ||||
| 			return nil, want[0], No | ||||
| 		} | ||||
| 		return nil, Tag{}, No | ||||
| 	} | ||||
| 	return best.have, best.want, best.conf | ||||
| } | ||||
|  | ||||
| // bestMatch accumulates the best match so far. | ||||
| type bestMatch struct { | ||||
| 	have            *haveTag | ||||
| 	want            Tag | ||||
| 	conf            Confidence | ||||
| 	pinnedRegion    regionID | ||||
| 	pinLanguage     bool | ||||
| 	sameRegionGroup bool | ||||
| 	// Cached results from applying tie-breaking rules. | ||||
| 	origLang     bool | ||||
| 	origReg      bool | ||||
| 	paradigmReg  bool | ||||
| 	regGroupDist uint8 | ||||
| 	origScript   bool | ||||
| } | ||||
|  | ||||
| // update updates the existing best match if the new pair is considered to be a | ||||
| // better match. To determine if the given pair is a better match, it first | ||||
| // computes the rough confidence level. If this surpasses the current match, it | ||||
| // will replace it and update the tie-breaker rule cache. If there is a tie, it | ||||
| // proceeds with applying a series of tie-breaker rules. If there is no | ||||
| // conclusive winner after applying the tie-breaker rules, it leaves the current | ||||
| // match as the preferred match. | ||||
| // | ||||
| // If pin is true and have and tag are a strong match, it will henceforth only | ||||
| // consider matches for this language. This corresponds to the nothing that most | ||||
| // users have a strong preference for the first defined language. A user can | ||||
| // still prefer a second language over a dialect of the preferred language by | ||||
| // explicitly specifying dialects, e.g. "en, nl, en-GB". In this case pin should | ||||
| // be false. | ||||
| func (m *bestMatch) update(have *haveTag, tag Tag, maxScript scriptID, maxRegion regionID, pin bool) { | ||||
| 	// Bail if the maximum attainable confidence is below that of the current best match. | ||||
| 	c := have.conf | ||||
| 	if c < m.conf { | ||||
| 		return | ||||
| 	} | ||||
| 	// Don't change the language once we already have found an exact match. | ||||
| 	if m.pinLanguage && tag.lang != m.want.lang { | ||||
| 		return | ||||
| 	} | ||||
| 	// Pin the region group if we are comparing tags for the same language. | ||||
| 	if tag.lang == m.want.lang && m.sameRegionGroup { | ||||
| 		_, sameGroup := regionGroupDist(m.pinnedRegion, have.maxRegion, have.maxScript, m.want.lang) | ||||
| 		if !sameGroup { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	if c == Exact && have.maxScript == maxScript { | ||||
| 		// If there is another language and then another entry of this language, | ||||
| 		// don't pin anything, otherwise pin the language. | ||||
| 		m.pinLanguage = pin | ||||
| 	} | ||||
| 	if have.tag.equalsRest(tag) { | ||||
| 	} else if have.maxScript != maxScript { | ||||
| 		// There is usually very little comprehension between different scripts. | ||||
| 		// In a few cases there may still be Low comprehension. This possibility | ||||
| 		// is pre-computed and stored in have.altScript. | ||||
| 		if Low < m.conf || have.altScript != maxScript { | ||||
| 			return | ||||
| 		} | ||||
| 		c = Low | ||||
| 	} else if have.maxRegion != maxRegion { | ||||
| 		if High < c { | ||||
| 			// There is usually a small difference between languages across regions. | ||||
| 			c = High | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// We store the results of the computations of the tie-breaker rules along | ||||
| 	// with the best match. There is no need to do the checks once we determine | ||||
| 	// we have a winner, but we do still need to do the tie-breaker computations. | ||||
| 	// We use "beaten" to keep track if we still need to do the checks. | ||||
| 	beaten := false // true if the new pair defeats the current one. | ||||
| 	if c != m.conf { | ||||
| 		if c < m.conf { | ||||
| 			return | ||||
| 		} | ||||
| 		beaten = true | ||||
| 	} | ||||
|  | ||||
| 	// Tie-breaker rules: | ||||
| 	// We prefer if the pre-maximized language was specified and identical. | ||||
| 	origLang := have.tag.lang == tag.lang && tag.lang != 0 | ||||
| 	if !beaten && m.origLang != origLang { | ||||
| 		if m.origLang { | ||||
| 			return | ||||
| 		} | ||||
| 		beaten = true | ||||
| 	} | ||||
|  | ||||
| 	// We prefer if the pre-maximized region was specified and identical. | ||||
| 	origReg := have.tag.region == tag.region && tag.region != 0 | ||||
| 	if !beaten && m.origReg != origReg { | ||||
| 		if m.origReg { | ||||
| 			return | ||||
| 		} | ||||
| 		beaten = true | ||||
| 	} | ||||
|  | ||||
| 	regGroupDist, sameGroup := regionGroupDist(have.maxRegion, maxRegion, maxScript, tag.lang) | ||||
| 	if !beaten && m.regGroupDist != regGroupDist { | ||||
| 		if regGroupDist > m.regGroupDist { | ||||
| 			return | ||||
| 		} | ||||
| 		beaten = true | ||||
| 	} | ||||
|  | ||||
| 	paradigmReg := isParadigmLocale(tag.lang, have.maxRegion) | ||||
| 	if !beaten && m.paradigmReg != paradigmReg { | ||||
| 		if !paradigmReg { | ||||
| 			return | ||||
| 		} | ||||
| 		beaten = true | ||||
| 	} | ||||
|  | ||||
| 	// Next we prefer if the pre-maximized script was specified and identical. | ||||
| 	origScript := have.tag.script == tag.script && tag.script != 0 | ||||
| 	if !beaten && m.origScript != origScript { | ||||
| 		if m.origScript { | ||||
| 			return | ||||
| 		} | ||||
| 		beaten = true | ||||
| 	} | ||||
|  | ||||
| 	// Update m to the newly found best match. | ||||
| 	if beaten { | ||||
| 		m.have = have | ||||
| 		m.want = tag | ||||
| 		m.conf = c | ||||
| 		m.pinnedRegion = maxRegion | ||||
| 		m.sameRegionGroup = sameGroup | ||||
| 		m.origLang = origLang | ||||
| 		m.origReg = origReg | ||||
| 		m.paradigmReg = paradigmReg | ||||
| 		m.origScript = origScript | ||||
| 		m.regGroupDist = regGroupDist | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func isParadigmLocale(lang langID, r regionID) bool { | ||||
| 	for _, e := range paradigmLocales { | ||||
| 		if langID(e[0]) == lang && (r == regionID(e[1]) || r == regionID(e[2])) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // regionGroupDist computes the distance between two regions based on their | ||||
| // CLDR grouping. | ||||
| func regionGroupDist(a, b regionID, script scriptID, lang langID) (dist uint8, same bool) { | ||||
| 	const defaultDistance = 4 | ||||
|  | ||||
| 	aGroup := uint(regionToGroups[a]) << 1 | ||||
| 	bGroup := uint(regionToGroups[b]) << 1 | ||||
| 	for _, ri := range matchRegion { | ||||
| 		if langID(ri.lang) == lang && (ri.script == 0 || scriptID(ri.script) == script) { | ||||
| 			group := uint(1 << (ri.group &^ 0x80)) | ||||
| 			if 0x80&ri.group == 0 { | ||||
| 				if aGroup&bGroup&group != 0 { // Both regions are in the group. | ||||
| 					return ri.distance, ri.distance == defaultDistance | ||||
| 				} | ||||
| 			} else { | ||||
| 				if (aGroup|bGroup)&group == 0 { // Both regions are not in the group. | ||||
| 					return ri.distance, ri.distance == defaultDistance | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return defaultDistance, true | ||||
| } | ||||
|  | ||||
| func (t Tag) variants() string { | ||||
| 	if t.pVariant == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return t.str[t.pVariant:t.pExt] | ||||
| } | ||||
|  | ||||
| // variantOrPrivateTagStr returns variants or private use tags. | ||||
| func (t Tag) variantOrPrivateTagStr() string { | ||||
| 	if t.pExt > 0 { | ||||
| 		return t.str[t.pVariant:t.pExt] | ||||
| 	} | ||||
| 	return t.str[t.pVariant:] | ||||
| } | ||||
|  | ||||
| // equalsRest compares everything except the language. | ||||
| func (a Tag) equalsRest(b Tag) bool { | ||||
| 	// TODO: don't include extensions in this comparison. To do this efficiently, | ||||
| 	// though, we should handle private tags separately. | ||||
| 	return a.script == b.script && a.region == b.region && a.variantOrPrivateTagStr() == b.variantOrPrivateTagStr() | ||||
| } | ||||
|  | ||||
| // isExactEquivalent returns true if canonicalizing the language will not alter | ||||
| // the script or region of a tag. | ||||
| func isExactEquivalent(l langID) bool { | ||||
| 	for _, o := range notEquivalent { | ||||
| 		if o == l { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| var notEquivalent []langID | ||||
|  | ||||
| func init() { | ||||
| 	// Create a list of all languages for which canonicalization may alter the | ||||
| 	// script or region. | ||||
| 	for _, lm := range langAliasMap { | ||||
| 		tag := Tag{lang: langID(lm.from)} | ||||
| 		if tag, _ = tag.canonicalize(All); tag.script != 0 || tag.region != 0 { | ||||
| 			notEquivalent = append(notEquivalent, langID(lm.from)) | ||||
| 		} | ||||
| 	} | ||||
| 	// Maximize undefined regions of paradigm locales. | ||||
| 	for i, v := range paradigmLocales { | ||||
| 		max, _ := addTags(Tag{lang: langID(v[0])}) | ||||
| 		if v[1] == 0 { | ||||
| 			paradigmLocales[i][1] = uint16(max.region) | ||||
| 		} | ||||
| 		if v[2] == 0 { | ||||
| 			paradigmLocales[i][2] = uint16(max.region) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										859
									
								
								vendor/golang.org/x/text/language/parse.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										859
									
								
								vendor/golang.org/x/text/language/parse.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,859 @@ | ||||
| // Copyright 2013 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. | ||||
|  | ||||
| package language | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	"golang.org/x/text/internal/tag" | ||||
| ) | ||||
|  | ||||
| // isAlpha returns true if the byte is not a digit. | ||||
| // b must be an ASCII letter or digit. | ||||
| func isAlpha(b byte) bool { | ||||
| 	return b > '9' | ||||
| } | ||||
|  | ||||
| // isAlphaNum returns true if the string contains only ASCII letters or digits. | ||||
| func isAlphaNum(s []byte) bool { | ||||
| 	for _, c := range s { | ||||
| 		if !('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9') { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| // errSyntax is returned by any of the parsing functions when the | ||||
| // input is not well-formed, according to BCP 47. | ||||
| // TODO: return the position at which the syntax error occurred? | ||||
| var errSyntax = errors.New("language: tag is not well-formed") | ||||
|  | ||||
| // ValueError is returned by any of the parsing functions when the | ||||
| // input is well-formed but the respective subtag is not recognized | ||||
| // as a valid value. | ||||
| type ValueError struct { | ||||
| 	v [8]byte | ||||
| } | ||||
|  | ||||
| func mkErrInvalid(s []byte) error { | ||||
| 	var e ValueError | ||||
| 	copy(e.v[:], s) | ||||
| 	return e | ||||
| } | ||||
|  | ||||
| func (e ValueError) tag() []byte { | ||||
| 	n := bytes.IndexByte(e.v[:], 0) | ||||
| 	if n == -1 { | ||||
| 		n = 8 | ||||
| 	} | ||||
| 	return e.v[:n] | ||||
| } | ||||
|  | ||||
| // Error implements the error interface. | ||||
| func (e ValueError) Error() string { | ||||
| 	return fmt.Sprintf("language: subtag %q is well-formed but unknown", e.tag()) | ||||
| } | ||||
|  | ||||
| // Subtag returns the subtag for which the error occurred. | ||||
| func (e ValueError) Subtag() string { | ||||
| 	return string(e.tag()) | ||||
| } | ||||
|  | ||||
| // scanner is used to scan BCP 47 tokens, which are separated by _ or -. | ||||
| type scanner struct { | ||||
| 	b     []byte | ||||
| 	bytes [max99thPercentileSize]byte | ||||
| 	token []byte | ||||
| 	start int // start position of the current token | ||||
| 	end   int // end position of the current token | ||||
| 	next  int // next point for scan | ||||
| 	err   error | ||||
| 	done  bool | ||||
| } | ||||
|  | ||||
| func makeScannerString(s string) scanner { | ||||
| 	scan := scanner{} | ||||
| 	if len(s) <= len(scan.bytes) { | ||||
| 		scan.b = scan.bytes[:copy(scan.bytes[:], s)] | ||||
| 	} else { | ||||
| 		scan.b = []byte(s) | ||||
| 	} | ||||
| 	scan.init() | ||||
| 	return scan | ||||
| } | ||||
|  | ||||
| // makeScanner returns a scanner using b as the input buffer. | ||||
| // b is not copied and may be modified by the scanner routines. | ||||
| func makeScanner(b []byte) scanner { | ||||
| 	scan := scanner{b: b} | ||||
| 	scan.init() | ||||
| 	return scan | ||||
| } | ||||
|  | ||||
| func (s *scanner) init() { | ||||
| 	for i, c := range s.b { | ||||
| 		if c == '_' { | ||||
| 			s.b[i] = '-' | ||||
| 		} | ||||
| 	} | ||||
| 	s.scan() | ||||
| } | ||||
|  | ||||
| // restToLower converts the string between start and end to lower case. | ||||
| func (s *scanner) toLower(start, end int) { | ||||
| 	for i := start; i < end; i++ { | ||||
| 		c := s.b[i] | ||||
| 		if 'A' <= c && c <= 'Z' { | ||||
| 			s.b[i] += 'a' - 'A' | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (s *scanner) setError(e error) { | ||||
| 	if s.err == nil || (e == errSyntax && s.err != errSyntax) { | ||||
| 		s.err = e | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // resizeRange shrinks or grows the array at position oldStart such that | ||||
| // a new string of size newSize can fit between oldStart and oldEnd. | ||||
| // Sets the scan point to after the resized range. | ||||
| func (s *scanner) resizeRange(oldStart, oldEnd, newSize int) { | ||||
| 	s.start = oldStart | ||||
| 	if end := oldStart + newSize; end != oldEnd { | ||||
| 		diff := end - oldEnd | ||||
| 		if end < cap(s.b) { | ||||
| 			b := make([]byte, len(s.b)+diff) | ||||
| 			copy(b, s.b[:oldStart]) | ||||
| 			copy(b[end:], s.b[oldEnd:]) | ||||
| 			s.b = b | ||||
| 		} else { | ||||
| 			s.b = append(s.b[end:], s.b[oldEnd:]...) | ||||
| 		} | ||||
| 		s.next = end + (s.next - s.end) | ||||
| 		s.end = end | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // replace replaces the current token with repl. | ||||
| func (s *scanner) replace(repl string) { | ||||
| 	s.resizeRange(s.start, s.end, len(repl)) | ||||
| 	copy(s.b[s.start:], repl) | ||||
| } | ||||
|  | ||||
| // gobble removes the current token from the input. | ||||
| // Caller must call scan after calling gobble. | ||||
| func (s *scanner) gobble(e error) { | ||||
| 	s.setError(e) | ||||
| 	if s.start == 0 { | ||||
| 		s.b = s.b[:+copy(s.b, s.b[s.next:])] | ||||
| 		s.end = 0 | ||||
| 	} else { | ||||
| 		s.b = s.b[:s.start-1+copy(s.b[s.start-1:], s.b[s.end:])] | ||||
| 		s.end = s.start - 1 | ||||
| 	} | ||||
| 	s.next = s.start | ||||
| } | ||||
|  | ||||
| // deleteRange removes the given range from s.b before the current token. | ||||
| func (s *scanner) deleteRange(start, end int) { | ||||
| 	s.setError(errSyntax) | ||||
| 	s.b = s.b[:start+copy(s.b[start:], s.b[end:])] | ||||
| 	diff := end - start | ||||
| 	s.next -= diff | ||||
| 	s.start -= diff | ||||
| 	s.end -= diff | ||||
| } | ||||
|  | ||||
| // scan parses the next token of a BCP 47 string.  Tokens that are larger | ||||
| // than 8 characters or include non-alphanumeric characters result in an error | ||||
| // and are gobbled and removed from the output. | ||||
| // It returns the end position of the last token consumed. | ||||
| func (s *scanner) scan() (end int) { | ||||
| 	end = s.end | ||||
| 	s.token = nil | ||||
| 	for s.start = s.next; s.next < len(s.b); { | ||||
| 		i := bytes.IndexByte(s.b[s.next:], '-') | ||||
| 		if i == -1 { | ||||
| 			s.end = len(s.b) | ||||
| 			s.next = len(s.b) | ||||
| 			i = s.end - s.start | ||||
| 		} else { | ||||
| 			s.end = s.next + i | ||||
| 			s.next = s.end + 1 | ||||
| 		} | ||||
| 		token := s.b[s.start:s.end] | ||||
| 		if i < 1 || i > 8 || !isAlphaNum(token) { | ||||
| 			s.gobble(errSyntax) | ||||
| 			continue | ||||
| 		} | ||||
| 		s.token = token | ||||
| 		return end | ||||
| 	} | ||||
| 	if n := len(s.b); n > 0 && s.b[n-1] == '-' { | ||||
| 		s.setError(errSyntax) | ||||
| 		s.b = s.b[:len(s.b)-1] | ||||
| 	} | ||||
| 	s.done = true | ||||
| 	return end | ||||
| } | ||||
|  | ||||
| // acceptMinSize parses multiple tokens of the given size or greater. | ||||
| // It returns the end position of the last token consumed. | ||||
| func (s *scanner) acceptMinSize(min int) (end int) { | ||||
| 	end = s.end | ||||
| 	s.scan() | ||||
| 	for ; len(s.token) >= min; s.scan() { | ||||
| 		end = s.end | ||||
| 	} | ||||
| 	return end | ||||
| } | ||||
|  | ||||
| // Parse parses the given BCP 47 string and returns a valid Tag. If parsing | ||||
| // failed it returns an error and any part of the tag that could be parsed. | ||||
| // If parsing succeeded but an unknown value was found, it returns | ||||
| // ValueError. The Tag returned in this case is just stripped of the unknown | ||||
| // value. All other values are preserved. It accepts tags in the BCP 47 format | ||||
| // and extensions to this standard defined in | ||||
| // http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers. | ||||
| // The resulting tag is canonicalized using the default canonicalization type. | ||||
| func Parse(s string) (t Tag, err error) { | ||||
| 	return Default.Parse(s) | ||||
| } | ||||
|  | ||||
| // Parse parses the given BCP 47 string and returns a valid Tag. If parsing | ||||
| // failed it returns an error and any part of the tag that could be parsed. | ||||
| // If parsing succeeded but an unknown value was found, it returns | ||||
| // ValueError. The Tag returned in this case is just stripped of the unknown | ||||
| // value. All other values are preserved. It accepts tags in the BCP 47 format | ||||
| // and extensions to this standard defined in | ||||
| // http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers. | ||||
| // The resulting tag is canonicalized using the the canonicalization type c. | ||||
| func (c CanonType) Parse(s string) (t Tag, err error) { | ||||
| 	// TODO: consider supporting old-style locale key-value pairs. | ||||
| 	if s == "" { | ||||
| 		return und, errSyntax | ||||
| 	} | ||||
| 	if len(s) <= maxAltTaglen { | ||||
| 		b := [maxAltTaglen]byte{} | ||||
| 		for i, c := range s { | ||||
| 			// Generating invalid UTF-8 is okay as it won't match. | ||||
| 			if 'A' <= c && c <= 'Z' { | ||||
| 				c += 'a' - 'A' | ||||
| 			} else if c == '_' { | ||||
| 				c = '-' | ||||
| 			} | ||||
| 			b[i] = byte(c) | ||||
| 		} | ||||
| 		if t, ok := grandfathered(b); ok { | ||||
| 			return t, nil | ||||
| 		} | ||||
| 	} | ||||
| 	scan := makeScannerString(s) | ||||
| 	t, err = parse(&scan, s) | ||||
| 	t, changed := t.canonicalize(c) | ||||
| 	if changed { | ||||
| 		t.remakeString() | ||||
| 	} | ||||
| 	return t, err | ||||
| } | ||||
|  | ||||
| func parse(scan *scanner, s string) (t Tag, err error) { | ||||
| 	t = und | ||||
| 	var end int | ||||
| 	if n := len(scan.token); n <= 1 { | ||||
| 		scan.toLower(0, len(scan.b)) | ||||
| 		if n == 0 || scan.token[0] != 'x' { | ||||
| 			return t, errSyntax | ||||
| 		} | ||||
| 		end = parseExtensions(scan) | ||||
| 	} else if n >= 4 { | ||||
| 		return und, errSyntax | ||||
| 	} else { // the usual case | ||||
| 		t, end = parseTag(scan) | ||||
| 		if n := len(scan.token); n == 1 { | ||||
| 			t.pExt = uint16(end) | ||||
| 			end = parseExtensions(scan) | ||||
| 		} else if end < len(scan.b) { | ||||
| 			scan.setError(errSyntax) | ||||
| 			scan.b = scan.b[:end] | ||||
| 		} | ||||
| 	} | ||||
| 	if int(t.pVariant) < len(scan.b) { | ||||
| 		if end < len(s) { | ||||
| 			s = s[:end] | ||||
| 		} | ||||
| 		if len(s) > 0 && tag.Compare(s, scan.b) == 0 { | ||||
| 			t.str = s | ||||
| 		} else { | ||||
| 			t.str = string(scan.b) | ||||
| 		} | ||||
| 	} else { | ||||
| 		t.pVariant, t.pExt = 0, 0 | ||||
| 	} | ||||
| 	return t, scan.err | ||||
| } | ||||
|  | ||||
| // parseTag parses language, script, region and variants. | ||||
| // It returns a Tag and the end position in the input that was parsed. | ||||
| func parseTag(scan *scanner) (t Tag, end int) { | ||||
| 	var e error | ||||
| 	// TODO: set an error if an unknown lang, script or region is encountered. | ||||
| 	t.lang, e = getLangID(scan.token) | ||||
| 	scan.setError(e) | ||||
| 	scan.replace(t.lang.String()) | ||||
| 	langStart := scan.start | ||||
| 	end = scan.scan() | ||||
| 	for len(scan.token) == 3 && isAlpha(scan.token[0]) { | ||||
| 		// From http://tools.ietf.org/html/bcp47, <lang>-<extlang> tags are equivalent | ||||
| 		// to a tag of the form <extlang>. | ||||
| 		lang, e := getLangID(scan.token) | ||||
| 		if lang != 0 { | ||||
| 			t.lang = lang | ||||
| 			copy(scan.b[langStart:], lang.String()) | ||||
| 			scan.b[langStart+3] = '-' | ||||
| 			scan.start = langStart + 4 | ||||
| 		} | ||||
| 		scan.gobble(e) | ||||
| 		end = scan.scan() | ||||
| 	} | ||||
| 	if len(scan.token) == 4 && isAlpha(scan.token[0]) { | ||||
| 		t.script, e = getScriptID(script, scan.token) | ||||
| 		if t.script == 0 { | ||||
| 			scan.gobble(e) | ||||
| 		} | ||||
| 		end = scan.scan() | ||||
| 	} | ||||
| 	if n := len(scan.token); n >= 2 && n <= 3 { | ||||
| 		t.region, e = getRegionID(scan.token) | ||||
| 		if t.region == 0 { | ||||
| 			scan.gobble(e) | ||||
| 		} else { | ||||
| 			scan.replace(t.region.String()) | ||||
| 		} | ||||
| 		end = scan.scan() | ||||
| 	} | ||||
| 	scan.toLower(scan.start, len(scan.b)) | ||||
| 	t.pVariant = byte(end) | ||||
| 	end = parseVariants(scan, end, t) | ||||
| 	t.pExt = uint16(end) | ||||
| 	return t, end | ||||
| } | ||||
|  | ||||
| var separator = []byte{'-'} | ||||
|  | ||||
| // parseVariants scans tokens as long as each token is a valid variant string. | ||||
| // Duplicate variants are removed. | ||||
| func parseVariants(scan *scanner, end int, t Tag) int { | ||||
| 	start := scan.start | ||||
| 	varIDBuf := [4]uint8{} | ||||
| 	variantBuf := [4][]byte{} | ||||
| 	varID := varIDBuf[:0] | ||||
| 	variant := variantBuf[:0] | ||||
| 	last := -1 | ||||
| 	needSort := false | ||||
| 	for ; len(scan.token) >= 4; scan.scan() { | ||||
| 		// TODO: measure the impact of needing this conversion and redesign | ||||
| 		// the data structure if there is an issue. | ||||
| 		v, ok := variantIndex[string(scan.token)] | ||||
| 		if !ok { | ||||
| 			// unknown variant | ||||
| 			// TODO: allow user-defined variants? | ||||
| 			scan.gobble(mkErrInvalid(scan.token)) | ||||
| 			continue | ||||
| 		} | ||||
| 		varID = append(varID, v) | ||||
| 		variant = append(variant, scan.token) | ||||
| 		if !needSort { | ||||
| 			if last < int(v) { | ||||
| 				last = int(v) | ||||
| 			} else { | ||||
| 				needSort = true | ||||
| 				// There is no legal combinations of more than 7 variants | ||||
| 				// (and this is by no means a useful sequence). | ||||
| 				const maxVariants = 8 | ||||
| 				if len(varID) > maxVariants { | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		end = scan.end | ||||
| 	} | ||||
| 	if needSort { | ||||
| 		sort.Sort(variantsSort{varID, variant}) | ||||
| 		k, l := 0, -1 | ||||
| 		for i, v := range varID { | ||||
| 			w := int(v) | ||||
| 			if l == w { | ||||
| 				// Remove duplicates. | ||||
| 				continue | ||||
| 			} | ||||
| 			varID[k] = varID[i] | ||||
| 			variant[k] = variant[i] | ||||
| 			k++ | ||||
| 			l = w | ||||
| 		} | ||||
| 		if str := bytes.Join(variant[:k], separator); len(str) == 0 { | ||||
| 			end = start - 1 | ||||
| 		} else { | ||||
| 			scan.resizeRange(start, end, len(str)) | ||||
| 			copy(scan.b[scan.start:], str) | ||||
| 			end = scan.end | ||||
| 		} | ||||
| 	} | ||||
| 	return end | ||||
| } | ||||
|  | ||||
| type variantsSort struct { | ||||
| 	i []uint8 | ||||
| 	v [][]byte | ||||
| } | ||||
|  | ||||
| func (s variantsSort) Len() int { | ||||
| 	return len(s.i) | ||||
| } | ||||
|  | ||||
| func (s variantsSort) Swap(i, j int) { | ||||
| 	s.i[i], s.i[j] = s.i[j], s.i[i] | ||||
| 	s.v[i], s.v[j] = s.v[j], s.v[i] | ||||
| } | ||||
|  | ||||
| func (s variantsSort) Less(i, j int) bool { | ||||
| 	return s.i[i] < s.i[j] | ||||
| } | ||||
|  | ||||
| type bytesSort [][]byte | ||||
|  | ||||
| func (b bytesSort) Len() int { | ||||
| 	return len(b) | ||||
| } | ||||
|  | ||||
| func (b bytesSort) Swap(i, j int) { | ||||
| 	b[i], b[j] = b[j], b[i] | ||||
| } | ||||
|  | ||||
| func (b bytesSort) Less(i, j int) bool { | ||||
| 	return bytes.Compare(b[i], b[j]) == -1 | ||||
| } | ||||
|  | ||||
| // parseExtensions parses and normalizes the extensions in the buffer. | ||||
| // It returns the last position of scan.b that is part of any extension. | ||||
| // It also trims scan.b to remove excess parts accordingly. | ||||
| func parseExtensions(scan *scanner) int { | ||||
| 	start := scan.start | ||||
| 	exts := [][]byte{} | ||||
| 	private := []byte{} | ||||
| 	end := scan.end | ||||
| 	for len(scan.token) == 1 { | ||||
| 		extStart := scan.start | ||||
| 		ext := scan.token[0] | ||||
| 		end = parseExtension(scan) | ||||
| 		extension := scan.b[extStart:end] | ||||
| 		if len(extension) < 3 || (ext != 'x' && len(extension) < 4) { | ||||
| 			scan.setError(errSyntax) | ||||
| 			end = extStart | ||||
| 			continue | ||||
| 		} else if start == extStart && (ext == 'x' || scan.start == len(scan.b)) { | ||||
| 			scan.b = scan.b[:end] | ||||
| 			return end | ||||
| 		} else if ext == 'x' { | ||||
| 			private = extension | ||||
| 			break | ||||
| 		} | ||||
| 		exts = append(exts, extension) | ||||
| 	} | ||||
| 	sort.Sort(bytesSort(exts)) | ||||
| 	if len(private) > 0 { | ||||
| 		exts = append(exts, private) | ||||
| 	} | ||||
| 	scan.b = scan.b[:start] | ||||
| 	if len(exts) > 0 { | ||||
| 		scan.b = append(scan.b, bytes.Join(exts, separator)...) | ||||
| 	} else if start > 0 { | ||||
| 		// Strip trailing '-'. | ||||
| 		scan.b = scan.b[:start-1] | ||||
| 	} | ||||
| 	return end | ||||
| } | ||||
|  | ||||
| // parseExtension parses a single extension and returns the position of | ||||
| // the extension end. | ||||
| func parseExtension(scan *scanner) int { | ||||
| 	start, end := scan.start, scan.end | ||||
| 	switch scan.token[0] { | ||||
| 	case 'u': | ||||
| 		attrStart := end | ||||
| 		scan.scan() | ||||
| 		for last := []byte{}; len(scan.token) > 2; scan.scan() { | ||||
| 			if bytes.Compare(scan.token, last) != -1 { | ||||
| 				// Attributes are unsorted. Start over from scratch. | ||||
| 				p := attrStart + 1 | ||||
| 				scan.next = p | ||||
| 				attrs := [][]byte{} | ||||
| 				for scan.scan(); len(scan.token) > 2; scan.scan() { | ||||
| 					attrs = append(attrs, scan.token) | ||||
| 					end = scan.end | ||||
| 				} | ||||
| 				sort.Sort(bytesSort(attrs)) | ||||
| 				copy(scan.b[p:], bytes.Join(attrs, separator)) | ||||
| 				break | ||||
| 			} | ||||
| 			last = scan.token | ||||
| 			end = scan.end | ||||
| 		} | ||||
| 		var last, key []byte | ||||
| 		for attrEnd := end; len(scan.token) == 2; last = key { | ||||
| 			key = scan.token | ||||
| 			keyEnd := scan.end | ||||
| 			end = scan.acceptMinSize(3) | ||||
| 			// TODO: check key value validity | ||||
| 			if keyEnd == end || bytes.Compare(key, last) != 1 { | ||||
| 				// We have an invalid key or the keys are not sorted. | ||||
| 				// Start scanning keys from scratch and reorder. | ||||
| 				p := attrEnd + 1 | ||||
| 				scan.next = p | ||||
| 				keys := [][]byte{} | ||||
| 				for scan.scan(); len(scan.token) == 2; { | ||||
| 					keyStart, keyEnd := scan.start, scan.end | ||||
| 					end = scan.acceptMinSize(3) | ||||
| 					if keyEnd != end { | ||||
| 						keys = append(keys, scan.b[keyStart:end]) | ||||
| 					} else { | ||||
| 						scan.setError(errSyntax) | ||||
| 						end = keyStart | ||||
| 					} | ||||
| 				} | ||||
| 				sort.Sort(bytesSort(keys)) | ||||
| 				reordered := bytes.Join(keys, separator) | ||||
| 				if e := p + len(reordered); e < end { | ||||
| 					scan.deleteRange(e, end) | ||||
| 					end = e | ||||
| 				} | ||||
| 				copy(scan.b[p:], bytes.Join(keys, separator)) | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 	case 't': | ||||
| 		scan.scan() | ||||
| 		if n := len(scan.token); n >= 2 && n <= 3 && isAlpha(scan.token[1]) { | ||||
| 			_, end = parseTag(scan) | ||||
| 			scan.toLower(start, end) | ||||
| 		} | ||||
| 		for len(scan.token) == 2 && !isAlpha(scan.token[1]) { | ||||
| 			end = scan.acceptMinSize(3) | ||||
| 		} | ||||
| 	case 'x': | ||||
| 		end = scan.acceptMinSize(1) | ||||
| 	default: | ||||
| 		end = scan.acceptMinSize(2) | ||||
| 	} | ||||
| 	return end | ||||
| } | ||||
|  | ||||
| // Compose creates a Tag from individual parts, which may be of type Tag, Base, | ||||
| // Script, Region, Variant, []Variant, Extension, []Extension or error. If a | ||||
| // Base, Script or Region or slice of type Variant or Extension is passed more | ||||
| // than once, the latter will overwrite the former. Variants and Extensions are | ||||
| // accumulated, but if two extensions of the same type are passed, the latter | ||||
| // will replace the former. A Tag overwrites all former values and typically | ||||
| // only makes sense as the first argument. The resulting tag is returned after | ||||
| // canonicalizing using the Default CanonType. If one or more errors are | ||||
| // encountered, one of the errors is returned. | ||||
| func Compose(part ...interface{}) (t Tag, err error) { | ||||
| 	return Default.Compose(part...) | ||||
| } | ||||
|  | ||||
| // Compose creates a Tag from individual parts, which may be of type Tag, Base, | ||||
| // Script, Region, Variant, []Variant, Extension, []Extension or error. If a | ||||
| // Base, Script or Region or slice of type Variant or Extension is passed more | ||||
| // than once, the latter will overwrite the former. Variants and Extensions are | ||||
| // accumulated, but if two extensions of the same type are passed, the latter | ||||
| // will replace the former. A Tag overwrites all former values and typically | ||||
| // only makes sense as the first argument. The resulting tag is returned after | ||||
| // canonicalizing using CanonType c. If one or more errors are encountered, | ||||
| // one of the errors is returned. | ||||
| func (c CanonType) Compose(part ...interface{}) (t Tag, err error) { | ||||
| 	var b builder | ||||
| 	if err = b.update(part...); err != nil { | ||||
| 		return und, err | ||||
| 	} | ||||
| 	t, _ = b.tag.canonicalize(c) | ||||
|  | ||||
| 	if len(b.ext) > 0 || len(b.variant) > 0 { | ||||
| 		sort.Sort(sortVariant(b.variant)) | ||||
| 		sort.Strings(b.ext) | ||||
| 		if b.private != "" { | ||||
| 			b.ext = append(b.ext, b.private) | ||||
| 		} | ||||
| 		n := maxCoreSize + tokenLen(b.variant...) + tokenLen(b.ext...) | ||||
| 		buf := make([]byte, n) | ||||
| 		p := t.genCoreBytes(buf) | ||||
| 		t.pVariant = byte(p) | ||||
| 		p += appendTokens(buf[p:], b.variant...) | ||||
| 		t.pExt = uint16(p) | ||||
| 		p += appendTokens(buf[p:], b.ext...) | ||||
| 		t.str = string(buf[:p]) | ||||
| 	} else if b.private != "" { | ||||
| 		t.str = b.private | ||||
| 		t.remakeString() | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| type builder struct { | ||||
| 	tag Tag | ||||
|  | ||||
| 	private string // the x extension | ||||
| 	ext     []string | ||||
| 	variant []string | ||||
|  | ||||
| 	err error | ||||
| } | ||||
|  | ||||
| func (b *builder) addExt(e string) { | ||||
| 	if e == "" { | ||||
| 	} else if e[0] == 'x' { | ||||
| 		b.private = e | ||||
| 	} else { | ||||
| 		b.ext = append(b.ext, e) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| var errInvalidArgument = errors.New("invalid Extension or Variant") | ||||
|  | ||||
| func (b *builder) update(part ...interface{}) (err error) { | ||||
| 	replace := func(l *[]string, s string, eq func(a, b string) bool) bool { | ||||
| 		if s == "" { | ||||
| 			b.err = errInvalidArgument | ||||
| 			return true | ||||
| 		} | ||||
| 		for i, v := range *l { | ||||
| 			if eq(v, s) { | ||||
| 				(*l)[i] = s | ||||
| 				return true | ||||
| 			} | ||||
| 		} | ||||
| 		return false | ||||
| 	} | ||||
| 	for _, x := range part { | ||||
| 		switch v := x.(type) { | ||||
| 		case Tag: | ||||
| 			b.tag.lang = v.lang | ||||
| 			b.tag.region = v.region | ||||
| 			b.tag.script = v.script | ||||
| 			if v.str != "" { | ||||
| 				b.variant = nil | ||||
| 				for x, s := "", v.str[v.pVariant:v.pExt]; s != ""; { | ||||
| 					x, s = nextToken(s) | ||||
| 					b.variant = append(b.variant, x) | ||||
| 				} | ||||
| 				b.ext, b.private = nil, "" | ||||
| 				for i, e := int(v.pExt), ""; i < len(v.str); { | ||||
| 					i, e = getExtension(v.str, i) | ||||
| 					b.addExt(e) | ||||
| 				} | ||||
| 			} | ||||
| 		case Base: | ||||
| 			b.tag.lang = v.langID | ||||
| 		case Script: | ||||
| 			b.tag.script = v.scriptID | ||||
| 		case Region: | ||||
| 			b.tag.region = v.regionID | ||||
| 		case Variant: | ||||
| 			if !replace(&b.variant, v.variant, func(a, b string) bool { return a == b }) { | ||||
| 				b.variant = append(b.variant, v.variant) | ||||
| 			} | ||||
| 		case Extension: | ||||
| 			if !replace(&b.ext, v.s, func(a, b string) bool { return a[0] == b[0] }) { | ||||
| 				b.addExt(v.s) | ||||
| 			} | ||||
| 		case []Variant: | ||||
| 			b.variant = nil | ||||
| 			for _, x := range v { | ||||
| 				b.update(x) | ||||
| 			} | ||||
| 		case []Extension: | ||||
| 			b.ext, b.private = nil, "" | ||||
| 			for _, e := range v { | ||||
| 				b.update(e) | ||||
| 			} | ||||
| 		// TODO: support parsing of raw strings based on morphology or just extensions? | ||||
| 		case error: | ||||
| 			err = v | ||||
| 		} | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func tokenLen(token ...string) (n int) { | ||||
| 	for _, t := range token { | ||||
| 		n += len(t) + 1 | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func appendTokens(b []byte, token ...string) int { | ||||
| 	p := 0 | ||||
| 	for _, t := range token { | ||||
| 		b[p] = '-' | ||||
| 		copy(b[p+1:], t) | ||||
| 		p += 1 + len(t) | ||||
| 	} | ||||
| 	return p | ||||
| } | ||||
|  | ||||
| type sortVariant []string | ||||
|  | ||||
| func (s sortVariant) Len() int { | ||||
| 	return len(s) | ||||
| } | ||||
|  | ||||
| func (s sortVariant) Swap(i, j int) { | ||||
| 	s[j], s[i] = s[i], s[j] | ||||
| } | ||||
|  | ||||
| func (s sortVariant) Less(i, j int) bool { | ||||
| 	return variantIndex[s[i]] < variantIndex[s[j]] | ||||
| } | ||||
|  | ||||
| func findExt(list []string, x byte) int { | ||||
| 	for i, e := range list { | ||||
| 		if e[0] == x { | ||||
| 			return i | ||||
| 		} | ||||
| 	} | ||||
| 	return -1 | ||||
| } | ||||
|  | ||||
| // getExtension returns the name, body and end position of the extension. | ||||
| func getExtension(s string, p int) (end int, ext string) { | ||||
| 	if s[p] == '-' { | ||||
| 		p++ | ||||
| 	} | ||||
| 	if s[p] == 'x' { | ||||
| 		return len(s), s[p:] | ||||
| 	} | ||||
| 	end = nextExtension(s, p) | ||||
| 	return end, s[p:end] | ||||
| } | ||||
|  | ||||
| // nextExtension finds the next extension within the string, searching | ||||
| // for the -<char>- pattern from position p. | ||||
| // In the fast majority of cases, language tags will have at most | ||||
| // one extension and extensions tend to be small. | ||||
| func nextExtension(s string, p int) int { | ||||
| 	for n := len(s) - 3; p < n; { | ||||
| 		if s[p] == '-' { | ||||
| 			if s[p+2] == '-' { | ||||
| 				return p | ||||
| 			} | ||||
| 			p += 3 | ||||
| 		} else { | ||||
| 			p++ | ||||
| 		} | ||||
| 	} | ||||
| 	return len(s) | ||||
| } | ||||
|  | ||||
| var errInvalidWeight = errors.New("ParseAcceptLanguage: invalid weight") | ||||
|  | ||||
| // ParseAcceptLanguage parses the contents of an Accept-Language header as | ||||
| // defined in http://www.ietf.org/rfc/rfc2616.txt and returns a list of Tags and | ||||
| // a list of corresponding quality weights. It is more permissive than RFC 2616 | ||||
| // and may return non-nil slices even if the input is not valid. | ||||
| // The Tags will be sorted by highest weight first and then by first occurrence. | ||||
| // Tags with a weight of zero will be dropped. An error will be returned if the | ||||
| // input could not be parsed. | ||||
| func ParseAcceptLanguage(s string) (tag []Tag, q []float32, err error) { | ||||
| 	var entry string | ||||
| 	for s != "" { | ||||
| 		if entry, s = split(s, ','); entry == "" { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		entry, weight := split(entry, ';') | ||||
|  | ||||
| 		// Scan the language. | ||||
| 		t, err := Parse(entry) | ||||
| 		if err != nil { | ||||
| 			id, ok := acceptFallback[entry] | ||||
| 			if !ok { | ||||
| 				return nil, nil, err | ||||
| 			} | ||||
| 			t = Tag{lang: id} | ||||
| 		} | ||||
|  | ||||
| 		// Scan the optional weight. | ||||
| 		w := 1.0 | ||||
| 		if weight != "" { | ||||
| 			weight = consume(weight, 'q') | ||||
| 			weight = consume(weight, '=') | ||||
| 			// consume returns the empty string when a token could not be | ||||
| 			// consumed, resulting in an error for ParseFloat. | ||||
| 			if w, err = strconv.ParseFloat(weight, 32); err != nil { | ||||
| 				return nil, nil, errInvalidWeight | ||||
| 			} | ||||
| 			// Drop tags with a quality weight of 0. | ||||
| 			if w <= 0 { | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		tag = append(tag, t) | ||||
| 		q = append(q, float32(w)) | ||||
| 	} | ||||
| 	sortStable(&tagSort{tag, q}) | ||||
| 	return tag, q, nil | ||||
| } | ||||
|  | ||||
| // consume removes a leading token c from s and returns the result or the empty | ||||
| // string if there is no such token. | ||||
| func consume(s string, c byte) string { | ||||
| 	if s == "" || s[0] != c { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return strings.TrimSpace(s[1:]) | ||||
| } | ||||
|  | ||||
| func split(s string, c byte) (head, tail string) { | ||||
| 	if i := strings.IndexByte(s, c); i >= 0 { | ||||
| 		return strings.TrimSpace(s[:i]), strings.TrimSpace(s[i+1:]) | ||||
| 	} | ||||
| 	return strings.TrimSpace(s), "" | ||||
| } | ||||
|  | ||||
| // Add hack mapping to deal with a small number of cases that that occur | ||||
| // in Accept-Language (with reasonable frequency). | ||||
| var acceptFallback = map[string]langID{ | ||||
| 	"english": _en, | ||||
| 	"deutsch": _de, | ||||
| 	"italian": _it, | ||||
| 	"french":  _fr, | ||||
| 	"*":       _mul, // defined in the spec to match all languages. | ||||
| } | ||||
|  | ||||
| type tagSort struct { | ||||
| 	tag []Tag | ||||
| 	q   []float32 | ||||
| } | ||||
|  | ||||
| func (s *tagSort) Len() int { | ||||
| 	return len(s.q) | ||||
| } | ||||
|  | ||||
| func (s *tagSort) Less(i, j int) bool { | ||||
| 	return s.q[i] > s.q[j] | ||||
| } | ||||
|  | ||||
| func (s *tagSort) Swap(i, j int) { | ||||
| 	s.tag[i], s.tag[j] = s.tag[j], s.tag[i] | ||||
| 	s.q[i], s.q[j] = s.q[j], s.q[i] | ||||
| } | ||||
							
								
								
									
										3686
									
								
								vendor/golang.org/x/text/language/tables.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3686
									
								
								vendor/golang.org/x/text/language/tables.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										143
									
								
								vendor/golang.org/x/text/language/tags.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								vendor/golang.org/x/text/language/tags.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| // Copyright 2013 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. | ||||
|  | ||||
| package language | ||||
|  | ||||
| // TODO: Various sets of commonly use tags and regions. | ||||
|  | ||||
| // MustParse is like Parse, but panics if the given BCP 47 tag cannot be parsed. | ||||
| // It simplifies safe initialization of Tag values. | ||||
| func MustParse(s string) Tag { | ||||
| 	t, err := Parse(s) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return t | ||||
| } | ||||
|  | ||||
| // MustParse is like Parse, but panics if the given BCP 47 tag cannot be parsed. | ||||
| // It simplifies safe initialization of Tag values. | ||||
| func (c CanonType) MustParse(s string) Tag { | ||||
| 	t, err := c.Parse(s) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return t | ||||
| } | ||||
|  | ||||
| // MustParseBase is like ParseBase, but panics if the given base cannot be parsed. | ||||
| // It simplifies safe initialization of Base values. | ||||
| func MustParseBase(s string) Base { | ||||
| 	b, err := ParseBase(s) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| // MustParseScript is like ParseScript, but panics if the given script cannot be | ||||
| // parsed. It simplifies safe initialization of Script values. | ||||
| func MustParseScript(s string) Script { | ||||
| 	scr, err := ParseScript(s) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return scr | ||||
| } | ||||
|  | ||||
| // MustParseRegion is like ParseRegion, but panics if the given region cannot be | ||||
| // parsed. It simplifies safe initialization of Region values. | ||||
| func MustParseRegion(s string) Region { | ||||
| 	r, err := ParseRegion(s) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return r | ||||
| } | ||||
|  | ||||
| var ( | ||||
| 	und = Tag{} | ||||
|  | ||||
| 	Und Tag = Tag{} | ||||
|  | ||||
| 	Afrikaans            Tag = Tag{lang: _af}                //  af | ||||
| 	Amharic              Tag = Tag{lang: _am}                //  am | ||||
| 	Arabic               Tag = Tag{lang: _ar}                //  ar | ||||
| 	ModernStandardArabic Tag = Tag{lang: _ar, region: _001}  //  ar-001 | ||||
| 	Azerbaijani          Tag = Tag{lang: _az}                //  az | ||||
| 	Bulgarian            Tag = Tag{lang: _bg}                //  bg | ||||
| 	Bengali              Tag = Tag{lang: _bn}                //  bn | ||||
| 	Catalan              Tag = Tag{lang: _ca}                //  ca | ||||
| 	Czech                Tag = Tag{lang: _cs}                //  cs | ||||
| 	Danish               Tag = Tag{lang: _da}                //  da | ||||
| 	German               Tag = Tag{lang: _de}                //  de | ||||
| 	Greek                Tag = Tag{lang: _el}                //  el | ||||
| 	English              Tag = Tag{lang: _en}                //  en | ||||
| 	AmericanEnglish      Tag = Tag{lang: _en, region: _US}   //  en-US | ||||
| 	BritishEnglish       Tag = Tag{lang: _en, region: _GB}   //  en-GB | ||||
| 	Spanish              Tag = Tag{lang: _es}                //  es | ||||
| 	EuropeanSpanish      Tag = Tag{lang: _es, region: _ES}   //  es-ES | ||||
| 	LatinAmericanSpanish Tag = Tag{lang: _es, region: _419}  //  es-419 | ||||
| 	Estonian             Tag = Tag{lang: _et}                //  et | ||||
| 	Persian              Tag = Tag{lang: _fa}                //  fa | ||||
| 	Finnish              Tag = Tag{lang: _fi}                //  fi | ||||
| 	Filipino             Tag = Tag{lang: _fil}               //  fil | ||||
| 	French               Tag = Tag{lang: _fr}                //  fr | ||||
| 	CanadianFrench       Tag = Tag{lang: _fr, region: _CA}   //  fr-CA | ||||
| 	Gujarati             Tag = Tag{lang: _gu}                //  gu | ||||
| 	Hebrew               Tag = Tag{lang: _he}                //  he | ||||
| 	Hindi                Tag = Tag{lang: _hi}                //  hi | ||||
| 	Croatian             Tag = Tag{lang: _hr}                //  hr | ||||
| 	Hungarian            Tag = Tag{lang: _hu}                //  hu | ||||
| 	Armenian             Tag = Tag{lang: _hy}                //  hy | ||||
| 	Indonesian           Tag = Tag{lang: _id}                //  id | ||||
| 	Icelandic            Tag = Tag{lang: _is}                //  is | ||||
| 	Italian              Tag = Tag{lang: _it}                //  it | ||||
| 	Japanese             Tag = Tag{lang: _ja}                //  ja | ||||
| 	Georgian             Tag = Tag{lang: _ka}                //  ka | ||||
| 	Kazakh               Tag = Tag{lang: _kk}                //  kk | ||||
| 	Khmer                Tag = Tag{lang: _km}                //  km | ||||
| 	Kannada              Tag = Tag{lang: _kn}                //  kn | ||||
| 	Korean               Tag = Tag{lang: _ko}                //  ko | ||||
| 	Kirghiz              Tag = Tag{lang: _ky}                //  ky | ||||
| 	Lao                  Tag = Tag{lang: _lo}                //  lo | ||||
| 	Lithuanian           Tag = Tag{lang: _lt}                //  lt | ||||
| 	Latvian              Tag = Tag{lang: _lv}                //  lv | ||||
| 	Macedonian           Tag = Tag{lang: _mk}                //  mk | ||||
| 	Malayalam            Tag = Tag{lang: _ml}                //  ml | ||||
| 	Mongolian            Tag = Tag{lang: _mn}                //  mn | ||||
| 	Marathi              Tag = Tag{lang: _mr}                //  mr | ||||
| 	Malay                Tag = Tag{lang: _ms}                //  ms | ||||
| 	Burmese              Tag = Tag{lang: _my}                //  my | ||||
| 	Nepali               Tag = Tag{lang: _ne}                //  ne | ||||
| 	Dutch                Tag = Tag{lang: _nl}                //  nl | ||||
| 	Norwegian            Tag = Tag{lang: _no}                //  no | ||||
| 	Punjabi              Tag = Tag{lang: _pa}                //  pa | ||||
| 	Polish               Tag = Tag{lang: _pl}                //  pl | ||||
| 	Portuguese           Tag = Tag{lang: _pt}                //  pt | ||||
| 	BrazilianPortuguese  Tag = Tag{lang: _pt, region: _BR}   //  pt-BR | ||||
| 	EuropeanPortuguese   Tag = Tag{lang: _pt, region: _PT}   //  pt-PT | ||||
| 	Romanian             Tag = Tag{lang: _ro}                //  ro | ||||
| 	Russian              Tag = Tag{lang: _ru}                //  ru | ||||
| 	Sinhala              Tag = Tag{lang: _si}                //  si | ||||
| 	Slovak               Tag = Tag{lang: _sk}                //  sk | ||||
| 	Slovenian            Tag = Tag{lang: _sl}                //  sl | ||||
| 	Albanian             Tag = Tag{lang: _sq}                //  sq | ||||
| 	Serbian              Tag = Tag{lang: _sr}                //  sr | ||||
| 	SerbianLatin         Tag = Tag{lang: _sr, script: _Latn} //  sr-Latn | ||||
| 	Swedish              Tag = Tag{lang: _sv}                //  sv | ||||
| 	Swahili              Tag = Tag{lang: _sw}                //  sw | ||||
| 	Tamil                Tag = Tag{lang: _ta}                //  ta | ||||
| 	Telugu               Tag = Tag{lang: _te}                //  te | ||||
| 	Thai                 Tag = Tag{lang: _th}                //  th | ||||
| 	Turkish              Tag = Tag{lang: _tr}                //  tr | ||||
| 	Ukrainian            Tag = Tag{lang: _uk}                //  uk | ||||
| 	Urdu                 Tag = Tag{lang: _ur}                //  ur | ||||
| 	Uzbek                Tag = Tag{lang: _uz}                //  uz | ||||
| 	Vietnamese           Tag = Tag{lang: _vi}                //  vi | ||||
| 	Chinese              Tag = Tag{lang: _zh}                //  zh | ||||
| 	SimplifiedChinese    Tag = Tag{lang: _zh, script: _Hans} //  zh-Hans | ||||
| 	TraditionalChinese   Tag = Tag{lang: _zh, script: _Hant} //  zh-Hant | ||||
| 	Zulu                 Tag = Tag{lang: _zu}                //  zu | ||||
| ) | ||||
		Reference in New Issue
	
	Block a user