├── go.mod ├── go.sum ├── tools └── generate_templates.go ├── test_templates └── template.gohtml ├── .gitignore ├── test_translations ├── nl_NL.po ├── multiplural.po ├── en_US.po └── translations.pot ├── .github ├── FUNDING.yml └── workflows │ └── golangci-lint.yml ├── LICENSE ├── set.go ├── get.go ├── file_templates.go ├── README.md ├── set_test.go ├── translator_test.go ├── translator.go ├── generated_plural_templates.go └── plurals.json /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/donseba/go-translator 2 | 3 | go 1.24 4 | 5 | require github.com/leonelquinteros/gotext v1.7.2-0.20250311125224-bdb582003488 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/leonelquinteros/gotext v1.7.2-0.20250311125224-bdb582003488 h1:ifSZSC/OCl/43oeCGssg+HmSgR/Ni88yEhqEOlLFRwo= 2 | github.com/leonelquinteros/gotext v1.7.2-0.20250311125224-bdb582003488/go.mod h1:I0WoFDn9u2D3VbPnnDPT8mzZu0iSXG8iih+AH2fHHqg= 3 | -------------------------------------------------------------------------------- /tools/generate_templates.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/donseba/go-translator" 7 | ) 8 | 9 | func main() { 10 | err := translator.GenerateLanguageHeaderTemplatesFromJSON("./plurals.json", "./generated_plural_templates.go") 11 | if err != nil { 12 | log.Fatalf("Error generating templates: %v", err) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test_templates/template.gohtml: -------------------------------------------------------------------------------- 1 | {{ tn .Loc "Testing TN translation with amount" "Testing TN translations with amount" 2 }} 2 | {{ tl .Loc "Testing TL translation" }} 3 | {{ tl .Loc "First translation text" }} 4 | {{ tn .Loc "Testing TN translation" "Testing TN translations" }} 5 | {{ tn .Loc "Testing TN translation with amount and vars" "Testing %s translations with amount and vars" 2 "TN" }} 6 | 7 | {{ ctl .Loc "in context" "Testing CTL translation" }} 8 | {{ ctn .Loc "in context" "Testing CTN translation" "Testing CTN translations" 2 "CTN" }} 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | /test_translations/en_TL.po 23 | /test_translations/en_TN.po 24 | -------------------------------------------------------------------------------- /test_translations/nl_NL.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: translator\n" 4 | "POT-Creation-Date: \n" 5 | "PO-Revision-Date: \n" 6 | "Last-Translator: \n" 7 | "Language-Team: Savage Software\n" 8 | "Language: nl_NL\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 13 | "X-Generator: Poedit 3.4.1\n" 14 | "X-Poedit-SourceCharset: UTF-8\n" 15 | 16 | msgid "First translation text" 17 | msgstr "Eerste vertalingstekst" 18 | 19 | msgid "Second translation text" 20 | msgstr "Tweede vertalingstekst" 21 | 22 | msgid "Third translation text" 23 | msgstr "Derde vertalingstekst" -------------------------------------------------------------------------------- /test_translations/multiplural.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: translator\n" 4 | "POT-Creation-Date: \n" 5 | "PO-Revision-Date: \n" 6 | "Last-Translator: \n" 7 | "Language-Team: Savage Software\n" 8 | "Language: multiplural\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n==2 ? 1 : 2);\n" 13 | "X-Generator: Poedit 3.4.1\n" 14 | "X-Poedit-SourceCharset: UTF-8\n" 15 | 16 | msgid "First translation text" 17 | msgstr "Primo testo di traduzione" 18 | 19 | msgid "Second translation text" 20 | msgstr "Secondo testo di traduzione" 21 | 22 | msgid "Third translation text" 23 | msgstr "Terzo testo di traduzione" 24 | 25 | msgid "There is one apple" 26 | msgid_plural "There are many apples" 27 | msgstr[0] "C'è una mela" 28 | msgstr[1] "Ci sono due mele" 29 | msgstr[2] "Ci sono %d mele" -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: donseba # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | -------------------------------------------------------------------------------- /test_translations/en_US.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: translator\n" 4 | "POT-Creation-Date: \n" 5 | "PO-Revision-Date: \n" 6 | "Last-Translator: \n" 7 | "Language-Team: Savage Software\n" 8 | "Language: en_US\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 13 | "X-Generator: Poedit 3.4.1\n" 14 | "X-Poedit-SourceCharset: UTF-8\n" 15 | 16 | msgid "First translation text" 17 | msgstr "First translation text" 18 | 19 | msgid "Second translation text" 20 | msgstr "Second translation text" 21 | 22 | msgid "Third translation text" 23 | msgstr "Third translation text" 24 | 25 | msgid "There is one apple" 26 | msgid_plural "There are many apples" 27 | msgstr[0] "There is one apple" 28 | msgstr[1] "There are many apples" 29 | 30 | msgid "First translation text with %s" 31 | msgstr "First translation text with %s" 32 | 33 | msgid "Second translation text with %s" 34 | msgstr "Second translation text with %s" 35 | 36 | msgid "Third translation text with %s" 37 | msgstr "Third translation text with %s" 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Sebastiano Bellinzis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test_translations/translations.pot: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: translator\n" 4 | "Content-Type: text/plain; charset=UTF-8\n" 5 | "Language: en\n" 6 | 7 | msgid "First translation text" 8 | msgstr "" 9 | 10 | msgid "Second translation text" 11 | msgstr "" 12 | 13 | msgid "Third translation text" 14 | msgstr "" 15 | 16 | msgid "Non-existent text" 17 | msgstr "" 18 | 19 | msgid "There is one apple" 20 | msgid_plural "There are many apples" 21 | msgstr[0] "" 22 | msgstr[1] "" 23 | 24 | msgid "I see one cat" 25 | msgid_plural "I see many cats" 26 | msgstr[0] "" 27 | msgstr[1] "" 28 | 29 | msgctxt "context" 30 | msgid "Context text" 31 | msgstr "" 32 | 33 | msgid "First translation text" 34 | msgid_plural "First translation text plural" 35 | msgstr[0] "" 36 | msgstr[1] "" 37 | 38 | msgid "Testing TN translation with amount" 39 | msgid_plural "Testing TN translations with amount" 40 | msgstr[0] "" 41 | msgstr[1] "" 42 | 43 | msgid "Testing TN translation" 44 | msgid_plural "Testing TN translations" 45 | msgstr[0] "" 46 | msgstr[1] "" 47 | 48 | msgid "Testing TN translation with amount and vars" 49 | msgid_plural "Testing %s translations with amount and vars" 50 | msgstr[0] "" 51 | msgstr[1] "" 52 | 53 | msgid "Testing TL translation" 54 | msgstr "" 55 | 56 | msgctxt "in context" 57 | msgid "Testing CTL translation" 58 | msgstr "" 59 | 60 | msgctxt "in context" 61 | msgid "Testing CTN translation" 62 | msgid_plural "Testing CTN translations" 63 | msgstr[0] "" 64 | msgstr[1] "" 65 | 66 | msgid "First translation text with %s" 67 | msgstr "" 68 | 69 | msgid "Second translation text with %s" 70 | msgstr "" 71 | 72 | msgid "Third translation text with %s" 73 | msgstr "" 74 | 75 | msgid "Non-existent text with %s" 76 | msgstr "" 77 | -------------------------------------------------------------------------------- /.github/workflows/golangci-lint.yml: -------------------------------------------------------------------------------- 1 | name: golangci-lint 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - main 7 | pull_request: 8 | 9 | permissions: 10 | contents: read 11 | # Optional: allow read access to pull request. Use with `only-new-issues` option. 12 | # pull-requests: read 13 | 14 | jobs: 15 | golangci: 16 | name: lint 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v3 20 | - uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.24' 23 | cache: false 24 | - name: golangci-lint 25 | uses: golangci/golangci-lint-action@v3 26 | with: 27 | # Require: The version of golangci-lint to use. 28 | # When `install-mode` is `binary` (default) the value can be v1.2 or v1.2.3 or `latest` to use the latest version. 29 | # When `install-mode` is `goinstall` the value can be v1.2.3, `latest`, or the hash of a commit. 30 | version: latest 31 | 32 | # Optional: working directory, useful for monorepos 33 | # working-directory: somedir 34 | 35 | # Optional: golangci-lint command line arguments. 36 | # 37 | # Note: By default, the `.golangci.yml` file should be at the root of the repository. 38 | # The location of the configuration file can be changed by using `--config=` 39 | # args: --timeout=30m --config=/my/path/.golangci.yml --issues-exit-code=0 40 | args: --out-format=colored-line-number 41 | 42 | # Optional: show only new issues if it's a pull request. The default value is `false`. 43 | # only-new-issues: true 44 | 45 | # Optional: if set to true, then all caching functionality will be completely disabled, 46 | # takes precedence over all other caching options. 47 | # skip-cache: true 48 | 49 | # Optional: if set to true, then the action won't cache or restore ~/go/pkg. 50 | # skip-pkg-cache: true 51 | 52 | # Optional: if set to true, then the action won't cache or restore ~/.cache/go-build. 53 | # skip-build-cache: true 54 | 55 | # Optional: The mode to install golangci-lint. It can be 'binary' or 'goinstall'. 56 | # install-mode: "goinstall" -------------------------------------------------------------------------------- /set.go: -------------------------------------------------------------------------------- 1 | package translator 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "time" 8 | 9 | "github.com/leonelquinteros/gotext" 10 | ) 11 | 12 | func (t *Translator) SetTL(loc Localizer, key string, value string) error { 13 | translator, exists := t.languages[loc.GetLocale()] 14 | if !exists { 15 | return ErrorLanguageNotFound 16 | } 17 | 18 | translator.Set(key, value) 19 | return nil 20 | } 21 | 22 | func (t *Translator) SetTLN(loc Localizer, key, plural string, n int, value string) error { 23 | translator, exists := t.languages[loc.GetLocale()] 24 | if !exists { 25 | return ErrorLanguageNotFound 26 | } 27 | 28 | translator.SetN(key, plural, n, value) 29 | return nil 30 | } 31 | 32 | func (t *Translator) SetCTL(loc Localizer, key, ctx, value string) error { 33 | translator, exists := t.languages[loc.GetLocale()] 34 | if !exists { 35 | return ErrorLanguageNotFound 36 | } 37 | 38 | translator.SetC(key, ctx, value) 39 | return nil 40 | } 41 | 42 | func (t *Translator) SetCTN(loc Localizer, key, plural, ctx string, n int, value string) error { 43 | translator, exists := t.languages[loc.GetLocale()] 44 | if !exists { 45 | return ErrorLanguageNotFound 46 | } 47 | 48 | translator.SetNC(key, plural, ctx, n, value) 49 | return nil 50 | } 51 | 52 | func (t *Translator) SetRefs(loc Localizer, key string, refs []string) error { 53 | translator, exists := t.languages[loc.GetLocale()] 54 | if !exists { 55 | return ErrorLanguageNotFound 56 | } 57 | 58 | translator.SetRefs(key, refs) 59 | return nil 60 | } 61 | 62 | func (t *Translator) SetDetails(loc Localizer, key, value string) error { 63 | translator, exists := t.languages[loc.GetLocale()] 64 | if !exists { 65 | return ErrorLanguageNotFound 66 | } 67 | 68 | translator.GetDomain().Headers.Set(key, value) 69 | return nil 70 | } 71 | 72 | func (t *Translator) AddDetails(loc Localizer, key, value string) error { 73 | translator, exists := t.languages[loc.GetLocale()] 74 | if !exists { 75 | return ErrorLanguageNotFound 76 | } 77 | 78 | translator.GetDomain().Headers.Add(key, value) 79 | return nil 80 | } 81 | 82 | func (t *Translator) Write(loc Localizer) error { 83 | translator, exists := t.languages[loc.GetLocale()] 84 | if !exists { 85 | return ErrorLanguageNotFound 86 | } 87 | 88 | if translator.GetDomain().Headers.Get("PO-Revision-Date") == "" { 89 | translator.GetDomain().Headers.Set("PO-Revision-Date", time.Now().Format(time.RFC3339)) 90 | } 91 | 92 | data, err := translator.MarshalText() 93 | if err != nil { 94 | return err 95 | } 96 | 97 | err = os.WriteFile(filepath.Join(t.translationsDir, fmt.Sprintf("%s.po", loc.GetLocale())), data, 0644) 98 | if err != nil { 99 | return err 100 | } 101 | 102 | return nil 103 | } 104 | 105 | func (t *Translator) NewLanguage(loc Localizer, headers ...map[string]string) error { 106 | _, exists := t.languages[loc.GetLocale()] 107 | if exists { 108 | return ErrorLanguageAlreadyExists 109 | } 110 | 111 | if t.languages == nil { 112 | t.languages = make(map[string]*gotext.Po) 113 | } 114 | 115 | t.languages[loc.GetLocale()] = gotext.NewPo() 116 | t.languages[loc.GetLocale()].GetDomain().Headers.Set("Language", loc.GetLocale()) 117 | t.languages[loc.GetLocale()].GetDomain().Headers.Set("Content-Type", "text/plain; charset=UTF-8") 118 | t.languages[loc.GetLocale()].GetDomain().Headers.Set("X-Generator:", "github.com/donseba/go-translator") 119 | 120 | for _, header := range headers { 121 | for key, value := range header { 122 | t.languages[loc.GetLocale()].GetDomain().Headers.Set(key, value) 123 | } 124 | } 125 | 126 | return nil 127 | } 128 | -------------------------------------------------------------------------------- /get.go: -------------------------------------------------------------------------------- 1 | package translator 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Tl translates a string based on the given language tag and key. 8 | func (t *Translator) tl(loc Localizer, key string, args ...any) string { 9 | // Always add to POT if not exists 10 | if _, ok := t.uniqueKeys[key]; !ok { 11 | t.uniqueKeys[key] = uniqueKey{singular: key} 12 | err := t.addToPotFileIfNotExists(translationKey{"", key, false}) 13 | if err != nil { 14 | fmt.Println(err) 15 | } 16 | } 17 | 18 | translator, exists := t.languages[loc.GetLocale()] 19 | if !exists { 20 | return fmt.Sprintf(DefaultNoTranslationTL, key) 21 | } 22 | 23 | if !translator.IsTranslated(key) { 24 | return fmt.Sprintf(DefaultNoTranslationTL, key) 25 | } 26 | 27 | translated := translator.Get(fmt.Sprintf("%s", key), args...) //nolint:gosimple 28 | return t.removePrefix(translated) 29 | } 30 | 31 | func (t *Translator) ctl(loc Localizer, ctx, key string, args ...any) string { 32 | if t.uniqueKeysCtx[ctx] == nil { 33 | t.uniqueKeysCtx[ctx] = make(map[string]uniqueKey) 34 | } 35 | if _, ok := t.uniqueKeysCtx[ctx][key]; !ok { 36 | t.uniqueKeysCtx[ctx][key] = uniqueKey{singular: key} 37 | err := t.addToPotFileIfNotExists(translationKey{ctx, key, false}) 38 | if err != nil { 39 | fmt.Println(err) 40 | } 41 | } 42 | 43 | translator, exists := t.languages[loc.GetLocale()] 44 | if !exists { 45 | return fmt.Sprintf(DefaultNoTranslationCTL, ctx, key) 46 | } 47 | 48 | if ctx == "" { 49 | return t.tl(loc, key, args...) 50 | } 51 | 52 | if !translator.IsTranslatedC(key, ctx) { 53 | return fmt.Sprintf(DefaultNoTranslationCTL, ctx, key) 54 | } 55 | 56 | translated := translator.GetC(fmt.Sprintf("%s", key), ctx, args...) //nolint:gosimple 57 | return t.removePrefix(translated) 58 | } 59 | 60 | // tn method for handling plurals 61 | func (t *Translator) tn(loc Localizer, singular, plural string, n int, args ...any) string { 62 | // Always add to POT if not exists 63 | if _, ok := t.uniqueKeys[singular]; !ok { 64 | t.uniqueKeys[singular] = uniqueKey{singular: singular, plural: plural} 65 | err := t.addToPotFileIfNotExists(translationKey{"", singular, true}) 66 | if err != nil { 67 | fmt.Println(err) 68 | } 69 | } 70 | 71 | translator, exists := t.languages[loc.GetLocale()] 72 | if !exists { 73 | return fmt.Sprintf(DefaultNoTranslationTN, singular, plural) 74 | } 75 | 76 | if !translator.IsTranslatedN(singular, n) { 77 | return fmt.Sprintf(DefaultNoTranslationTN, singular, plural) 78 | } 79 | 80 | translated := translator.GetN(singular, plural, n, args...) 81 | return t.removePrefix(translated) 82 | } 83 | 84 | func (t *Translator) ctn(loc Localizer, ctx, singular, plural string, n int, args ...any) string { 85 | if t.uniqueKeysCtx[ctx] == nil { 86 | t.uniqueKeysCtx[ctx] = make(map[string]uniqueKey) 87 | } 88 | if _, ok := t.uniqueKeysCtx[ctx][singular]; !ok { 89 | t.uniqueKeysCtx[ctx][singular] = uniqueKey{singular: singular, plural: plural} 90 | err := t.addToPotFileIfNotExists(translationKey{ctx, singular, true}) 91 | if err != nil { 92 | fmt.Println(err) 93 | } 94 | } 95 | 96 | translator, exists := t.languages[loc.GetLocale()] 97 | if !exists { 98 | return fmt.Sprintf(DefaultNoTranslationCTN, ctx, singular, plural) 99 | } 100 | 101 | if !translator.IsTranslatedNC(singular, n, ctx) { 102 | return fmt.Sprintf(DefaultNoTranslationCTN, ctx, singular, plural) 103 | } 104 | 105 | translated := translator.GetNC(singular, plural, n, ctx, args...) 106 | return t.removePrefix(translated) 107 | } 108 | 109 | // Tl translates a string based on the given language tag and key. 110 | func (t *Translator) Tl(loc Localizer, key string, args ...any) string { 111 | return t.tl(loc, key, args...) 112 | } 113 | 114 | // Tn method for handling plurals 115 | func (t *Translator) Tn(loc Localizer, singular, plural string, n int) string { 116 | return t.tn(loc, singular, plural, n) 117 | } 118 | 119 | // Ctl method for handling string translation with context 120 | func (t *Translator) Ctl(loc Localizer, ctx, key string, args ...any) string { 121 | return t.ctl(loc, ctx, key, args...) 122 | } 123 | 124 | // Ctn method for handling plurals with context 125 | func (t *Translator) Ctn(loc Localizer, ctx, singular, plural string, n int) string { 126 | return t.ctn(loc, ctx, singular, plural, n) 127 | } 128 | 129 | // Details return the header of the language file 130 | func (t *Translator) Details(loc Localizer) map[string][]string { 131 | return t.languages[loc.GetLocale()].Headers 132 | } 133 | -------------------------------------------------------------------------------- /file_templates.go: -------------------------------------------------------------------------------- 1 | package translator 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | "time" 8 | ) 9 | 10 | // POT/PO file header template struct 11 | // You can expand this struct as needed for more fields 12 | // See: https://www.gnu.org/software/gettext/manual/html_node/Header-Entry.html 13 | 14 | type TranslationFileHeader struct { 15 | ProjectID string 16 | ReportMsgidBugsTo string 17 | POTCreationDate string 18 | PORevisionDate string 19 | LastTranslator string 20 | LanguageTeam string 21 | Language string 22 | MIMEVersion string 23 | ContentType string 24 | ContentTransferEncoding string 25 | PluralForms string 26 | } 27 | 28 | // DefaultHeader returns a default header for a given language 29 | func DefaultHeader(language string) TranslationFileHeader { 30 | now := time.Now().Format("2006-01-02 15:04-0700") 31 | return TranslationFileHeader{ 32 | ProjectID: "PACKAGE VERSION", 33 | ReportMsgidBugsTo: "", 34 | POTCreationDate: now, 35 | PORevisionDate: now, 36 | LastTranslator: "", 37 | LanguageTeam: "", 38 | Language: language, 39 | MIMEVersion: "1.0", 40 | ContentType: "text/plain; charset=UTF-8", 41 | ContentTransferEncoding: "8bit", 42 | PluralForms: "nplurals=2; plural=(n != 1);", 43 | } 44 | } 45 | 46 | // GetHeaderForLanguage returns a header template for a given language, falling back to DefaultHeader 47 | func GetHeaderForLanguage(language string) TranslationFileHeader { 48 | if h, ok := LanguageHeaderTemplates[language]; ok { 49 | h.ProjectID = "Go-FORM" 50 | h.POTCreationDate = time.Now().Format("2006-01-02 15:04-0700") 51 | h.PORevisionDate = h.POTCreationDate 52 | h.LanguageTeam = "Go-FORM" 53 | h.Language = language 54 | h.ContentType = "text/plain; charset=UTF-8" 55 | h.ContentTransferEncoding = "8bit" 56 | return h 57 | } 58 | return DefaultHeader(language) 59 | } 60 | 61 | // WritePOTFile creates a POT file with the default header if it does not exist 62 | func WritePOTFile(path string) error { 63 | if _, err := os.Stat(path); err == nil { 64 | return nil // file exists 65 | } 66 | h := DefaultHeader("") 67 | f, err := os.Create(path) 68 | if err != nil { 69 | return err 70 | } 71 | defer f.Close() 72 | _, err = f.WriteString(h.HeaderString()) 73 | return err 74 | } 75 | 76 | // WritePOFile creates a PO file for a language with the default header if it does not exist 77 | func WritePOFile(path, language string) error { 78 | if _, err := os.Stat(path); err == nil { 79 | return nil // file exists 80 | } 81 | h := DefaultHeader(language) 82 | f, err := os.Create(path) 83 | if err != nil { 84 | return err 85 | } 86 | defer f.Close() 87 | _, err = f.WriteString(h.HeaderString()) 88 | return err 89 | } 90 | 91 | // HeaderString returns the formatted header for a PO/POT file 92 | func (h TranslationFileHeader) HeaderString() string { 93 | // Ensure Language is always set and not empty 94 | lang := h.Language 95 | if lang == "" { 96 | lang = "C" 97 | } 98 | return fmt.Sprintf(`msgid "" 99 | msgstr "" 100 | "Project-Id-Version: %s\n" 101 | "Report-Msgid-Bugs-To: %s\n" 102 | "POT-Creation-Date: %s\n" 103 | "PO-Revision-Date: %s\n" 104 | "Last-Translator: %s\n" 105 | "Language-Team: %s\n" 106 | "Language: %s\n" 107 | "MIME-Version: %s\n" 108 | "Content-Type: %s\n" 109 | "Content-Transfer-Encoding: %s\n" 110 | "Plural-Forms: %s\n" 111 | `, 112 | h.ProjectID, 113 | h.ReportMsgidBugsTo, 114 | h.POTCreationDate, 115 | h.PORevisionDate, 116 | h.LastTranslator, 117 | h.LanguageTeam, 118 | lang, 119 | h.MIMEVersion, 120 | h.ContentType, 121 | h.ContentTransferEncoding, 122 | h.PluralForms, 123 | ) 124 | } 125 | 126 | // GenerateLanguageHeaderTemplatesFromJSON parses a plurals.json file and generates Go code for LanguageHeaderTemplates 127 | func GenerateLanguageHeaderTemplatesFromJSON(jsonPath, goPath string) error { 128 | data, err := os.ReadFile(jsonPath) 129 | if err != nil { 130 | return err 131 | } 132 | var plurals map[string]struct { 133 | Name string `json:"name"` 134 | Formulas struct { 135 | Standard string `json:"standard"` 136 | PHP string `json:"php"` 137 | } `json:"formulas"` 138 | Plurals int `json:"plurals"` 139 | } 140 | if err := json.Unmarshal(data, &plurals); err != nil { 141 | return err 142 | } 143 | 144 | out := "package translator\n\n// Code generated by script. DO NOT EDIT.\n\nvar LanguageHeaderTemplates = map[string]TranslationFileHeader{\n" 145 | for code, v := range plurals { 146 | pluralForms := "nplurals=" + itoa(v.Plurals) + "; plural=(" + v.Formulas.Standard + ");" 147 | out += "\t\"" + code + "\": {PluralForms: \"" + pluralForms + "\", Language: \"" + code + "\"},\n" 148 | } 149 | out += "}\n" 150 | return os.WriteFile(goPath, []byte(out), 0644) 151 | } 152 | 153 | func itoa(i int) string { 154 | return fmt.Sprintf("%d", i) 155 | } 156 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-translator 2 | 3 | Overview 4 | -- 5 | The Translator package is a Go module designed to facilitate easy and dynamic localization of applications. It provides a robust and flexible way to manage translations, including support for plural forms and customizable prefix handling for translation keys. This package utilizes the gotext library for managing PO files and integrates seamlessly with Go templates, making it an ideal solution for applications requiring multi-language support. 6 | 7 | Features 8 | -- 9 | - **Dynamic Language Support**: Add new languages by parsing PO files. 10 | - **Template Integration**: Works with Go HTML templates, extracting translation keys directly from them. 11 | - **Pluralization Support**: Handles singular and plural forms for languages with complex plural rules. 12 | - **Contextual Translations**: Supports context-based translations for more accurate localization. 13 | - **Prefix Handling**: Customizable prefix handling in translation keys, allowing for organized and readable translation files. 14 | - **Missing Translation Detection**: Scans for and logs missing translations, simplifying the translation management process. 15 | 16 | Installation 17 | -- 18 | To install the Translator package, use the following go get command: 19 | 20 | ```bash 21 | go get github.com/donseba/go-translator 22 | ``` 23 | 24 | Usage 25 | -- 26 | Initializing the Translator 27 | 28 | ```go 29 | package main 30 | 31 | import "github.com/donseba/go-translator" 32 | 33 | func main() { 34 | translationsDir := "path/to/translations" 35 | templateDir := "path/to/templates" 36 | 37 | tr := translator.NewTranslator(translationsDir, templateDir) 38 | tr.SetPrefixSeparator("__.") // Set the prefix separator if different from the default 39 | 40 | // load languages 41 | tr.AddLanguage("en_US") 42 | tr.AddLanguage("nl_NL") 43 | 44 | // check for missing translations and add them to the pot file 45 | err = app.Translation.CheckMissingTranslations("translations.pot") 46 | if err != nil { 47 | log.Fatal(err) 48 | } 49 | 50 | // add template functions 51 | // this will add the tl, tn, ctl and ctn functions to the template 52 | var yourTemplateFunctions = make(template.FuncMap) 53 | for k, v := range tr.FuncMap() { 54 | yourTemplateFunctions[k] = v 55 | } 56 | ``` 57 | 58 | Using the Translator in Templates 59 | -- 60 | Use tl and ctl for translating singular texts and tn and ctn for plural forms. 61 | 62 | ```html 63 | 64 |

{{ tl .Loc "Hello, World!" }}

65 | 66 | 67 |

{{ tn .Loc "You have one message." "You have %d messages." 5, 5 }}

68 | ``` 69 | 70 | Localizer interface 71 | -- 72 | 73 | The Localizer interface is used to provide the translator with the current language. 74 | It is used to determine the correct translation for the given key. 75 | 76 | ```go 77 | //Localizer interface contains the methods that are needed for the translator 78 | Localizer interface { 79 | // GetLocale returns the locale of the localizer, ie. "en_US" 80 | GetLocale() string 81 | } 82 | ``` 83 | 84 | 85 | Setting a language to use 86 | -- 87 | ```go 88 | tr.SetLanguage("en_US") 89 | tr.SetLanguage("nl_NL") 90 | ``` 91 | 92 | Setting and possibly creating a New Language 93 | --------------------- 94 | 95 | To add or ensure a language file exists (and is loaded), use the `EnsureLanguage` method. This will create a new `.po` file with the correct header (including plural forms) if it does not exist, and then load it into the translator: 96 | 97 | ```go 98 | err := tr.EnsureLanguage("fr") // creates fr.po if missing, with correct header 99 | if err != nil { 100 | log.Fatal(err) 101 | } 102 | ``` 103 | 104 | - The generated `.po` file will always include the recommended headers: 105 | - `Content-Type: text/plain; charset=UTF-8` 106 | - `Content-Transfer-Encoding: 8bit` 107 | - `Plural-Forms` (auto-filled from plural rules) 108 | - `Language` (set to the language code) 109 | 110 | - Calling `EnsureLanguage` multiple times is safe and will not overwrite existing files or translations. 111 | 112 | Testing and Idempotency 113 | ----------------------- 114 | 115 | - The package includes tests to ensure that language files are created with the correct headers and that repeated calls to `EnsureLanguage` do not overwrite existing files. 116 | - Plural rules are generated from `plurals.json` and included in the codebase for accuracy and maintainability. 117 | 118 | Updating Plural Rules 119 | --------------------- 120 | 121 | If you update `plurals.json`, regenerate the plural rules Go map by running: 122 | 123 | ```sh 124 | go run tools/generate_templates.go 125 | ``` 126 | 127 | This will update `generated_plural_templates.go` with the latest plural forms and language codes. 128 | 129 | Scanning for Missing Translations 130 | -- 131 | To check for missing translations in your templates: 132 | 133 | ```go 134 | err := tr.CheckMissingTranslations("messages.pot") 135 | if err != nil { 136 | log.Fatal(err) 137 | } 138 | ``` 139 | Customizing Prefix Separator 140 | -- 141 | You can customize the prefix separator used in translation keys: 142 | 143 | ```go 144 | tr.SetPrefixSeparator("__CUSTOM__") 145 | ``` 146 | The default prefix separator is `__.` 147 | 148 | > **Note/Disclaimer:** While customizing the prefix separator is supported, it is generally recommended to use the CTL or CTN methods instead. These methods allow you to set translation context explicitly, which is more robust and flexible for handling similar keys in different contexts. 149 | 150 | Removing Prefixes from Translations 151 | -- 152 | The package automatically handles the removal of prefixes from translations at runtime: 153 | 154 | ```go 155 | translatedText := tr.Tl(localizer, "prefix__.your_translation_key") // output your_translation_key 156 | ``` 157 | 158 | TODO 159 | -- 160 | - add caching functionality of the loaded translated keys. 161 | - add more unit tests 162 | - live reload translations when the file changes 163 | - fallback language 164 | 165 | 166 | Contributing 167 | -- 168 | Contributions to the Translator package are welcome! Please submit a pull request or open an issue for any bugs, features, or improvements. 169 | 170 | License 171 | -- 172 | This package is licensed under MIT. Please see the LICENSE file for more details. 173 | -------------------------------------------------------------------------------- /set_test.go: -------------------------------------------------------------------------------- 1 | package translator 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | "testing" 9 | ) 10 | 11 | var ( 12 | testSetLanguageTL = "en_TL" 13 | testSetLanguageTN = "en_TN" 14 | ) 15 | 16 | func TestTranslator_SetTL(t *testing.T) { 17 | type fields struct { 18 | translationsDir string 19 | templateDir string 20 | uniqueKeys map[string]uniqueKey 21 | uniqueKeysCtx map[string]map[string]uniqueKey 22 | } 23 | type args struct { 24 | key string 25 | plural string 26 | n int 27 | value string 28 | } 29 | 30 | localizerTL := NewLocalizer(testSetLanguageTL) 31 | localizerTN := NewLocalizer(testSetLanguageTN) 32 | 33 | tests := []struct { 34 | name string 35 | mode string 36 | loc Localizer 37 | lang string 38 | fields fields 39 | args []args 40 | wantErr bool 41 | }{ 42 | { 43 | name: "Test SetTL", 44 | mode: "SetTL", 45 | lang: testSetLanguageTL, 46 | fields: fields{ 47 | translationsDir: "test_translations", 48 | templateDir: "test_templates", 49 | uniqueKeys: map[string]uniqueKey{}, 50 | uniqueKeysCtx: map[string]map[string]uniqueKey{}, 51 | }, 52 | loc: localizerTL, 53 | args: []args{ 54 | {key: "test_key", value: "test_value"}, 55 | {key: "test_key2", value: "test_value2"}, 56 | {key: "test_key3", value: "test_value3"}, 57 | }, 58 | wantErr: false, 59 | }, 60 | { 61 | name: "Test SetTN", 62 | mode: "SetTN", 63 | lang: testSetLanguageTN, 64 | fields: fields{ 65 | translationsDir: "test_translations", 66 | templateDir: "test_templates", 67 | uniqueKeys: map[string]uniqueKey{}, 68 | uniqueKeysCtx: map[string]map[string]uniqueKey{}, 69 | }, 70 | loc: localizerTN, 71 | args: []args{ 72 | {key: "test_key", plural: "test_plural", n: 0, value: "test_value 0"}, 73 | {key: "test_key", plural: "test_plural", n: 1, value: "test_value 1"}, 74 | {key: "test_key", plural: "test_plural", n: 2, value: "test_value 2"}, 75 | {key: "test_key", plural: "test_plural", n: 3, value: "test_value 2"}, 76 | {key: "test_key2", plural: "test_plural2", n: 2, value: "test_value2"}, 77 | {key: "test_key3", plural: "test_plural3", n: 3, value: "test_value3"}, 78 | }, 79 | }, 80 | } 81 | 82 | for _, tt := range tests { 83 | t.Run(tt.name, func(t *testing.T) { 84 | tr := &Translator{ 85 | translationsDir: tt.fields.translationsDir, 86 | templateDir: tt.fields.templateDir, 87 | uniqueKeys: tt.fields.uniqueKeys, 88 | uniqueKeysCtx: tt.fields.uniqueKeysCtx, 89 | } 90 | 91 | err := tr.NewLanguage(tt.loc, map[string]string{ 92 | "Language-Team": fmt.Sprintf("%s %s", "English", tt.mode), 93 | "Last-Translator:": "Don Seba ", 94 | "Language": tt.lang, 95 | "Content-Type": "text/plain; charset=UTF-8", 96 | "Plural-Forms": "nplurals=2; plural=(n != 1)", 97 | "X-Generator": "Poedit 2.2.4", 98 | "Project-Id-Version": "TEST VERSION", 99 | }) 100 | 101 | if err != nil { 102 | t.Errorf("error = %v", err) 103 | } 104 | 105 | for _, arg := range tt.args { 106 | switch tt.mode { 107 | case "SetTL": 108 | if err := tr.SetTL(tt.loc, arg.key, arg.value); (err != nil) != tt.wantErr { 109 | t.Errorf("Translator.SetTL() error = %v, wantErr %v", err, tt.wantErr) 110 | } 111 | case "SetTN": 112 | if err := tr.SetTLN(tt.loc, arg.key, arg.plural, arg.n, arg.value); (err != nil) != tt.wantErr { 113 | t.Errorf("Translator.SetTL() error = %v, wantErr %v", err, tt.wantErr) 114 | } 115 | } 116 | } 117 | 118 | //tls := tr.languages[tt.lang].GetDomain().GetTranslations() 119 | //for _, tl := range tls { 120 | // fmt.Printf("%+v\n", tl) 121 | //} 122 | 123 | err = tr.Write(tt.loc) 124 | if err != nil { 125 | t.Errorf("error = %v", err) 126 | } 127 | }) 128 | } 129 | 130 | } 131 | 132 | func TestGeneratedPluralForms(t *testing.T) { 133 | cases := []struct { 134 | lang string 135 | expect string 136 | }{ 137 | {"en", "nplurals=2; plural=(n != 1);"}, 138 | {"ar", "nplurals=6; plural=(n == 0 ? 0 : n == 1 ? 1 : n == 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5);"}, 139 | {"ru", "nplurals=3; plural=(n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : 2);"}, 140 | } 141 | for _, c := range cases { 142 | h, ok := LanguageHeaderTemplates[c.lang] 143 | if !ok { 144 | t.Errorf("Language %s not found in LanguageHeaderTemplates", c.lang) 145 | continue 146 | } 147 | if h.PluralForms != c.expect { 148 | t.Errorf("PluralForms for %s: got %q, want %q", c.lang, h.PluralForms, c.expect) 149 | } 150 | } 151 | } 152 | 153 | func TestEnsureLanguage(t *testing.T) { 154 | tempDir := t.TempDir() 155 | tr := NewTranslator(tempDir, "") 156 | lang := "fr" 157 | poPath := filepath.Join(tempDir, lang+DefaultPoExtension) 158 | 159 | // Ensure file does not exist 160 | if _, err := os.Stat(poPath); err == nil { 161 | os.Remove(poPath) 162 | } 163 | 164 | t.Cleanup(func() { 165 | if err := os.Remove(poPath); err != nil && !os.IsNotExist(err) { 166 | t.Errorf("Failed to clean up PO file: %v", err) 167 | } 168 | }) 169 | 170 | err := tr.EnsureLanguage(lang) 171 | if err != nil { 172 | t.Fatalf("EnsureLanguage failed: %v", err) 173 | } 174 | 175 | // Check file was created 176 | if _, err := os.Stat(poPath); err != nil { 177 | t.Errorf("PO file was not created: %v", err) 178 | } 179 | 180 | // Check language is loaded 181 | if _, ok := tr.languages[lang]; !ok { 182 | t.Errorf("Language %s not loaded in translator", lang) 183 | } 184 | } 185 | 186 | func TestEnsureLanguage_Idempotent(t *testing.T) { 187 | tempDir := t.TempDir() 188 | tr := NewTranslator(tempDir, "") 189 | lang := "fr" 190 | poPath := filepath.Join(tempDir, lang+DefaultPoExtension) 191 | 192 | // Ensure file does not exist 193 | if _, err := os.Stat(poPath); err == nil { 194 | os.Remove(poPath) 195 | } 196 | 197 | t.Cleanup(func() { 198 | if err := os.Remove(poPath); err != nil && !os.IsNotExist(err) { 199 | t.Errorf("Failed to clean up PO file: %v", err) 200 | } 201 | }) 202 | 203 | // First call should create the file 204 | err := tr.EnsureLanguage(lang) 205 | if err != nil { 206 | t.Fatalf("First EnsureLanguage failed: %v", err) 207 | } 208 | 209 | // Write a marker to the file 210 | marker := "# marker line\n" 211 | f, err := os.OpenFile(poPath, os.O_APPEND|os.O_WRONLY, 0644) 212 | if err != nil { 213 | t.Fatalf("Failed to open PO file for appending: %v", err) 214 | } 215 | _, err = f.WriteString(marker) 216 | f.Close() 217 | if err != nil { 218 | t.Fatalf("Failed to write marker: %v", err) 219 | } 220 | 221 | // Second call should NOT overwrite the file 222 | err = tr.EnsureLanguage(lang) 223 | if err != nil { 224 | t.Fatalf("Second EnsureLanguage failed: %v", err) 225 | } 226 | 227 | // Check that marker is still present 228 | data, err := os.ReadFile(poPath) 229 | if err != nil { 230 | t.Fatalf("Failed to read PO file: %v", err) 231 | } 232 | if !contains(string(data), marker) { 233 | t.Errorf("Marker line was overwritten by EnsureLanguage: %s", string(data)) 234 | } 235 | } 236 | 237 | func contains(s, substr string) bool { 238 | return strings.Contains(s, substr) 239 | } 240 | 241 | func NewLocalizer(s string) Localizer { 242 | return mockLocalizer{locale: s} 243 | } 244 | -------------------------------------------------------------------------------- /translator_test.go: -------------------------------------------------------------------------------- 1 | package translator 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var ( 8 | translationsDir = "test_translations" 9 | templateDir = "test_templates" 10 | 11 | lang = "en_US" 12 | langNL = "nl_NL" 13 | multiplural = "multiplural" 14 | ) 15 | 16 | func TestNewTranslator(t *testing.T) { 17 | translator := NewTranslator(translationsDir, templateDir) 18 | 19 | if translator.translationsDir != translationsDir { 20 | t.Errorf("NewTranslator() translationsDir = %v, want %v", translator.translationsDir, translationsDir) 21 | } 22 | 23 | if translator.templateDir != templateDir { 24 | t.Errorf("NewTranslator() templateDir = %v, want %v", translator.templateDir, templateDir) 25 | } 26 | 27 | if translator.languages == nil { 28 | t.Errorf("NewTranslator() languages map should not be nil") 29 | } 30 | 31 | if translator.uniqueKeys == nil { 32 | t.Errorf("NewTranslator() uniqueKeys map should not be nil") 33 | } 34 | } 35 | 36 | func TestSetAndGetPrefixSeparator(t *testing.T) { 37 | translator := NewTranslator(translationsDir, templateDir) 38 | 39 | defaultSeparator := "__." 40 | if sep := translator.PrefixSeparator(); sep != defaultSeparator { 41 | t.Errorf("PrefixSeparator() = %v, want %v", sep, defaultSeparator) 42 | } 43 | 44 | newSeparator := "--" 45 | translator.SetPrefixSeparator(newSeparator) 46 | if sep := translator.PrefixSeparator(); sep != newSeparator { 47 | t.Errorf("SetPrefixSeparator() = %v, want %v", sep, newSeparator) 48 | } 49 | } 50 | 51 | func TestSetLanguage(t *testing.T) { 52 | translator := NewTranslator(translationsDir, templateDir) 53 | 54 | err := translator.SetLanguage(lang) 55 | if err != nil { 56 | t.Errorf("SetLanguage() error = %v", err) 57 | } 58 | 59 | if _, exists := translator.languages[lang]; !exists { 60 | t.Errorf("SetLanguage() language %v not added", lang) 61 | } 62 | 63 | // Test with non-existent language file 64 | nonExistentLang := "non_existent" 65 | err = translator.SetLanguage(nonExistentLang) 66 | if err == nil { 67 | t.Errorf("SetLanguage() should return error for non-existent language %v", nonExistentLang) 68 | } 69 | 70 | if _, exists := translator.languages[nonExistentLang]; exists { 71 | t.Errorf("SetLanguage() should not add non-existent language %v", nonExistentLang) 72 | } 73 | } 74 | 75 | func TestTlFunction(t *testing.T) { 76 | translator := NewTranslator(translationsDir, templateDir) 77 | err := translator.SetLanguage(lang) 78 | if err != nil { 79 | t.Errorf("SetLanguage() error = %v", err) 80 | } 81 | 82 | loc := mockLocalizer{locale: lang} // Assume mockLocalizer implements Localizer interface 83 | 84 | tests := []struct { 85 | key string 86 | expected string 87 | }{ 88 | {"First translation text", "First translation text"}, 89 | {"Second translation text", "Second translation text"}, 90 | {"Third translation text", "Third translation text"}, 91 | {"Non-existent text", "*Non-existent text*"}, // Assuming the behavior for non-translated text 92 | } 93 | 94 | for _, tt := range tests { 95 | result := translator.tl(loc, tt.key) 96 | if result != tt.expected { 97 | t.Errorf("tl() for key '%s' = %s, want %s", tt.key, result, tt.expected) 98 | } 99 | } 100 | } 101 | 102 | func TestTlFunctionWithArgs(t *testing.T) { 103 | translator := NewTranslator(translationsDir, templateDir) 104 | err := translator.SetLanguage(lang) 105 | if err != nil { 106 | t.Errorf("SetLanguage() error = %v", err) 107 | } 108 | 109 | loc := mockLocalizer{locale: lang} 110 | 111 | tests := []struct { 112 | key string 113 | expected string 114 | args []any 115 | }{ 116 | {"First translation text with %s", "First translation text with args", []any{"args"}}, 117 | {"Second translation text with %s", "Second translation text with args", []any{"args"}}, 118 | {"Third translation text with %s", "Third translation text with args", []any{"args"}}, 119 | {"Non-existent text with %s", "*Non-existent text with %s*", []any{"args"}}, // Assuming the behavior for non-translated text 120 | } 121 | 122 | for _, tt := range tests { 123 | result := translator.tl(loc, tt.key, tt.args...) 124 | if result != tt.expected { 125 | t.Errorf("tl() for key '%s' = %s, want %s", tt.key, result, tt.expected) 126 | } 127 | } 128 | } 129 | 130 | func TestTlFunctionNL(t *testing.T) { 131 | translator := NewTranslator(translationsDir, templateDir) 132 | err := translator.SetLanguage(langNL) 133 | if err != nil { 134 | t.Errorf("SetLanguage() error = %v", err) 135 | } 136 | 137 | loc := mockLocalizer{locale: langNL} // Assume mockLocalizer implements Localizer interface 138 | 139 | tests := []struct { 140 | key string 141 | expected string 142 | }{ 143 | {"First translation text", "Eerste vertalingstekst"}, 144 | {"Second translation text", "Tweede vertalingstekst"}, 145 | {"Third translation text", "Derde vertalingstekst"}, 146 | {"Non-existent text", "*Non-existent text*"}, // Assuming the behavior for non-translated text 147 | } 148 | 149 | for _, tt := range tests { 150 | result := translator.tl(loc, tt.key) 151 | if result != tt.expected { 152 | t.Errorf("tl() for key '%s' = %s, want %s", tt.key, result, tt.expected) 153 | } 154 | } 155 | } 156 | 157 | func TestTnFunction(t *testing.T) { 158 | translator := NewTranslator("test_translations", "") 159 | err := translator.SetLanguage(lang) 160 | if err != nil { 161 | t.Errorf("SetLanguage() error = %v", err) 162 | } 163 | 164 | loc := mockLocalizer{locale: lang} 165 | 166 | // Assuming these translations are defined in your .po file 167 | singular := "There is one apple" 168 | plural := "There are many apples" 169 | 170 | // Singular case 171 | resultSingular := translator.tn(loc, singular, plural, 1) 172 | expectedSingular := "There is one apple" // expected translation for singular 173 | if resultSingular != expectedSingular { 174 | t.Errorf("tn() for 1 apple = %s, want %s", resultSingular, expectedSingular) 175 | } 176 | 177 | // Plural case 178 | resultPlural := translator.tn(loc, singular, plural, 5) 179 | expectedPlural := "There are many apples" // expected translation for plural 180 | if resultPlural != expectedPlural { 181 | t.Errorf("tn() for 5 apples = %s, want %s", resultPlural, expectedPlural) 182 | } 183 | } 184 | 185 | func TestTnFunctionMultiPlural(t *testing.T) { 186 | translator := NewTranslator("test_translations", "") 187 | err := translator.SetLanguage(multiplural) 188 | if err != nil { 189 | t.Errorf("SetLanguage() error = %v", err) 190 | } 191 | 192 | loc := mockLocalizer{locale: multiplural} 193 | 194 | tests := []struct { 195 | singular string 196 | plural string 197 | count int 198 | expected string 199 | vars []any 200 | }{ 201 | {"There is one apple", "There are many apples", 1, "C'è una mela", nil}, 202 | {"There is one apple", "There are many apples", 2, "Ci sono due mele", nil}, 203 | {"There is one apple", "There are %d apples", 0, "Ci sono 0 mele", []any{0}}, 204 | {"There is one apple", "There are %d apples", 5, "Ci sono 5 mele", []any{5}}, 205 | // Add more test cases as needed 206 | } 207 | 208 | for _, tt := range tests { 209 | result := translator.tn(loc, tt.singular, tt.plural, tt.count, tt.vars...) 210 | if result != tt.expected { 211 | t.Errorf("tn() with count %d = %s, want %s", tt.count, result, tt.expected) 212 | } else { 213 | t.Logf("tn() with count %d = %s", tt.count, result) 214 | } 215 | } 216 | } 217 | 218 | func TestCheckMissingTranslations(t *testing.T) { 219 | translator := NewTranslator(translationsDir, templateDir) 220 | err := translator.SetLanguage(lang) 221 | if err != nil { 222 | t.Errorf("SetLanguage() error = %v", err) 223 | } 224 | 225 | // Test with missing translations 226 | err = translator.CheckMissingTranslations() 227 | if err != nil { 228 | t.Error(err) 229 | } 230 | } 231 | 232 | // mockLocalizer is a mock implementation of the Localizer interface for testing 233 | type mockLocalizer struct { 234 | locale string 235 | } 236 | 237 | func (m mockLocalizer) GetLocale() string { 238 | return m.locale 239 | } 240 | -------------------------------------------------------------------------------- /translator.go: -------------------------------------------------------------------------------- 1 | package translator 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "html/template" 7 | "os" 8 | "path" 9 | "path/filepath" 10 | "regexp" 11 | "strings" 12 | 13 | "github.com/leonelquinteros/gotext" 14 | ) 15 | 16 | var ( 17 | re = regexp.MustCompile(`{{\s*tl\s+(?:"[^"]+"|[^"]+)\s+"([^"]+)"`) 18 | rePlural = regexp.MustCompile(`{{\s*tn\s+[.\$][a-zA-Z_]\w*\s+"([^"]+)"\s+"([^"]+)"(.*?)\s*}}`) 19 | reCtx = regexp.MustCompile(`{{\s*ctl\s+[.\$][a-zA-Z_]\w*\s+"([^"]+)"\s+"([^"]+)"\s*}}`) 20 | reCtxPlural = regexp.MustCompile(`{{\s*ctn\s+[.\$][a-zA-Z_]\w*\s+"([^"]+)"\s+"([^"]+)"\s+"([^"]+)"`) 21 | ) 22 | 23 | var ( 24 | ErrorLanguageNotFound = fmt.Errorf("language not found") 25 | ErrorLanguageAlreadyExists = fmt.Errorf("language already exists") 26 | ) 27 | 28 | var ( 29 | TemplateExtension = ".gohtml" 30 | DefaultPotFile = "translations.pot" 31 | DefaultPoExtension = ".po" 32 | DefaultNoTranslationTL = "*%s*" 33 | DefaultNoTranslationTN = "*%s/%s*" 34 | DefaultNoTranslationCTL = "*%s/%s*" 35 | DefaultNoTranslationCTN = "*%s/%s/%s*" 36 | DefaultSeparator = "__." 37 | ) 38 | 39 | type ( 40 | //Localizer interface contains the methods that are needed for the translator 41 | Localizer interface { 42 | // GetLocale returns the locale of the localizer, ie. "en_US" 43 | GetLocale() string 44 | } 45 | 46 | Translator struct { 47 | languages map[string]*gotext.Po 48 | translationsDir string 49 | templateDir string 50 | prefixSeparator string 51 | uniqueKeys map[string]uniqueKey 52 | uniqueKeysCtx map[string]map[string]uniqueKey 53 | potFile string 54 | pot *gotext.Po 55 | } 56 | 57 | uniqueKey struct{ singular, plural string } 58 | 59 | translationKey struct { 60 | ctx string 61 | value string 62 | plural bool // true if it's a plural key 63 | } 64 | ) 65 | 66 | func NewTranslator(translationsDir, templateDir string) *Translator { 67 | tr := &Translator{ 68 | translationsDir: translationsDir, 69 | templateDir: templateDir, 70 | potFile: DefaultPotFile, 71 | prefixSeparator: DefaultSeparator, 72 | languages: make(map[string]*gotext.Po), 73 | uniqueKeys: make(map[string]uniqueKey), 74 | uniqueKeysCtx: make(map[string]map[string]uniqueKey), 75 | } 76 | 77 | // load pot file if it exists 78 | tr.pot = gotext.NewPo() 79 | tr.pot.ParseFile(filepath.Join(tr.translationsDir, tr.potFile)) 80 | 81 | return tr 82 | } 83 | 84 | func (t *Translator) SetPrefixSeparator(prefix string) { 85 | t.prefixSeparator = prefix 86 | } 87 | 88 | func (t *Translator) PrefixSeparator() string { 89 | return t.prefixSeparator 90 | } 91 | 92 | func (t *Translator) SetPotFile(potFile string) { 93 | t.potFile = potFile 94 | } 95 | 96 | func (t *Translator) PotFile() string { 97 | return t.potFile 98 | } 99 | 100 | func (t *Translator) SetTranslationsDir(translationsDir string) { 101 | t.translationsDir = translationsDir 102 | } 103 | 104 | func (t *Translator) TranslationsDir() string { 105 | return t.translationsDir 106 | } 107 | 108 | func (t *Translator) SetTemplateDir(templateDir string) { 109 | t.templateDir = templateDir 110 | } 111 | 112 | func (t *Translator) TemplateDir() string { 113 | return t.templateDir 114 | } 115 | 116 | // SetLanguage adds a new language to the translator by loading its .po file. 117 | func (t *Translator) SetLanguage(lang string) error { 118 | po := gotext.NewPo() 119 | 120 | po.ParseFile(filepath.Join(t.translationsDir, lang+DefaultPoExtension)) 121 | 122 | if po.Language == "" { 123 | return ErrorLanguageNotFound 124 | } 125 | 126 | t.languages[lang] = po 127 | return nil 128 | } 129 | 130 | // AddLanguage adds a new language to the translator by loading its .po file. 131 | // Deprecated: use SetLanguage instead 132 | func (t *Translator) AddLanguage(lang string) error { 133 | return t.SetLanguage(lang) 134 | } 135 | 136 | func (t *Translator) EnsureLanguage(lang string) error { 137 | poPath := filepath.Join(t.translationsDir, lang+DefaultPoExtension) 138 | if _, err := os.Stat(poPath); os.IsNotExist(err) { 139 | h := GetHeaderForLanguage(lang) 140 | f, err := os.Create(poPath) 141 | if err != nil { 142 | return fmt.Errorf("failed to create new language file: %w", err) 143 | } 144 | defer f.Close() 145 | _, err = f.WriteString(h.HeaderString()) 146 | if err != nil { 147 | return fmt.Errorf("failed to write header to new language file: %w", err) 148 | } 149 | } 150 | 151 | return t.SetLanguage(lang) 152 | } 153 | 154 | // CheckMissingTranslations scans template files for missing translations and logs them. 155 | func (t *Translator) CheckMissingTranslations() error { 156 | err := t.ScanFiles(t.templateDir) 157 | if err != nil { 158 | return err 159 | } 160 | 161 | var ( 162 | tr = t.pot.GetDomain().GetTranslations() 163 | ctr = t.pot.GetDomain().GetCtxTranslations() 164 | ) 165 | 166 | for key, entry := range t.uniqueKeys { 167 | found := false 168 | for _, potKey := range tr { 169 | if key == potKey.ID { 170 | found = true 171 | break 172 | } 173 | } 174 | 175 | if !found { 176 | err = t.addToPotFile("", entry) 177 | if err != nil { 178 | fmt.Println(err) 179 | } 180 | } 181 | } 182 | 183 | for ctx, uniqueKeys := range t.uniqueKeysCtx { 184 | for key, entry := range uniqueKeys { 185 | found := false 186 | if _, ok := ctr[ctx]; ok { 187 | for _, potKey := range ctr[ctx] { 188 | if key == potKey.ID { 189 | found = true 190 | break 191 | } 192 | } 193 | } 194 | 195 | if !found { 196 | err = t.addToPotFile(ctx, entry) 197 | if err != nil { 198 | fmt.Println(err) 199 | } 200 | } 201 | } 202 | } 203 | 204 | return nil 205 | } 206 | 207 | func (t *Translator) ScanFiles(root string) error { 208 | return filepath.Walk(root, func(path string, info os.FileInfo, err error) error { 209 | if err != nil { 210 | return err 211 | } 212 | if !info.IsDir() && (strings.HasSuffix(path, TemplateExtension)) { 213 | err = t.scanFile(path) 214 | if err != nil { 215 | return err 216 | } 217 | } 218 | return nil 219 | }) 220 | } 221 | 222 | func (t *Translator) scanFile(filename string) error { 223 | data, err := os.ReadFile(filename) 224 | if err != nil { 225 | return err 226 | } 227 | 228 | matches := re.FindAllStringSubmatch(string(data), -1) 229 | for _, match := range matches { 230 | if len(match) > 1 { 231 | t.uniqueKeys[match[1]] = uniqueKey{singular: match[1]} 232 | } 233 | } 234 | 235 | matchesPlural := rePlural.FindAllStringSubmatch(string(data), -1) 236 | for _, match := range matchesPlural { 237 | if len(match) > 2 { 238 | t.uniqueKeys[match[1]] = uniqueKey{singular: match[1], plural: match[2]} 239 | } 240 | } 241 | 242 | matchesCtx := reCtx.FindAllStringSubmatch(string(data), -1) 243 | for _, match := range matchesCtx { 244 | if len(match) > 1 { 245 | if t.uniqueKeysCtx[match[1]] == nil { 246 | t.uniqueKeysCtx[match[1]] = make(map[string]uniqueKey) 247 | } 248 | t.uniqueKeysCtx[match[1]][match[2]] = uniqueKey{singular: match[2]} 249 | } 250 | } 251 | 252 | matchesCtxPlural := reCtxPlural.FindAllStringSubmatch(string(data), -1) 253 | for _, match := range matchesCtxPlural { 254 | if len(match) > 2 { 255 | if t.uniqueKeysCtx[match[1]] == nil { 256 | t.uniqueKeysCtx[match[1]] = make(map[string]uniqueKey) 257 | } 258 | t.uniqueKeysCtx[match[1]][match[2]] = uniqueKey{singular: match[2], plural: match[3]} 259 | } 260 | } 261 | 262 | return nil 263 | } 264 | 265 | func (t *Translator) addToPotFile(ctx string, entry uniqueKey) error { 266 | file, err := os.OpenFile(path.Join(t.translationsDir, t.potFile), os.O_APPEND|os.O_WRONLY, 0644) 267 | if err != nil { 268 | return err 269 | } 270 | defer file.Close() 271 | 272 | buf := bufio.NewWriter(file) 273 | 274 | // Write the new translation entry 275 | var content string 276 | if ctx == "" { 277 | if entry.plural == "" { 278 | content = fmt.Sprintf("\nmsgid \"%s\"\nmsgstr \"\"\n", gotext.EscapeSpecialCharacters(entry.singular)) 279 | } else { 280 | content = fmt.Sprintf("\nmsgid \"%s\"\nmsgid_plural \"%s\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n", gotext.EscapeSpecialCharacters(entry.singular), gotext.EscapeSpecialCharacters(entry.plural)) 281 | } 282 | } else { 283 | if entry.plural == "" { 284 | content = fmt.Sprintf("\nmsgctxt \"%s\"\nmsgid \"%s\"\nmsgstr \"\"\n", gotext.EscapeSpecialCharacters(ctx), gotext.EscapeSpecialCharacters(entry.singular)) 285 | } else { 286 | content = fmt.Sprintf("\nmsgctxt \"%s\"\nmsgid \"%s\"\nmsgid_plural \"%s\"\nmsgstr[0] \"\"\nmsgstr[1] \"\"\n", gotext.EscapeSpecialCharacters(ctx), gotext.EscapeSpecialCharacters(entry.singular), gotext.EscapeSpecialCharacters(entry.plural)) 287 | } 288 | } 289 | 290 | if _, err = buf.WriteString(content); err != nil { 291 | return err 292 | } 293 | 294 | if err = buf.Flush(); err != nil { 295 | return err 296 | } 297 | 298 | // Reload pot file contents 299 | t.pot.ParseFile(filepath.Join(t.translationsDir, t.potFile)) 300 | 301 | return nil 302 | } 303 | 304 | func (t *Translator) addToPotFileIfNotExists(key translationKey) error { 305 | tr := t.pot.GetDomain().GetTranslations() 306 | 307 | if key.ctx == "" { 308 | for _, potKey := range tr { 309 | if key.value == potKey.ID { 310 | return nil 311 | } 312 | } 313 | 314 | return t.addToPotFile("", uniqueKey{singular: key.value}) 315 | } 316 | 317 | ctr := t.pot.GetDomain().GetCtxTranslations() 318 | if ctr[key.ctx] == nil { 319 | return t.addToPotFile(key.ctx, uniqueKey{singular: key.value}) 320 | } 321 | 322 | for _, potKey := range ctr[key.ctx] { 323 | if key.value == potKey.ID { 324 | return nil 325 | } 326 | } 327 | 328 | return t.addToPotFile(key.ctx, uniqueKey{singular: key.value}) 329 | } 330 | 331 | func (t *Translator) FuncMap() template.FuncMap { 332 | return template.FuncMap{ 333 | "tl": t.tl, 334 | "tn": t.tn, 335 | "ctl": t.ctl, 336 | "ctn": t.ctn, 337 | } 338 | } 339 | 340 | // removePrefix removes any prefix ending with prefix separator from the translated string. 341 | func (t *Translator) removePrefix(s string) string { 342 | idx := strings.LastIndex(s, t.PrefixSeparator()) 343 | if idx != -1 { 344 | // Remove everything up to and including the prefix separator 345 | return s[idx+len(t.PrefixSeparator()):] 346 | } 347 | return s 348 | } 349 | -------------------------------------------------------------------------------- /generated_plural_templates.go: -------------------------------------------------------------------------------- 1 | package translator 2 | 3 | // Code generated by script. DO NOT EDIT. 4 | 5 | var LanguageHeaderTemplates = map[string]TranslationFileHeader{ 6 | "mt": {PluralForms: "nplurals=5; plural=(n == 1 ? 0 : n == 2 ? 1 : n == 0 || n % 100 >= 3 && n % 100 <= 10 ? 2 : n % 100 >= 11 && n % 100 <= 19 ? 3 : 4);"}, 7 | "pt_PT": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2);"}, 8 | "shi": {PluralForms: "nplurals=3; plural=(n == 0 || n == 1 ? 0 : n >= 2 && n <= 10 ? 1 : 2);"}, 9 | "smj": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n == 2 ? 1 : 2);"}, 10 | "tpi": {PluralForms: "nplurals=1; plural=(0);"}, 11 | "doi": {PluralForms: "nplurals=2; plural=(n > 1);"}, 12 | "ff": {PluralForms: "nplurals=2; plural=(n > 1);"}, 13 | "kw": {PluralForms: "nplurals=6; plural=(n == 0 ? 0 : n == 1 ? 1 : (n % 100 == 2 || n % 100 == 22 || n % 100 == 42 || n % 100 == 62 || n % 100 == 82) || n % 1000 == 0 && (n % 100000 >= 1000 && n % 100000 <= 20000 || n % 100000 == 40000 || n % 100000 == 60000 || n % 100000 == 80000) || n != 0 && n % 1000000 == 100000 ? 2 : (n % 100 == 3 || n % 100 == 23 || n % 100 == 43 || n % 100 == 63 || n % 100 == 83) ? 3 : n != 1 && (n % 100 == 1 || n % 100 == 21 || n % 100 == 41 || n % 100 == 61 || n % 100 == 81) ? 4 : 5);"}, 14 | "nqo": {PluralForms: "nplurals=1; plural=(0);"}, 15 | "sw": {PluralForms: "nplurals=2; plural=(n != 1);"}, 16 | "tk": {PluralForms: "nplurals=2; plural=(n != 1);"}, 17 | "yue": {PluralForms: "nplurals=1; plural=(0);"}, 18 | "bs": {PluralForms: "nplurals=3; plural=(n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : 2);"}, 19 | "gv": {PluralForms: "nplurals=4; plural=(n % 10 == 1 ? 0 : n % 10 == 2 ? 1 : (n % 100 == 0 || n % 100 == 20 || n % 100 == 40 || n % 100 == 60 || n % 100 == 80) ? 2 : 3);"}, 20 | "lld": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2);"}, 21 | "naq": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n == 2 ? 1 : 2);"}, 22 | "sg": {PluralForms: "nplurals=1; plural=(0);"}, 23 | "ug": {PluralForms: "nplurals=2; plural=(n != 1);"}, 24 | "zh": {PluralForms: "nplurals=1; plural=(0);"}, 25 | "bez": {PluralForms: "nplurals=2; plural=(n != 1);"}, 26 | "lag": {PluralForms: "nplurals=3; plural=(n == 0 ? 0 : n == 1 ? 1 : 2);"}, 27 | "syr": {PluralForms: "nplurals=2; plural=(n != 1);"}, 28 | "tzm": {PluralForms: "nplurals=2; plural=(n >= 2 && (n < 11 || n > 99));"}, 29 | "wae": {PluralForms: "nplurals=2; plural=(n != 1);"}, 30 | "ak": {PluralForms: "nplurals=2; plural=(n > 1);"}, 31 | "an": {PluralForms: "nplurals=2; plural=(n != 1);"}, 32 | "bn": {PluralForms: "nplurals=2; plural=(n > 1);"}, 33 | "ia": {PluralForms: "nplurals=2; plural=(n != 1);"}, 34 | "ka": {PluralForms: "nplurals=2; plural=(n != 1);"}, 35 | "mn": {PluralForms: "nplurals=2; plural=(n != 1);"}, 36 | "pa": {PluralForms: "nplurals=2; plural=(n > 1);"}, 37 | "sq": {PluralForms: "nplurals=2; plural=(n != 1);"}, 38 | "cy": {PluralForms: "nplurals=6; plural=(n == 0 ? 0 : n == 1 ? 1 : n == 2 ? 2 : n == 3 ? 3 : n == 6 ? 4 : 5);"}, 39 | "jgo": {PluralForms: "nplurals=2; plural=(n != 1);"}, 40 | "sl": {PluralForms: "nplurals=4; plural=(n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : (n % 100 == 3 || n % 100 == 4) ? 2 : 3);"}, 41 | "zu": {PluralForms: "nplurals=2; plural=(n > 1);"}, 42 | "rm": {PluralForms: "nplurals=2; plural=(n != 1);"}, 43 | "scn": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2);"}, 44 | "seh": {PluralForms: "nplurals=2; plural=(n != 1);"}, 45 | "sk": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2);"}, 46 | "ve": {PluralForms: "nplurals=2; plural=(n != 1);"}, 47 | "am": {PluralForms: "nplurals=2; plural=(n > 1);"}, 48 | "dsb": {PluralForms: "nplurals=4; plural=(n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : (n % 100 == 3 || n % 100 == 4) ? 2 : 3);"}, 49 | "et": {PluralForms: "nplurals=2; plural=(n != 1);"}, 50 | "fi": {PluralForms: "nplurals=2; plural=(n != 1);"}, 51 | "fr": {PluralForms: "nplurals=3; plural=((n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2);"}, 52 | "ku": {PluralForms: "nplurals=2; plural=(n != 1);"}, 53 | "mas": {PluralForms: "nplurals=2; plural=(n != 1);"}, 54 | "nn": {PluralForms: "nplurals=2; plural=(n != 1);"}, 55 | "fy": {PluralForms: "nplurals=2; plural=(n != 1);"}, 56 | "sc": {PluralForms: "nplurals=2; plural=(n != 1);"}, 57 | "ssy": {PluralForms: "nplurals=2; plural=(n != 1);"}, 58 | "vi": {PluralForms: "nplurals=1; plural=(0);"}, 59 | "gl": {PluralForms: "nplurals=2; plural=(n != 1);"}, 60 | "haw": {PluralForms: "nplurals=2; plural=(n != 1);"}, 61 | "he": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n == 2 ? 1 : 2);"}, 62 | "hr": {PluralForms: "nplurals=3; plural=(n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : 2);"}, 63 | "id": {PluralForms: "nplurals=1; plural=(0);"}, 64 | "kl": {PluralForms: "nplurals=2; plural=(n != 1);"}, 65 | "ko": {PluralForms: "nplurals=1; plural=(0);"}, 66 | "my": {PluralForms: "nplurals=1; plural=(0);"}, 67 | "bo": {PluralForms: "nplurals=1; plural=(0);"}, 68 | "hi": {PluralForms: "nplurals=2; plural=(n > 1);"}, 69 | "ln": {PluralForms: "nplurals=2; plural=(n > 1);"}, 70 | "ms": {PluralForms: "nplurals=1; plural=(0);"}, 71 | "nd": {PluralForms: "nplurals=2; plural=(n != 1);"}, 72 | "nyn": {PluralForms: "nplurals=2; plural=(n != 1);"}, 73 | "om": {PluralForms: "nplurals=2; plural=(n != 1);"}, 74 | "rof": {PluralForms: "nplurals=2; plural=(n != 1);"}, 75 | "ga": {PluralForms: "nplurals=5; plural=(n == 1 ? 0 : n == 2 ? 1 : n >= 3 && n <= 6 ? 2 : n >= 7 && n <= 10 ? 3 : 4);"}, 76 | "af": {PluralForms: "nplurals=2; plural=(n != 1);"}, 77 | "bg": {PluralForms: "nplurals=2; plural=(n != 1);"}, 78 | "chr": {PluralForms: "nplurals=2; plural=(n != 1);"}, 79 | "ee": {PluralForms: "nplurals=2; plural=(n != 1);"}, 80 | "eu": {PluralForms: "nplurals=2; plural=(n != 1);"}, 81 | "fil": {PluralForms: "nplurals=2; plural=(n != 1 && n != 2 && n != 3 && (n % 10 == 4 || n % 10 == 6 || n % 10 == 9));"}, 82 | "gsw": {PluralForms: "nplurals=2; plural=(n != 1);"}, 83 | "cs": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2);"}, 84 | "kab": {PluralForms: "nplurals=2; plural=(n > 1);"}, 85 | "ksh": {PluralForms: "nplurals=3; plural=(n == 0 ? 0 : n == 1 ? 1 : 2);"}, 86 | "mk": {PluralForms: "nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);"}, 87 | "nr": {PluralForms: "nplurals=2; plural=(n != 1);"}, 88 | "sdh": {PluralForms: "nplurals=2; plural=(n != 1);"}, 89 | "smn": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n == 2 ? 1 : 2);"}, 90 | "sn": {PluralForms: "nplurals=2; plural=(n != 1);"}, 91 | "kea": {PluralForms: "nplurals=1; plural=(0);"}, 92 | "brx": {PluralForms: "nplurals=2; plural=(n != 1);"}, 93 | "gu": {PluralForms: "nplurals=2; plural=(n > 1);"}, 94 | "hsb": {PluralForms: "nplurals=4; plural=(n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : (n % 100 == 3 || n % 100 == 4) ? 2 : 3);"}, 95 | "ja": {PluralForms: "nplurals=1; plural=(0);"}, 96 | "nb": {PluralForms: "nplurals=2; plural=(n != 1);"}, 97 | "nnh": {PluralForms: "nplurals=2; plural=(n != 1);"}, 98 | "pcm": {PluralForms: "nplurals=2; plural=(n > 1);"}, 99 | "ar": {PluralForms: "nplurals=6; plural=(n == 0 ? 0 : n == 1 ? 1 : n == 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5);"}, 100 | "ca": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2);"}, 101 | "ceb": {PluralForms: "nplurals=2; plural=(n != 1 && n != 2 && n != 3 && (n % 10 == 4 || n % 10 == 6 || n % 10 == 9));"}, 102 | "dv": {PluralForms: "nplurals=2; plural=(n != 1);"}, 103 | "en": {PluralForms: "nplurals=2; plural=(n != 1);"}, 104 | "hnj": {PluralForms: "nplurals=1; plural=(0);"}, 105 | "kn": {PluralForms: "nplurals=2; plural=(n > 1);"}, 106 | "st": {PluralForms: "nplurals=2; plural=(n != 1);"}, 107 | "bho": {PluralForms: "nplurals=2; plural=(n > 1);"}, 108 | "guw": {PluralForms: "nplurals=2; plural=(n > 1);"}, 109 | "hu": {PluralForms: "nplurals=2; plural=(n != 1);"}, 110 | "hy": {PluralForms: "nplurals=2; plural=(n > 1);"}, 111 | "kaj": {PluralForms: "nplurals=2; plural=(n != 1);"}, 112 | "ny": {PluralForms: "nplurals=2; plural=(n != 1);"}, 113 | "ta": {PluralForms: "nplurals=2; plural=(n != 1);"}, 114 | "ses": {PluralForms: "nplurals=1; plural=(0);"}, 115 | "tig": {PluralForms: "nplurals=2; plural=(n != 1);"}, 116 | "tr": {PluralForms: "nplurals=2; plural=(n != 1);"}, 117 | "uk": {PluralForms: "nplurals=3; plural=(n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : 2);"}, 118 | "bm": {PluralForms: "nplurals=1; plural=(0);"}, 119 | "eo": {PluralForms: "nplurals=2; plural=(n != 1);"}, 120 | "smi": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n == 2 ? 1 : 2);"}, 121 | "su": {PluralForms: "nplurals=1; plural=(0);"}, 122 | "teo": {PluralForms: "nplurals=2; plural=(n != 1);"}, 123 | "vun": {PluralForms: "nplurals=2; plural=(n != 1);"}, 124 | "wa": {PluralForms: "nplurals=2; plural=(n > 1);"}, 125 | "yo": {PluralForms: "nplurals=1; plural=(0);"}, 126 | "az": {PluralForms: "nplurals=2; plural=(n != 1);"}, 127 | "io": {PluralForms: "nplurals=2; plural=(n != 1);"}, 128 | "ky": {PluralForms: "nplurals=2; plural=(n != 1);"}, 129 | "ne": {PluralForms: "nplurals=2; plural=(n != 1);"}, 130 | "so": {PluralForms: "nplurals=2; plural=(n != 1);"}, 131 | "uz": {PluralForms: "nplurals=2; plural=(n != 1);"}, 132 | "vec": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2);"}, 133 | "gd": {PluralForms: "nplurals=4; plural=((n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : (n >= 3 && n <= 10 || n >= 13 && n <= 19) ? 2 : 3);"}, 134 | "fa": {PluralForms: "nplurals=2; plural=(n > 1);"}, 135 | "no": {PluralForms: "nplurals=2; plural=(n != 1);"}, 136 | "sat": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n == 2 ? 1 : 2);"}, 137 | "sv": {PluralForms: "nplurals=2; plural=(n != 1);"}, 138 | "ti": {PluralForms: "nplurals=2; plural=(n > 1);"}, 139 | "ts": {PluralForms: "nplurals=2; plural=(n != 1);"}, 140 | "yi": {PluralForms: "nplurals=2; plural=(n != 1);"}, 141 | "ce": {PluralForms: "nplurals=2; plural=(n != 1);"}, 142 | "es": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2);"}, 143 | "ha": {PluralForms: "nplurals=2; plural=(n != 1);"}, 144 | "ksb": {PluralForms: "nplurals=2; plural=(n != 1);"}, 145 | "lb": {PluralForms: "nplurals=2; plural=(n != 1);"}, 146 | "nl": {PluralForms: "nplurals=2; plural=(n != 1);"}, 147 | "ps": {PluralForms: "nplurals=2; plural=(n != 1);"}, 148 | "ro": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n == 0 || n != 1 && n % 100 >= 1 && n % 100 <= 19 ? 1 : 2);"}, 149 | "asa": {PluralForms: "nplurals=2; plural=(n != 1);"}, 150 | "rwk": {PluralForms: "nplurals=2; plural=(n != 1);"}, 151 | "sma": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n == 2 ? 1 : 2);"}, 152 | "ss": {PluralForms: "nplurals=2; plural=(n != 1);"}, 153 | "tn": {PluralForms: "nplurals=2; plural=(n != 1);"}, 154 | "to": {PluralForms: "nplurals=1; plural=(0);"}, 155 | "ast": {PluralForms: "nplurals=2; plural=(n != 1);"}, 156 | "csw": {PluralForms: "nplurals=2; plural=(n > 1);"}, 157 | "iu": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n == 2 ? 1 : 2);"}, 158 | "kkj": {PluralForms: "nplurals=2; plural=(n != 1);"}, 159 | "lkt": {PluralForms: "nplurals=1; plural=(0);"}, 160 | "lv": {PluralForms: "nplurals=3; plural=(n % 10 == 0 || n % 100 >= 11 && n % 100 <= 19 ? 0 : n % 10 == 1 && n % 100 != 11 ? 1 : 2);"}, 161 | "nah": {PluralForms: "nplurals=2; plural=(n != 1);"}, 162 | "prg": {PluralForms: "nplurals=3; plural=(n % 10 == 0 || n % 100 >= 11 && n % 100 <= 19 ? 0 : n % 10 == 1 && n % 100 != 11 ? 1 : 2);"}, 163 | "bal": {PluralForms: "nplurals=2; plural=(n != 1);"}, 164 | "blo": {PluralForms: "nplurals=3; plural=(n == 0 ? 0 : n == 1 ? 1 : 2);"}, 165 | "kde": {PluralForms: "nplurals=1; plural=(0);"}, 166 | "kk": {PluralForms: "nplurals=2; plural=(n != 1);"}, 167 | "mgo": {PluralForms: "nplurals=2; plural=(n != 1);"}, 168 | "pt": {PluralForms: "nplurals=3; plural=((n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2);"}, 169 | "ru": {PluralForms: "nplurals=3; plural=(n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : 2);"}, 170 | "sh": {PluralForms: "nplurals=3; plural=(n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : 2);"}, 171 | "be": {PluralForms: "nplurals=3; plural=(n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : 2);"}, 172 | "br": {PluralForms: "nplurals=5; plural=(n % 10 == 1 && n % 100 != 11 && n % 100 != 71 && n % 100 != 91 ? 0 : n % 10 == 2 && n % 100 != 12 && n % 100 != 72 && n % 100 != 92 ? 1 : ((n % 10 == 3 || n % 10 == 4) || n % 10 == 9) && (n % 100 < 10 || n % 100 > 19) && (n % 100 < 70 || n % 100 > 79) && (n % 100 < 90 || n % 100 > 99) ? 2 : n != 0 && n % 1000000 == 0 ? 3 : 4);"}, 173 | "is": {PluralForms: "nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);"}, 174 | "os": {PluralForms: "nplurals=2; plural=(n != 1);"}, 175 | "tl": {PluralForms: "nplurals=2; plural=(n != 1 && n != 2 && n != 3 && (n % 10 == 4 || n % 10 == 6 || n % 10 == 9));"}, 176 | "xog": {PluralForms: "nplurals=2; plural=(n != 1);"}, 177 | "lt": {PluralForms: "nplurals=3; plural=(n % 10 == 1 && (n % 100 < 11 || n % 100 > 19) ? 0 : n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19) ? 1 : 2);"}, 178 | "ars": {PluralForms: "nplurals=6; plural=(n == 0 ? 0 : n == 1 ? 1 : n == 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5);"}, 179 | "da": {PluralForms: "nplurals=2; plural=(n != 1);"}, 180 | "it": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2);"}, 181 | "mr": {PluralForms: "nplurals=2; plural=(n != 1);"}, 182 | "osa": {PluralForms: "nplurals=1; plural=(0);"}, 183 | "te": {PluralForms: "nplurals=2; plural=(n != 1);"}, 184 | "th": {PluralForms: "nplurals=1; plural=(0);"}, 185 | "bem": {PluralForms: "nplurals=2; plural=(n != 1);"}, 186 | "ii": {PluralForms: "nplurals=1; plural=(0);"}, 187 | "jmc": {PluralForms: "nplurals=2; plural=(n != 1);"}, 188 | "ur": {PluralForms: "nplurals=2; plural=(n != 1);"}, 189 | "wo": {PluralForms: "nplurals=1; plural=(0);"}, 190 | "lij": {PluralForms: "nplurals=2; plural=(n != 1);"}, 191 | "km": {PluralForms: "nplurals=1; plural=(0);"}, 192 | "pap": {PluralForms: "nplurals=2; plural=(n != 1);"}, 193 | "sd": {PluralForms: "nplurals=2; plural=(n != 1);"}, 194 | "sr": {PluralForms: "nplurals=3; plural=(n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : 2);"}, 195 | "lg": {PluralForms: "nplurals=2; plural=(n != 1);"}, 196 | "mg": {PluralForms: "nplurals=2; plural=(n > 1);"}, 197 | "ckb": {PluralForms: "nplurals=2; plural=(n != 1);"}, 198 | "el": {PluralForms: "nplurals=2; plural=(n != 1);"}, 199 | "ig": {PluralForms: "nplurals=1; plural=(0);"}, 200 | "ks": {PluralForms: "nplurals=2; plural=(n != 1);"}, 201 | "se": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n == 2 ? 1 : 2);"}, 202 | "si": {PluralForms: "nplurals=2; plural=(n > 1);"}, 203 | "jv": {PluralForms: "nplurals=1; plural=(0);"}, 204 | "kcg": {PluralForms: "nplurals=2; plural=(n != 1);"}, 205 | "lo": {PluralForms: "nplurals=1; plural=(0);"}, 206 | "cgg": {PluralForms: "nplurals=2; plural=(n != 1);"}, 207 | "de": {PluralForms: "nplurals=2; plural=(n != 1);"}, 208 | "dz": {PluralForms: "nplurals=1; plural=(0);"}, 209 | "fur": {PluralForms: "nplurals=2; plural=(n != 1);"}, 210 | "or": {PluralForms: "nplurals=2; plural=(n != 1);"}, 211 | "jbo": {PluralForms: "nplurals=1; plural=(0);"}, 212 | "nso": {PluralForms: "nplurals=2; plural=(n > 1);"}, 213 | "pl": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : 2);"}, 214 | "saq": {PluralForms: "nplurals=2; plural=(n != 1);"}, 215 | "vo": {PluralForms: "nplurals=2; plural=(n != 1);"}, 216 | "fo": {PluralForms: "nplurals=2; plural=(n != 1);"}, 217 | "ml": {PluralForms: "nplurals=2; plural=(n != 1);"}, 218 | "sah": {PluralForms: "nplurals=1; plural=(0);"}, 219 | "sms": {PluralForms: "nplurals=3; plural=(n == 1 ? 0 : n == 2 ? 1 : 2);"}, 220 | "xh": {PluralForms: "nplurals=2; plural=(n != 1);"}, 221 | "as": {PluralForms: "nplurals=2; plural=(n > 1);"}, 222 | } 223 | -------------------------------------------------------------------------------- /plurals.json: -------------------------------------------------------------------------------- 1 | { 2 | "af": { 3 | "name": "Afrikaans", 4 | "formulas": { 5 | "standard": "n != 1", 6 | "php": "n != 1" 7 | }, 8 | "plurals": 2, 9 | "cases": [ 10 | "one", 11 | "other" 12 | ], 13 | "examples": { 14 | "one": "1", 15 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 16 | } 17 | }, 18 | "ak": { 19 | "name": "Akan", 20 | "formulas": { 21 | "standard": "n > 1", 22 | "php": "n > 1" 23 | }, 24 | "plurals": 2, 25 | "cases": [ 26 | "one", 27 | "other" 28 | ], 29 | "examples": { 30 | "one": "0, 1", 31 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 32 | } 33 | }, 34 | "am": { 35 | "name": "Amharic", 36 | "formulas": { 37 | "standard": "n > 1", 38 | "php": "n > 1" 39 | }, 40 | "plurals": 2, 41 | "cases": [ 42 | "one", 43 | "other" 44 | ], 45 | "examples": { 46 | "one": "0, 1", 47 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 48 | } 49 | }, 50 | "an": { 51 | "name": "Aragonese", 52 | "formulas": { 53 | "standard": "n != 1", 54 | "php": "n != 1" 55 | }, 56 | "plurals": 2, 57 | "cases": [ 58 | "one", 59 | "other" 60 | ], 61 | "examples": { 62 | "one": "1", 63 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 64 | } 65 | }, 66 | "ar": { 67 | "name": "Arabic", 68 | "formulas": { 69 | "standard": "n == 0 ? 0 : n == 1 ? 1 : n == 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5", 70 | "php": "(n == 0) ? 0 : ((n == 1) ? 1 : ((n == 2) ? 2 : ((n % 100 >= 3 && n % 100 <= 10) ? 3 : ((n % 100 >= 11 && n % 100 <= 99) ? 4 : 5))))" 71 | }, 72 | "plurals": 6, 73 | "cases": [ 74 | "zero", 75 | "one", 76 | "two", 77 | "few", 78 | "many", 79 | "other" 80 | ], 81 | "examples": { 82 | "zero": "0", 83 | "one": "1", 84 | "two": "2", 85 | "few": "3~10, 103~110, 1003, …", 86 | "many": "11~26, 111, 1011, …", 87 | "other": "100~102, 200~202, 300~302, 400~402, 500~502, 600, 1000, 10000, 100000, 1000000, …" 88 | } 89 | }, 90 | "ars": { 91 | "name": "Najdi Arabic", 92 | "formulas": { 93 | "standard": "n == 0 ? 0 : n == 1 ? 1 : n == 2 ? 2 : n % 100 >= 3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5", 94 | "php": "(n == 0) ? 0 : ((n == 1) ? 1 : ((n == 2) ? 2 : ((n % 100 >= 3 && n % 100 <= 10) ? 3 : ((n % 100 >= 11 && n % 100 <= 99) ? 4 : 5))))" 95 | }, 96 | "plurals": 6, 97 | "cases": [ 98 | "zero", 99 | "one", 100 | "two", 101 | "few", 102 | "many", 103 | "other" 104 | ], 105 | "examples": { 106 | "zero": "0", 107 | "one": "1", 108 | "two": "2", 109 | "few": "3~10, 103~110, 1003, …", 110 | "many": "11~26, 111, 1011, …", 111 | "other": "100~102, 200~202, 300~302, 400~402, 500~502, 600, 1000, 10000, 100000, 1000000, …" 112 | } 113 | }, 114 | "as": { 115 | "name": "Assamese", 116 | "formulas": { 117 | "standard": "n > 1", 118 | "php": "n > 1" 119 | }, 120 | "plurals": 2, 121 | "cases": [ 122 | "one", 123 | "other" 124 | ], 125 | "examples": { 126 | "one": "0, 1", 127 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 128 | } 129 | }, 130 | "asa": { 131 | "name": "Asu", 132 | "formulas": { 133 | "standard": "n != 1", 134 | "php": "n != 1" 135 | }, 136 | "plurals": 2, 137 | "cases": [ 138 | "one", 139 | "other" 140 | ], 141 | "examples": { 142 | "one": "1", 143 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 144 | } 145 | }, 146 | "ast": { 147 | "name": "Asturian", 148 | "formulas": { 149 | "standard": "n != 1", 150 | "php": "n != 1" 151 | }, 152 | "plurals": 2, 153 | "cases": [ 154 | "one", 155 | "other" 156 | ], 157 | "examples": { 158 | "one": "1", 159 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 160 | } 161 | }, 162 | "az": { 163 | "name": "Azerbaijani", 164 | "formulas": { 165 | "standard": "n != 1", 166 | "php": "n != 1" 167 | }, 168 | "plurals": 2, 169 | "cases": [ 170 | "one", 171 | "other" 172 | ], 173 | "examples": { 174 | "one": "1", 175 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 176 | } 177 | }, 178 | "bal": { 179 | "name": "Baluchi", 180 | "formulas": { 181 | "standard": "n != 1", 182 | "php": "n != 1" 183 | }, 184 | "plurals": 2, 185 | "cases": [ 186 | "one", 187 | "other" 188 | ], 189 | "examples": { 190 | "one": "1", 191 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 192 | } 193 | }, 194 | "be": { 195 | "name": "Belarusian", 196 | "formulas": { 197 | "standard": "n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : 2", 198 | "php": "(n % 10 == 1 && n % 100 != 11) ? 0 : ((n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) ? 1 : 2)" 199 | }, 200 | "plurals": 3, 201 | "cases": [ 202 | "one", 203 | "few", 204 | "other" 205 | ], 206 | "examples": { 207 | "one": "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …", 208 | "few": "2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …", 209 | "other": "0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 210 | } 211 | }, 212 | "bem": { 213 | "name": "Bemba", 214 | "formulas": { 215 | "standard": "n != 1", 216 | "php": "n != 1" 217 | }, 218 | "plurals": 2, 219 | "cases": [ 220 | "one", 221 | "other" 222 | ], 223 | "examples": { 224 | "one": "1", 225 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 226 | } 227 | }, 228 | "bez": { 229 | "name": "Bena", 230 | "formulas": { 231 | "standard": "n != 1", 232 | "php": "n != 1" 233 | }, 234 | "plurals": 2, 235 | "cases": [ 236 | "one", 237 | "other" 238 | ], 239 | "examples": { 240 | "one": "1", 241 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 242 | } 243 | }, 244 | "bg": { 245 | "name": "Bulgarian", 246 | "formulas": { 247 | "standard": "n != 1", 248 | "php": "n != 1" 249 | }, 250 | "plurals": 2, 251 | "cases": [ 252 | "one", 253 | "other" 254 | ], 255 | "examples": { 256 | "one": "1", 257 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 258 | } 259 | }, 260 | "bho": { 261 | "name": "Bhojpuri", 262 | "formulas": { 263 | "standard": "n > 1", 264 | "php": "n > 1" 265 | }, 266 | "plurals": 2, 267 | "cases": [ 268 | "one", 269 | "other" 270 | ], 271 | "examples": { 272 | "one": "0, 1", 273 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 274 | } 275 | }, 276 | "blo": { 277 | "name": "Anii", 278 | "formulas": { 279 | "standard": "n == 0 ? 0 : n == 1 ? 1 : 2", 280 | "php": "(n == 0) ? 0 : ((n == 1) ? 1 : 2)" 281 | }, 282 | "plurals": 3, 283 | "cases": [ 284 | "zero", 285 | "one", 286 | "other" 287 | ], 288 | "examples": { 289 | "zero": "0", 290 | "one": "1", 291 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 292 | } 293 | }, 294 | "bm": { 295 | "name": "Bambara", 296 | "formulas": { 297 | "standard": "0", 298 | "php": "0" 299 | }, 300 | "plurals": 1, 301 | "cases": [ 302 | "other" 303 | ], 304 | "examples": { 305 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 306 | } 307 | }, 308 | "bn": { 309 | "name": "Bangla", 310 | "formulas": { 311 | "standard": "n > 1", 312 | "php": "n > 1" 313 | }, 314 | "plurals": 2, 315 | "cases": [ 316 | "one", 317 | "other" 318 | ], 319 | "examples": { 320 | "one": "0, 1", 321 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 322 | } 323 | }, 324 | "bo": { 325 | "name": "Tibetan", 326 | "formulas": { 327 | "standard": "0", 328 | "php": "0" 329 | }, 330 | "plurals": 1, 331 | "cases": [ 332 | "other" 333 | ], 334 | "examples": { 335 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 336 | } 337 | }, 338 | "br": { 339 | "name": "Breton", 340 | "formulas": { 341 | "standard": "n % 10 == 1 && n % 100 != 11 && n % 100 != 71 && n % 100 != 91 ? 0 : n % 10 == 2 && n % 100 != 12 && n % 100 != 72 && n % 100 != 92 ? 1 : ((n % 10 == 3 || n % 10 == 4) || n % 10 == 9) && (n % 100 < 10 || n % 100 > 19) && (n % 100 < 70 || n % 100 > 79) && (n % 100 < 90 || n % 100 > 99) ? 2 : n != 0 && n % 1000000 == 0 ? 3 : 4", 342 | "php": "(n % 10 == 1 && n % 100 != 11 && n % 100 != 71 && n % 100 != 91) ? 0 : ((n % 10 == 2 && n % 100 != 12 && n % 100 != 72 && n % 100 != 92) ? 1 : ((((n % 10 == 3 || n % 10 == 4) || n % 10 == 9) && (n % 100 < 10 || n % 100 > 19) && (n % 100 < 70 || n % 100 > 79) && (n % 100 < 90 || n % 100 > 99)) ? 2 : ((n != 0 && n % 1000000 == 0) ? 3 : 4)))" 343 | }, 344 | "plurals": 5, 345 | "cases": [ 346 | "one", 347 | "two", 348 | "few", 349 | "many", 350 | "other" 351 | ], 352 | "examples": { 353 | "one": "1, 21, 31, 41, 51, 61, 81, 101, 1001, …", 354 | "two": "2, 22, 32, 42, 52, 62, 82, 102, 1002, …", 355 | "few": "3, 4, 9, 23, 24, 29, 33, 34, 39, 43, 44, 49, 103, 1003, …", 356 | "many": "1000000, …", 357 | "other": "0, 5~8, 10~20, 100, 1000, 10000, 100000, …" 358 | } 359 | }, 360 | "brx": { 361 | "name": "Bodo", 362 | "formulas": { 363 | "standard": "n != 1", 364 | "php": "n != 1" 365 | }, 366 | "plurals": 2, 367 | "cases": [ 368 | "one", 369 | "other" 370 | ], 371 | "examples": { 372 | "one": "1", 373 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 374 | } 375 | }, 376 | "bs": { 377 | "name": "Bosnian", 378 | "formulas": { 379 | "standard": "n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : 2", 380 | "php": "(n % 10 == 1 && n % 100 != 11) ? 0 : ((n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) ? 1 : 2)" 381 | }, 382 | "plurals": 3, 383 | "cases": [ 384 | "one", 385 | "few", 386 | "other" 387 | ], 388 | "examples": { 389 | "one": "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …", 390 | "few": "2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …", 391 | "other": "0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 392 | } 393 | }, 394 | "ca": { 395 | "name": "Catalan", 396 | "formulas": { 397 | "standard": "n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2", 398 | "php": "(n == 1) ? 0 : ((n != 0 && n % 1000000 == 0) ? 1 : 2)" 399 | }, 400 | "plurals": 3, 401 | "cases": [ 402 | "one", 403 | "many", 404 | "other" 405 | ], 406 | "examples": { 407 | "one": "1", 408 | "many": "1000000, 1c6, 2c6, 3c6, 4c6, 5c6, 6c6, …", 409 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1c3, 2c3, 3c3, 4c3, 5c3, 6c3, …" 410 | } 411 | }, 412 | "ce": { 413 | "name": "Chechen", 414 | "formulas": { 415 | "standard": "n != 1", 416 | "php": "n != 1" 417 | }, 418 | "plurals": 2, 419 | "cases": [ 420 | "one", 421 | "other" 422 | ], 423 | "examples": { 424 | "one": "1", 425 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 426 | } 427 | }, 428 | "ceb": { 429 | "name": "Cebuano", 430 | "formulas": { 431 | "standard": "n != 1 && n != 2 && n != 3 && (n % 10 == 4 || n % 10 == 6 || n % 10 == 9)", 432 | "php": "n != 1 && n != 2 && n != 3 && (n % 10 == 4 || n % 10 == 6 || n % 10 == 9)" 433 | }, 434 | "plurals": 2, 435 | "cases": [ 436 | "one", 437 | "other" 438 | ], 439 | "examples": { 440 | "one": "0~3, 5, 7, 8, 10~13, 15, 17, 18, 20, 21, 100, 1000, 10000, 100000, 1000000, …", 441 | "other": "4, 6, 9, 14, 16, 19, 24, 26, 104, 1004, …" 442 | } 443 | }, 444 | "cgg": { 445 | "name": "Chiga", 446 | "formulas": { 447 | "standard": "n != 1", 448 | "php": "n != 1" 449 | }, 450 | "plurals": 2, 451 | "cases": [ 452 | "one", 453 | "other" 454 | ], 455 | "examples": { 456 | "one": "1", 457 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 458 | } 459 | }, 460 | "chr": { 461 | "name": "Cherokee", 462 | "formulas": { 463 | "standard": "n != 1", 464 | "php": "n != 1" 465 | }, 466 | "plurals": 2, 467 | "cases": [ 468 | "one", 469 | "other" 470 | ], 471 | "examples": { 472 | "one": "1", 473 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 474 | } 475 | }, 476 | "ckb": { 477 | "name": "Central Kurdish", 478 | "formulas": { 479 | "standard": "n != 1", 480 | "php": "n != 1" 481 | }, 482 | "plurals": 2, 483 | "cases": [ 484 | "one", 485 | "other" 486 | ], 487 | "examples": { 488 | "one": "1", 489 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 490 | } 491 | }, 492 | "cs": { 493 | "name": "Czech", 494 | "formulas": { 495 | "standard": "n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2", 496 | "php": "(n == 1) ? 0 : ((n >= 2 && n <= 4) ? 1 : 2)" 497 | }, 498 | "plurals": 3, 499 | "cases": [ 500 | "one", 501 | "few", 502 | "other" 503 | ], 504 | "examples": { 505 | "one": "1", 506 | "few": "2~4", 507 | "other": "0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 508 | } 509 | }, 510 | "csw": { 511 | "name": "Swampy Cree", 512 | "formulas": { 513 | "standard": "n > 1", 514 | "php": "n > 1" 515 | }, 516 | "plurals": 2, 517 | "cases": [ 518 | "one", 519 | "other" 520 | ], 521 | "examples": { 522 | "one": "0, 1", 523 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 524 | } 525 | }, 526 | "cy": { 527 | "name": "Welsh", 528 | "formulas": { 529 | "standard": "n == 0 ? 0 : n == 1 ? 1 : n == 2 ? 2 : n == 3 ? 3 : n == 6 ? 4 : 5", 530 | "php": "(n == 0) ? 0 : ((n == 1) ? 1 : ((n == 2) ? 2 : ((n == 3) ? 3 : ((n == 6) ? 4 : 5))))" 531 | }, 532 | "plurals": 6, 533 | "cases": [ 534 | "zero", 535 | "one", 536 | "two", 537 | "few", 538 | "many", 539 | "other" 540 | ], 541 | "examples": { 542 | "zero": "0", 543 | "one": "1", 544 | "two": "2", 545 | "few": "3", 546 | "many": "6", 547 | "other": "4, 5, 7~20, 100, 1000, 10000, 100000, 1000000, …" 548 | } 549 | }, 550 | "da": { 551 | "name": "Danish", 552 | "formulas": { 553 | "standard": "n != 1", 554 | "php": "n != 1" 555 | }, 556 | "plurals": 2, 557 | "cases": [ 558 | "one", 559 | "other" 560 | ], 561 | "examples": { 562 | "one": "1", 563 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 564 | } 565 | }, 566 | "de": { 567 | "name": "German", 568 | "formulas": { 569 | "standard": "n != 1", 570 | "php": "n != 1" 571 | }, 572 | "plurals": 2, 573 | "cases": [ 574 | "one", 575 | "other" 576 | ], 577 | "examples": { 578 | "one": "1", 579 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 580 | } 581 | }, 582 | "doi": { 583 | "name": "Dogri", 584 | "formulas": { 585 | "standard": "n > 1", 586 | "php": "n > 1" 587 | }, 588 | "plurals": 2, 589 | "cases": [ 590 | "one", 591 | "other" 592 | ], 593 | "examples": { 594 | "one": "0, 1", 595 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 596 | } 597 | }, 598 | "dsb": { 599 | "name": "Lower Sorbian", 600 | "formulas": { 601 | "standard": "n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : (n % 100 == 3 || n % 100 == 4) ? 2 : 3", 602 | "php": "(n % 100 == 1) ? 0 : ((n % 100 == 2) ? 1 : ((n % 100 == 3 || n % 100 == 4) ? 2 : 3))" 603 | }, 604 | "plurals": 4, 605 | "cases": [ 606 | "one", 607 | "two", 608 | "few", 609 | "other" 610 | ], 611 | "examples": { 612 | "one": "1, 101, 201, 301, 401, 501, 601, 701, 1001, …", 613 | "two": "2, 102, 202, 302, 402, 502, 602, 702, 1002, …", 614 | "few": "3, 4, 103, 104, 203, 204, 303, 304, 403, 404, 503, 504, 603, 604, 703, 704, 1003, …", 615 | "other": "0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 616 | } 617 | }, 618 | "dv": { 619 | "name": "Divehi", 620 | "formulas": { 621 | "standard": "n != 1", 622 | "php": "n != 1" 623 | }, 624 | "plurals": 2, 625 | "cases": [ 626 | "one", 627 | "other" 628 | ], 629 | "examples": { 630 | "one": "1", 631 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 632 | } 633 | }, 634 | "dz": { 635 | "name": "Dzongkha", 636 | "formulas": { 637 | "standard": "0", 638 | "php": "0" 639 | }, 640 | "plurals": 1, 641 | "cases": [ 642 | "other" 643 | ], 644 | "examples": { 645 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 646 | } 647 | }, 648 | "ee": { 649 | "name": "Ewe", 650 | "formulas": { 651 | "standard": "n != 1", 652 | "php": "n != 1" 653 | }, 654 | "plurals": 2, 655 | "cases": [ 656 | "one", 657 | "other" 658 | ], 659 | "examples": { 660 | "one": "1", 661 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 662 | } 663 | }, 664 | "el": { 665 | "name": "Greek", 666 | "formulas": { 667 | "standard": "n != 1", 668 | "php": "n != 1" 669 | }, 670 | "plurals": 2, 671 | "cases": [ 672 | "one", 673 | "other" 674 | ], 675 | "examples": { 676 | "one": "1", 677 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 678 | } 679 | }, 680 | "en": { 681 | "name": "English", 682 | "formulas": { 683 | "standard": "n != 1", 684 | "php": "n != 1" 685 | }, 686 | "plurals": 2, 687 | "cases": [ 688 | "one", 689 | "other" 690 | ], 691 | "examples": { 692 | "one": "1", 693 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 694 | } 695 | }, 696 | "eo": { 697 | "name": "Esperanto", 698 | "formulas": { 699 | "standard": "n != 1", 700 | "php": "n != 1" 701 | }, 702 | "plurals": 2, 703 | "cases": [ 704 | "one", 705 | "other" 706 | ], 707 | "examples": { 708 | "one": "1", 709 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 710 | } 711 | }, 712 | "es": { 713 | "name": "Spanish", 714 | "formulas": { 715 | "standard": "n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2", 716 | "php": "(n == 1) ? 0 : ((n != 0 && n % 1000000 == 0) ? 1 : 2)" 717 | }, 718 | "plurals": 3, 719 | "cases": [ 720 | "one", 721 | "many", 722 | "other" 723 | ], 724 | "examples": { 725 | "one": "1", 726 | "many": "1000000, 1c6, 2c6, 3c6, 4c6, 5c6, 6c6, …", 727 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1c3, 2c3, 3c3, 4c3, 5c3, 6c3, …" 728 | } 729 | }, 730 | "et": { 731 | "name": "Estonian", 732 | "formulas": { 733 | "standard": "n != 1", 734 | "php": "n != 1" 735 | }, 736 | "plurals": 2, 737 | "cases": [ 738 | "one", 739 | "other" 740 | ], 741 | "examples": { 742 | "one": "1", 743 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 744 | } 745 | }, 746 | "eu": { 747 | "name": "Basque", 748 | "formulas": { 749 | "standard": "n != 1", 750 | "php": "n != 1" 751 | }, 752 | "plurals": 2, 753 | "cases": [ 754 | "one", 755 | "other" 756 | ], 757 | "examples": { 758 | "one": "1", 759 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 760 | } 761 | }, 762 | "fa": { 763 | "name": "Persian", 764 | "formulas": { 765 | "standard": "n > 1", 766 | "php": "n > 1" 767 | }, 768 | "plurals": 2, 769 | "cases": [ 770 | "one", 771 | "other" 772 | ], 773 | "examples": { 774 | "one": "0, 1", 775 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 776 | } 777 | }, 778 | "ff": { 779 | "name": "Fula", 780 | "formulas": { 781 | "standard": "n > 1", 782 | "php": "n > 1" 783 | }, 784 | "plurals": 2, 785 | "cases": [ 786 | "one", 787 | "other" 788 | ], 789 | "examples": { 790 | "one": "0, 1", 791 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 792 | } 793 | }, 794 | "fi": { 795 | "name": "Finnish", 796 | "formulas": { 797 | "standard": "n != 1", 798 | "php": "n != 1" 799 | }, 800 | "plurals": 2, 801 | "cases": [ 802 | "one", 803 | "other" 804 | ], 805 | "examples": { 806 | "one": "1", 807 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 808 | } 809 | }, 810 | "fil": { 811 | "name": "Filipino", 812 | "formulas": { 813 | "standard": "n != 1 && n != 2 && n != 3 && (n % 10 == 4 || n % 10 == 6 || n % 10 == 9)", 814 | "php": "n != 1 && n != 2 && n != 3 && (n % 10 == 4 || n % 10 == 6 || n % 10 == 9)" 815 | }, 816 | "plurals": 2, 817 | "cases": [ 818 | "one", 819 | "other" 820 | ], 821 | "examples": { 822 | "one": "0~3, 5, 7, 8, 10~13, 15, 17, 18, 20, 21, 100, 1000, 10000, 100000, 1000000, …", 823 | "other": "4, 6, 9, 14, 16, 19, 24, 26, 104, 1004, …" 824 | } 825 | }, 826 | "fo": { 827 | "name": "Faroese", 828 | "formulas": { 829 | "standard": "n != 1", 830 | "php": "n != 1" 831 | }, 832 | "plurals": 2, 833 | "cases": [ 834 | "one", 835 | "other" 836 | ], 837 | "examples": { 838 | "one": "1", 839 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 840 | } 841 | }, 842 | "fr": { 843 | "name": "French", 844 | "formulas": { 845 | "standard": "(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2", 846 | "php": "(n == 0 || n == 1) ? 0 : ((n != 0 && n % 1000000 == 0) ? 1 : 2)" 847 | }, 848 | "plurals": 3, 849 | "cases": [ 850 | "one", 851 | "many", 852 | "other" 853 | ], 854 | "examples": { 855 | "one": "0, 1", 856 | "many": "1000000, 1c6, 2c6, 3c6, 4c6, 5c6, 6c6, …", 857 | "other": "2~17, 100, 1000, 10000, 100000, 1c3, 2c3, 3c3, 4c3, 5c3, 6c3, …" 858 | } 859 | }, 860 | "fur": { 861 | "name": "Friulian", 862 | "formulas": { 863 | "standard": "n != 1", 864 | "php": "n != 1" 865 | }, 866 | "plurals": 2, 867 | "cases": [ 868 | "one", 869 | "other" 870 | ], 871 | "examples": { 872 | "one": "1", 873 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 874 | } 875 | }, 876 | "fy": { 877 | "name": "Western Frisian", 878 | "formulas": { 879 | "standard": "n != 1", 880 | "php": "n != 1" 881 | }, 882 | "plurals": 2, 883 | "cases": [ 884 | "one", 885 | "other" 886 | ], 887 | "examples": { 888 | "one": "1", 889 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 890 | } 891 | }, 892 | "ga": { 893 | "name": "Irish", 894 | "formulas": { 895 | "standard": "n == 1 ? 0 : n == 2 ? 1 : n >= 3 && n <= 6 ? 2 : n >= 7 && n <= 10 ? 3 : 4", 896 | "php": "(n == 1) ? 0 : ((n == 2) ? 1 : ((n >= 3 && n <= 6) ? 2 : ((n >= 7 && n <= 10) ? 3 : 4)))" 897 | }, 898 | "plurals": 5, 899 | "cases": [ 900 | "one", 901 | "two", 902 | "few", 903 | "many", 904 | "other" 905 | ], 906 | "examples": { 907 | "one": "1", 908 | "two": "2", 909 | "few": "3~6", 910 | "many": "7~10", 911 | "other": "0, 11~25, 100, 1000, 10000, 100000, 1000000, …" 912 | } 913 | }, 914 | "gd": { 915 | "name": "Scottish Gaelic", 916 | "formulas": { 917 | "standard": "(n == 1 || n == 11) ? 0 : (n == 2 || n == 12) ? 1 : (n >= 3 && n <= 10 || n >= 13 && n <= 19) ? 2 : 3", 918 | "php": "(n == 1 || n == 11) ? 0 : ((n == 2 || n == 12) ? 1 : ((n >= 3 && n <= 10 || n >= 13 && n <= 19) ? 2 : 3))" 919 | }, 920 | "plurals": 4, 921 | "cases": [ 922 | "one", 923 | "two", 924 | "few", 925 | "other" 926 | ], 927 | "examples": { 928 | "one": "1, 11", 929 | "two": "2, 12", 930 | "few": "3~10, 13~19", 931 | "other": "0, 20~34, 100, 1000, 10000, 100000, 1000000, …" 932 | } 933 | }, 934 | "gl": { 935 | "name": "Galician", 936 | "formulas": { 937 | "standard": "n != 1", 938 | "php": "n != 1" 939 | }, 940 | "plurals": 2, 941 | "cases": [ 942 | "one", 943 | "other" 944 | ], 945 | "examples": { 946 | "one": "1", 947 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 948 | } 949 | }, 950 | "gsw": { 951 | "name": "Swiss German", 952 | "formulas": { 953 | "standard": "n != 1", 954 | "php": "n != 1" 955 | }, 956 | "plurals": 2, 957 | "cases": [ 958 | "one", 959 | "other" 960 | ], 961 | "examples": { 962 | "one": "1", 963 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 964 | } 965 | }, 966 | "gu": { 967 | "name": "Gujarati", 968 | "formulas": { 969 | "standard": "n > 1", 970 | "php": "n > 1" 971 | }, 972 | "plurals": 2, 973 | "cases": [ 974 | "one", 975 | "other" 976 | ], 977 | "examples": { 978 | "one": "0, 1", 979 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 980 | } 981 | }, 982 | "guw": { 983 | "name": "Gun", 984 | "formulas": { 985 | "standard": "n > 1", 986 | "php": "n > 1" 987 | }, 988 | "plurals": 2, 989 | "cases": [ 990 | "one", 991 | "other" 992 | ], 993 | "examples": { 994 | "one": "0, 1", 995 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 996 | } 997 | }, 998 | "gv": { 999 | "name": "Manx", 1000 | "formulas": { 1001 | "standard": "n % 10 == 1 ? 0 : n % 10 == 2 ? 1 : (n % 100 == 0 || n % 100 == 20 || n % 100 == 40 || n % 100 == 60 || n % 100 == 80) ? 2 : 3", 1002 | "php": "(n % 10 == 1) ? 0 : ((n % 10 == 2) ? 1 : ((n % 100 == 0 || n % 100 == 20 || n % 100 == 40 || n % 100 == 60 || n % 100 == 80) ? 2 : 3))" 1003 | }, 1004 | "plurals": 4, 1005 | "cases": [ 1006 | "one", 1007 | "two", 1008 | "few", 1009 | "other" 1010 | ], 1011 | "examples": { 1012 | "one": "1, 11, 21, 31, 41, 51, 61, 71, 101, 1001, …", 1013 | "two": "2, 12, 22, 32, 42, 52, 62, 72, 102, 1002, …", 1014 | "few": "0, 20, 40, 60, 80, 100, 120, 140, 1000, 10000, 100000, 1000000, …", 1015 | "other": "3~10, 13~19, 23, 103, 1003, …" 1016 | } 1017 | }, 1018 | "ha": { 1019 | "name": "Hausa", 1020 | "formulas": { 1021 | "standard": "n != 1", 1022 | "php": "n != 1" 1023 | }, 1024 | "plurals": 2, 1025 | "cases": [ 1026 | "one", 1027 | "other" 1028 | ], 1029 | "examples": { 1030 | "one": "1", 1031 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1032 | } 1033 | }, 1034 | "haw": { 1035 | "name": "Hawaiian", 1036 | "formulas": { 1037 | "standard": "n != 1", 1038 | "php": "n != 1" 1039 | }, 1040 | "plurals": 2, 1041 | "cases": [ 1042 | "one", 1043 | "other" 1044 | ], 1045 | "examples": { 1046 | "one": "1", 1047 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1048 | } 1049 | }, 1050 | "he": { 1051 | "name": "Hebrew", 1052 | "formulas": { 1053 | "standard": "n == 1 ? 0 : n == 2 ? 1 : 2", 1054 | "php": "(n == 1) ? 0 : ((n == 2) ? 1 : 2)" 1055 | }, 1056 | "plurals": 3, 1057 | "cases": [ 1058 | "one", 1059 | "two", 1060 | "other" 1061 | ], 1062 | "examples": { 1063 | "one": "1", 1064 | "two": "2", 1065 | "other": "0, 3~17, 100, 1000, 10000, 100000, 1000000, …" 1066 | } 1067 | }, 1068 | "hi": { 1069 | "name": "Hindi", 1070 | "formulas": { 1071 | "standard": "n > 1", 1072 | "php": "n > 1" 1073 | }, 1074 | "plurals": 2, 1075 | "cases": [ 1076 | "one", 1077 | "other" 1078 | ], 1079 | "examples": { 1080 | "one": "0, 1", 1081 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 1082 | } 1083 | }, 1084 | "hnj": { 1085 | "name": "Hmong Njua", 1086 | "formulas": { 1087 | "standard": "0", 1088 | "php": "0" 1089 | }, 1090 | "plurals": 1, 1091 | "cases": [ 1092 | "other" 1093 | ], 1094 | "examples": { 1095 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 1096 | } 1097 | }, 1098 | "hr": { 1099 | "name": "Croatian", 1100 | "formulas": { 1101 | "standard": "n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : 2", 1102 | "php": "(n % 10 == 1 && n % 100 != 11) ? 0 : ((n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) ? 1 : 2)" 1103 | }, 1104 | "plurals": 3, 1105 | "cases": [ 1106 | "one", 1107 | "few", 1108 | "other" 1109 | ], 1110 | "examples": { 1111 | "one": "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …", 1112 | "few": "2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …", 1113 | "other": "0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 1114 | } 1115 | }, 1116 | "hsb": { 1117 | "name": "Upper Sorbian", 1118 | "formulas": { 1119 | "standard": "n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : (n % 100 == 3 || n % 100 == 4) ? 2 : 3", 1120 | "php": "(n % 100 == 1) ? 0 : ((n % 100 == 2) ? 1 : ((n % 100 == 3 || n % 100 == 4) ? 2 : 3))" 1121 | }, 1122 | "plurals": 4, 1123 | "cases": [ 1124 | "one", 1125 | "two", 1126 | "few", 1127 | "other" 1128 | ], 1129 | "examples": { 1130 | "one": "1, 101, 201, 301, 401, 501, 601, 701, 1001, …", 1131 | "two": "2, 102, 202, 302, 402, 502, 602, 702, 1002, …", 1132 | "few": "3, 4, 103, 104, 203, 204, 303, 304, 403, 404, 503, 504, 603, 604, 703, 704, 1003, …", 1133 | "other": "0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 1134 | } 1135 | }, 1136 | "hu": { 1137 | "name": "Hungarian", 1138 | "formulas": { 1139 | "standard": "n != 1", 1140 | "php": "n != 1" 1141 | }, 1142 | "plurals": 2, 1143 | "cases": [ 1144 | "one", 1145 | "other" 1146 | ], 1147 | "examples": { 1148 | "one": "1", 1149 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1150 | } 1151 | }, 1152 | "hy": { 1153 | "name": "Armenian", 1154 | "formulas": { 1155 | "standard": "n > 1", 1156 | "php": "n > 1" 1157 | }, 1158 | "plurals": 2, 1159 | "cases": [ 1160 | "one", 1161 | "other" 1162 | ], 1163 | "examples": { 1164 | "one": "0, 1", 1165 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 1166 | } 1167 | }, 1168 | "ia": { 1169 | "name": "Interlingua", 1170 | "formulas": { 1171 | "standard": "n != 1", 1172 | "php": "n != 1" 1173 | }, 1174 | "plurals": 2, 1175 | "cases": [ 1176 | "one", 1177 | "other" 1178 | ], 1179 | "examples": { 1180 | "one": "1", 1181 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1182 | } 1183 | }, 1184 | "id": { 1185 | "name": "Indonesian", 1186 | "formulas": { 1187 | "standard": "0", 1188 | "php": "0" 1189 | }, 1190 | "plurals": 1, 1191 | "cases": [ 1192 | "other" 1193 | ], 1194 | "examples": { 1195 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 1196 | } 1197 | }, 1198 | "ig": { 1199 | "name": "Igbo", 1200 | "formulas": { 1201 | "standard": "0", 1202 | "php": "0" 1203 | }, 1204 | "plurals": 1, 1205 | "cases": [ 1206 | "other" 1207 | ], 1208 | "examples": { 1209 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 1210 | } 1211 | }, 1212 | "ii": { 1213 | "name": "Sichuan Yi", 1214 | "formulas": { 1215 | "standard": "0", 1216 | "php": "0" 1217 | }, 1218 | "plurals": 1, 1219 | "cases": [ 1220 | "other" 1221 | ], 1222 | "examples": { 1223 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 1224 | } 1225 | }, 1226 | "io": { 1227 | "name": "Ido", 1228 | "formulas": { 1229 | "standard": "n != 1", 1230 | "php": "n != 1" 1231 | }, 1232 | "plurals": 2, 1233 | "cases": [ 1234 | "one", 1235 | "other" 1236 | ], 1237 | "examples": { 1238 | "one": "1", 1239 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1240 | } 1241 | }, 1242 | "is": { 1243 | "name": "Icelandic", 1244 | "formulas": { 1245 | "standard": "n % 10 != 1 || n % 100 == 11", 1246 | "php": "n % 10 != 1 || n % 100 == 11" 1247 | }, 1248 | "plurals": 2, 1249 | "cases": [ 1250 | "one", 1251 | "other" 1252 | ], 1253 | "examples": { 1254 | "one": "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …", 1255 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1256 | } 1257 | }, 1258 | "it": { 1259 | "name": "Italian", 1260 | "formulas": { 1261 | "standard": "n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2", 1262 | "php": "(n == 1) ? 0 : ((n != 0 && n % 1000000 == 0) ? 1 : 2)" 1263 | }, 1264 | "plurals": 3, 1265 | "cases": [ 1266 | "one", 1267 | "many", 1268 | "other" 1269 | ], 1270 | "examples": { 1271 | "one": "1", 1272 | "many": "1000000, 1c6, 2c6, 3c6, 4c6, 5c6, 6c6, …", 1273 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1c3, 2c3, 3c3, 4c3, 5c3, 6c3, …" 1274 | } 1275 | }, 1276 | "iu": { 1277 | "name": "Inuktitut", 1278 | "formulas": { 1279 | "standard": "n == 1 ? 0 : n == 2 ? 1 : 2", 1280 | "php": "(n == 1) ? 0 : ((n == 2) ? 1 : 2)" 1281 | }, 1282 | "plurals": 3, 1283 | "cases": [ 1284 | "one", 1285 | "two", 1286 | "other" 1287 | ], 1288 | "examples": { 1289 | "one": "1", 1290 | "two": "2", 1291 | "other": "0, 3~17, 100, 1000, 10000, 100000, 1000000, …" 1292 | } 1293 | }, 1294 | "ja": { 1295 | "name": "Japanese", 1296 | "formulas": { 1297 | "standard": "0", 1298 | "php": "0" 1299 | }, 1300 | "plurals": 1, 1301 | "cases": [ 1302 | "other" 1303 | ], 1304 | "examples": { 1305 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 1306 | } 1307 | }, 1308 | "jbo": { 1309 | "name": "Lojban", 1310 | "formulas": { 1311 | "standard": "0", 1312 | "php": "0" 1313 | }, 1314 | "plurals": 1, 1315 | "cases": [ 1316 | "other" 1317 | ], 1318 | "examples": { 1319 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 1320 | } 1321 | }, 1322 | "jgo": { 1323 | "name": "Ngomba", 1324 | "formulas": { 1325 | "standard": "n != 1", 1326 | "php": "n != 1" 1327 | }, 1328 | "plurals": 2, 1329 | "cases": [ 1330 | "one", 1331 | "other" 1332 | ], 1333 | "examples": { 1334 | "one": "1", 1335 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1336 | } 1337 | }, 1338 | "jmc": { 1339 | "name": "Machame", 1340 | "formulas": { 1341 | "standard": "n != 1", 1342 | "php": "n != 1" 1343 | }, 1344 | "plurals": 2, 1345 | "cases": [ 1346 | "one", 1347 | "other" 1348 | ], 1349 | "examples": { 1350 | "one": "1", 1351 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1352 | } 1353 | }, 1354 | "jv": { 1355 | "name": "Javanese", 1356 | "formulas": { 1357 | "standard": "0", 1358 | "php": "0" 1359 | }, 1360 | "plurals": 1, 1361 | "cases": [ 1362 | "other" 1363 | ], 1364 | "examples": { 1365 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 1366 | } 1367 | }, 1368 | "ka": { 1369 | "name": "Georgian", 1370 | "formulas": { 1371 | "standard": "n != 1", 1372 | "php": "n != 1" 1373 | }, 1374 | "plurals": 2, 1375 | "cases": [ 1376 | "one", 1377 | "other" 1378 | ], 1379 | "examples": { 1380 | "one": "1", 1381 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1382 | } 1383 | }, 1384 | "kab": { 1385 | "name": "Kabyle", 1386 | "formulas": { 1387 | "standard": "n > 1", 1388 | "php": "n > 1" 1389 | }, 1390 | "plurals": 2, 1391 | "cases": [ 1392 | "one", 1393 | "other" 1394 | ], 1395 | "examples": { 1396 | "one": "0, 1", 1397 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 1398 | } 1399 | }, 1400 | "kaj": { 1401 | "name": "Jju", 1402 | "formulas": { 1403 | "standard": "n != 1", 1404 | "php": "n != 1" 1405 | }, 1406 | "plurals": 2, 1407 | "cases": [ 1408 | "one", 1409 | "other" 1410 | ], 1411 | "examples": { 1412 | "one": "1", 1413 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1414 | } 1415 | }, 1416 | "kcg": { 1417 | "name": "Tyap", 1418 | "formulas": { 1419 | "standard": "n != 1", 1420 | "php": "n != 1" 1421 | }, 1422 | "plurals": 2, 1423 | "cases": [ 1424 | "one", 1425 | "other" 1426 | ], 1427 | "examples": { 1428 | "one": "1", 1429 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1430 | } 1431 | }, 1432 | "kde": { 1433 | "name": "Makonde", 1434 | "formulas": { 1435 | "standard": "0", 1436 | "php": "0" 1437 | }, 1438 | "plurals": 1, 1439 | "cases": [ 1440 | "other" 1441 | ], 1442 | "examples": { 1443 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 1444 | } 1445 | }, 1446 | "kea": { 1447 | "name": "Kabuverdianu", 1448 | "formulas": { 1449 | "standard": "0", 1450 | "php": "0" 1451 | }, 1452 | "plurals": 1, 1453 | "cases": [ 1454 | "other" 1455 | ], 1456 | "examples": { 1457 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 1458 | } 1459 | }, 1460 | "kk": { 1461 | "name": "Kazakh", 1462 | "formulas": { 1463 | "standard": "n != 1", 1464 | "php": "n != 1" 1465 | }, 1466 | "plurals": 2, 1467 | "cases": [ 1468 | "one", 1469 | "other" 1470 | ], 1471 | "examples": { 1472 | "one": "1", 1473 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1474 | } 1475 | }, 1476 | "kkj": { 1477 | "name": "Kako", 1478 | "formulas": { 1479 | "standard": "n != 1", 1480 | "php": "n != 1" 1481 | }, 1482 | "plurals": 2, 1483 | "cases": [ 1484 | "one", 1485 | "other" 1486 | ], 1487 | "examples": { 1488 | "one": "1", 1489 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1490 | } 1491 | }, 1492 | "kl": { 1493 | "name": "Kalaallisut", 1494 | "formulas": { 1495 | "standard": "n != 1", 1496 | "php": "n != 1" 1497 | }, 1498 | "plurals": 2, 1499 | "cases": [ 1500 | "one", 1501 | "other" 1502 | ], 1503 | "examples": { 1504 | "one": "1", 1505 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1506 | } 1507 | }, 1508 | "km": { 1509 | "name": "Khmer", 1510 | "formulas": { 1511 | "standard": "0", 1512 | "php": "0" 1513 | }, 1514 | "plurals": 1, 1515 | "cases": [ 1516 | "other" 1517 | ], 1518 | "examples": { 1519 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 1520 | } 1521 | }, 1522 | "kn": { 1523 | "name": "Kannada", 1524 | "formulas": { 1525 | "standard": "n > 1", 1526 | "php": "n > 1" 1527 | }, 1528 | "plurals": 2, 1529 | "cases": [ 1530 | "one", 1531 | "other" 1532 | ], 1533 | "examples": { 1534 | "one": "0, 1", 1535 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 1536 | } 1537 | }, 1538 | "ko": { 1539 | "name": "Korean", 1540 | "formulas": { 1541 | "standard": "0", 1542 | "php": "0" 1543 | }, 1544 | "plurals": 1, 1545 | "cases": [ 1546 | "other" 1547 | ], 1548 | "examples": { 1549 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 1550 | } 1551 | }, 1552 | "ks": { 1553 | "name": "Kashmiri", 1554 | "formulas": { 1555 | "standard": "n != 1", 1556 | "php": "n != 1" 1557 | }, 1558 | "plurals": 2, 1559 | "cases": [ 1560 | "one", 1561 | "other" 1562 | ], 1563 | "examples": { 1564 | "one": "1", 1565 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1566 | } 1567 | }, 1568 | "ksb": { 1569 | "name": "Shambala", 1570 | "formulas": { 1571 | "standard": "n != 1", 1572 | "php": "n != 1" 1573 | }, 1574 | "plurals": 2, 1575 | "cases": [ 1576 | "one", 1577 | "other" 1578 | ], 1579 | "examples": { 1580 | "one": "1", 1581 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1582 | } 1583 | }, 1584 | "ksh": { 1585 | "name": "Colognian", 1586 | "formulas": { 1587 | "standard": "n == 0 ? 0 : n == 1 ? 1 : 2", 1588 | "php": "(n == 0) ? 0 : ((n == 1) ? 1 : 2)" 1589 | }, 1590 | "plurals": 3, 1591 | "cases": [ 1592 | "zero", 1593 | "one", 1594 | "other" 1595 | ], 1596 | "examples": { 1597 | "zero": "0", 1598 | "one": "1", 1599 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 1600 | } 1601 | }, 1602 | "ku": { 1603 | "name": "Kurdish", 1604 | "formulas": { 1605 | "standard": "n != 1", 1606 | "php": "n != 1" 1607 | }, 1608 | "plurals": 2, 1609 | "cases": [ 1610 | "one", 1611 | "other" 1612 | ], 1613 | "examples": { 1614 | "one": "1", 1615 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1616 | } 1617 | }, 1618 | "kw": { 1619 | "name": "Cornish", 1620 | "formulas": { 1621 | "standard": "n == 0 ? 0 : n == 1 ? 1 : (n % 100 == 2 || n % 100 == 22 || n % 100 == 42 || n % 100 == 62 || n % 100 == 82) || n % 1000 == 0 && (n % 100000 >= 1000 && n % 100000 <= 20000 || n % 100000 == 40000 || n % 100000 == 60000 || n % 100000 == 80000) || n != 0 && n % 1000000 == 100000 ? 2 : (n % 100 == 3 || n % 100 == 23 || n % 100 == 43 || n % 100 == 63 || n % 100 == 83) ? 3 : n != 1 && (n % 100 == 1 || n % 100 == 21 || n % 100 == 41 || n % 100 == 61 || n % 100 == 81) ? 4 : 5", 1622 | "php": "(n == 0) ? 0 : ((n == 1) ? 1 : (((n % 100 == 2 || n % 100 == 22 || n % 100 == 42 || n % 100 == 62 || n % 100 == 82) || n % 1000 == 0 && (n % 100000 >= 1000 && n % 100000 <= 20000 || n % 100000 == 40000 || n % 100000 == 60000 || n % 100000 == 80000) || n != 0 && n % 1000000 == 100000) ? 2 : ((n % 100 == 3 || n % 100 == 23 || n % 100 == 43 || n % 100 == 63 || n % 100 == 83) ? 3 : ((n != 1 && (n % 100 == 1 || n % 100 == 21 || n % 100 == 41 || n % 100 == 61 || n % 100 == 81)) ? 4 : 5))))" 1623 | }, 1624 | "plurals": 6, 1625 | "cases": [ 1626 | "zero", 1627 | "one", 1628 | "two", 1629 | "few", 1630 | "many", 1631 | "other" 1632 | ], 1633 | "examples": { 1634 | "zero": "0", 1635 | "one": "1", 1636 | "two": "2, 22, 42, 62, 82, 102, 122, 142, 1000, 10000, 100000, …", 1637 | "few": "3, 23, 43, 63, 83, 103, 123, 143, 1003, …", 1638 | "many": "21, 41, 61, 81, 101, 121, 141, 161, 1001, …", 1639 | "other": "4~19, 100, 1004, 1000000, …" 1640 | } 1641 | }, 1642 | "ky": { 1643 | "name": "Kyrgyz", 1644 | "formulas": { 1645 | "standard": "n != 1", 1646 | "php": "n != 1" 1647 | }, 1648 | "plurals": 2, 1649 | "cases": [ 1650 | "one", 1651 | "other" 1652 | ], 1653 | "examples": { 1654 | "one": "1", 1655 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1656 | } 1657 | }, 1658 | "lag": { 1659 | "name": "Langi", 1660 | "formulas": { 1661 | "standard": "n == 0 ? 0 : n == 1 ? 1 : 2", 1662 | "php": "(n == 0) ? 0 : ((n == 1) ? 1 : 2)" 1663 | }, 1664 | "plurals": 3, 1665 | "cases": [ 1666 | "zero", 1667 | "one", 1668 | "other" 1669 | ], 1670 | "examples": { 1671 | "zero": "0", 1672 | "one": "1", 1673 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 1674 | } 1675 | }, 1676 | "lb": { 1677 | "name": "Luxembourgish", 1678 | "formulas": { 1679 | "standard": "n != 1", 1680 | "php": "n != 1" 1681 | }, 1682 | "plurals": 2, 1683 | "cases": [ 1684 | "one", 1685 | "other" 1686 | ], 1687 | "examples": { 1688 | "one": "1", 1689 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1690 | } 1691 | }, 1692 | "lg": { 1693 | "name": "Ganda", 1694 | "formulas": { 1695 | "standard": "n != 1", 1696 | "php": "n != 1" 1697 | }, 1698 | "plurals": 2, 1699 | "cases": [ 1700 | "one", 1701 | "other" 1702 | ], 1703 | "examples": { 1704 | "one": "1", 1705 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1706 | } 1707 | }, 1708 | "lij": { 1709 | "name": "Ligurian", 1710 | "formulas": { 1711 | "standard": "n != 1", 1712 | "php": "n != 1" 1713 | }, 1714 | "plurals": 2, 1715 | "cases": [ 1716 | "one", 1717 | "other" 1718 | ], 1719 | "examples": { 1720 | "one": "1", 1721 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1722 | } 1723 | }, 1724 | "lkt": { 1725 | "name": "Lakota", 1726 | "formulas": { 1727 | "standard": "0", 1728 | "php": "0" 1729 | }, 1730 | "plurals": 1, 1731 | "cases": [ 1732 | "other" 1733 | ], 1734 | "examples": { 1735 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 1736 | } 1737 | }, 1738 | "lld": { 1739 | "name": "Dolomitic Ladin", 1740 | "formulas": { 1741 | "standard": "n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2", 1742 | "php": "(n == 1) ? 0 : ((n != 0 && n % 1000000 == 0) ? 1 : 2)" 1743 | }, 1744 | "plurals": 3, 1745 | "cases": [ 1746 | "one", 1747 | "many", 1748 | "other" 1749 | ], 1750 | "examples": { 1751 | "one": "1", 1752 | "many": "1000000, 1c6, 2c6, 3c6, 4c6, 5c6, 6c6, …", 1753 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1c3, 2c3, 3c3, 4c3, 5c3, 6c3, …" 1754 | } 1755 | }, 1756 | "ln": { 1757 | "name": "Lingala", 1758 | "formulas": { 1759 | "standard": "n > 1", 1760 | "php": "n > 1" 1761 | }, 1762 | "plurals": 2, 1763 | "cases": [ 1764 | "one", 1765 | "other" 1766 | ], 1767 | "examples": { 1768 | "one": "0, 1", 1769 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 1770 | } 1771 | }, 1772 | "lo": { 1773 | "name": "Lao", 1774 | "formulas": { 1775 | "standard": "0", 1776 | "php": "0" 1777 | }, 1778 | "plurals": 1, 1779 | "cases": [ 1780 | "other" 1781 | ], 1782 | "examples": { 1783 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 1784 | } 1785 | }, 1786 | "lt": { 1787 | "name": "Lithuanian", 1788 | "formulas": { 1789 | "standard": "n % 10 == 1 && (n % 100 < 11 || n % 100 > 19) ? 0 : n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19) ? 1 : 2", 1790 | "php": "(n % 10 == 1 && (n % 100 < 11 || n % 100 > 19)) ? 0 : ((n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) ? 1 : 2)" 1791 | }, 1792 | "plurals": 3, 1793 | "cases": [ 1794 | "one", 1795 | "few", 1796 | "other" 1797 | ], 1798 | "examples": { 1799 | "one": "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …", 1800 | "few": "2~9, 22~29, 102, 1002, …", 1801 | "other": "0, 10~20, 30, 40, 50, 60, 100, 1000, 10000, 100000, 1000000, …" 1802 | } 1803 | }, 1804 | "lv": { 1805 | "name": "Latvian", 1806 | "formulas": { 1807 | "standard": "n % 10 == 0 || n % 100 >= 11 && n % 100 <= 19 ? 0 : n % 10 == 1 && n % 100 != 11 ? 1 : 2", 1808 | "php": "(n % 10 == 0 || n % 100 >= 11 && n % 100 <= 19) ? 0 : ((n % 10 == 1 && n % 100 != 11) ? 1 : 2)" 1809 | }, 1810 | "plurals": 3, 1811 | "cases": [ 1812 | "zero", 1813 | "one", 1814 | "other" 1815 | ], 1816 | "examples": { 1817 | "zero": "0, 10~20, 30, 40, 50, 60, 100, 1000, 10000, 100000, 1000000, …", 1818 | "one": "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …", 1819 | "other": "2~9, 22~29, 102, 1002, …" 1820 | } 1821 | }, 1822 | "mas": { 1823 | "name": "Masai", 1824 | "formulas": { 1825 | "standard": "n != 1", 1826 | "php": "n != 1" 1827 | }, 1828 | "plurals": 2, 1829 | "cases": [ 1830 | "one", 1831 | "other" 1832 | ], 1833 | "examples": { 1834 | "one": "1", 1835 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1836 | } 1837 | }, 1838 | "mg": { 1839 | "name": "Malagasy", 1840 | "formulas": { 1841 | "standard": "n > 1", 1842 | "php": "n > 1" 1843 | }, 1844 | "plurals": 2, 1845 | "cases": [ 1846 | "one", 1847 | "other" 1848 | ], 1849 | "examples": { 1850 | "one": "0, 1", 1851 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 1852 | } 1853 | }, 1854 | "mgo": { 1855 | "name": "Metaʼ", 1856 | "formulas": { 1857 | "standard": "n != 1", 1858 | "php": "n != 1" 1859 | }, 1860 | "plurals": 2, 1861 | "cases": [ 1862 | "one", 1863 | "other" 1864 | ], 1865 | "examples": { 1866 | "one": "1", 1867 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1868 | } 1869 | }, 1870 | "mk": { 1871 | "name": "Macedonian", 1872 | "formulas": { 1873 | "standard": "n % 10 != 1 || n % 100 == 11", 1874 | "php": "n % 10 != 1 || n % 100 == 11" 1875 | }, 1876 | "plurals": 2, 1877 | "cases": [ 1878 | "one", 1879 | "other" 1880 | ], 1881 | "examples": { 1882 | "one": "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …", 1883 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1884 | } 1885 | }, 1886 | "ml": { 1887 | "name": "Malayalam", 1888 | "formulas": { 1889 | "standard": "n != 1", 1890 | "php": "n != 1" 1891 | }, 1892 | "plurals": 2, 1893 | "cases": [ 1894 | "one", 1895 | "other" 1896 | ], 1897 | "examples": { 1898 | "one": "1", 1899 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1900 | } 1901 | }, 1902 | "mn": { 1903 | "name": "Mongolian", 1904 | "formulas": { 1905 | "standard": "n != 1", 1906 | "php": "n != 1" 1907 | }, 1908 | "plurals": 2, 1909 | "cases": [ 1910 | "one", 1911 | "other" 1912 | ], 1913 | "examples": { 1914 | "one": "1", 1915 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1916 | } 1917 | }, 1918 | "mr": { 1919 | "name": "Marathi", 1920 | "formulas": { 1921 | "standard": "n != 1", 1922 | "php": "n != 1" 1923 | }, 1924 | "plurals": 2, 1925 | "cases": [ 1926 | "one", 1927 | "other" 1928 | ], 1929 | "examples": { 1930 | "one": "1", 1931 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1932 | } 1933 | }, 1934 | "ms": { 1935 | "name": "Malay", 1936 | "formulas": { 1937 | "standard": "0", 1938 | "php": "0" 1939 | }, 1940 | "plurals": 1, 1941 | "cases": [ 1942 | "other" 1943 | ], 1944 | "examples": { 1945 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 1946 | } 1947 | }, 1948 | "mt": { 1949 | "name": "Maltese", 1950 | "formulas": { 1951 | "standard": "n == 1 ? 0 : n == 2 ? 1 : n == 0 || n % 100 >= 3 && n % 100 <= 10 ? 2 : n % 100 >= 11 && n % 100 <= 19 ? 3 : 4", 1952 | "php": "(n == 1) ? 0 : ((n == 2) ? 1 : ((n == 0 || n % 100 >= 3 && n % 100 <= 10) ? 2 : ((n % 100 >= 11 && n % 100 <= 19) ? 3 : 4)))" 1953 | }, 1954 | "plurals": 5, 1955 | "cases": [ 1956 | "one", 1957 | "two", 1958 | "few", 1959 | "many", 1960 | "other" 1961 | ], 1962 | "examples": { 1963 | "one": "1", 1964 | "two": "2", 1965 | "few": "0, 3~10, 103~109, 1003, …", 1966 | "many": "11~19, 111~117, 1011, …", 1967 | "other": "20~35, 100, 1000, 10000, 100000, 1000000, …" 1968 | } 1969 | }, 1970 | "my": { 1971 | "name": "Burmese", 1972 | "formulas": { 1973 | "standard": "0", 1974 | "php": "0" 1975 | }, 1976 | "plurals": 1, 1977 | "cases": [ 1978 | "other" 1979 | ], 1980 | "examples": { 1981 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 1982 | } 1983 | }, 1984 | "nah": { 1985 | "name": "Nahuatl", 1986 | "formulas": { 1987 | "standard": "n != 1", 1988 | "php": "n != 1" 1989 | }, 1990 | "plurals": 2, 1991 | "cases": [ 1992 | "one", 1993 | "other" 1994 | ], 1995 | "examples": { 1996 | "one": "1", 1997 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 1998 | } 1999 | }, 2000 | "naq": { 2001 | "name": "Nama", 2002 | "formulas": { 2003 | "standard": "n == 1 ? 0 : n == 2 ? 1 : 2", 2004 | "php": "(n == 1) ? 0 : ((n == 2) ? 1 : 2)" 2005 | }, 2006 | "plurals": 3, 2007 | "cases": [ 2008 | "one", 2009 | "two", 2010 | "other" 2011 | ], 2012 | "examples": { 2013 | "one": "1", 2014 | "two": "2", 2015 | "other": "0, 3~17, 100, 1000, 10000, 100000, 1000000, …" 2016 | } 2017 | }, 2018 | "nb": { 2019 | "name": "Norwegian Bokmål", 2020 | "formulas": { 2021 | "standard": "n != 1", 2022 | "php": "n != 1" 2023 | }, 2024 | "plurals": 2, 2025 | "cases": [ 2026 | "one", 2027 | "other" 2028 | ], 2029 | "examples": { 2030 | "one": "1", 2031 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2032 | } 2033 | }, 2034 | "nd": { 2035 | "name": "North Ndebele", 2036 | "formulas": { 2037 | "standard": "n != 1", 2038 | "php": "n != 1" 2039 | }, 2040 | "plurals": 2, 2041 | "cases": [ 2042 | "one", 2043 | "other" 2044 | ], 2045 | "examples": { 2046 | "one": "1", 2047 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2048 | } 2049 | }, 2050 | "ne": { 2051 | "name": "Nepali", 2052 | "formulas": { 2053 | "standard": "n != 1", 2054 | "php": "n != 1" 2055 | }, 2056 | "plurals": 2, 2057 | "cases": [ 2058 | "one", 2059 | "other" 2060 | ], 2061 | "examples": { 2062 | "one": "1", 2063 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2064 | } 2065 | }, 2066 | "nl": { 2067 | "name": "Dutch", 2068 | "formulas": { 2069 | "standard": "n != 1", 2070 | "php": "n != 1" 2071 | }, 2072 | "plurals": 2, 2073 | "cases": [ 2074 | "one", 2075 | "other" 2076 | ], 2077 | "examples": { 2078 | "one": "1", 2079 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2080 | } 2081 | }, 2082 | "nn": { 2083 | "name": "Norwegian Nynorsk", 2084 | "formulas": { 2085 | "standard": "n != 1", 2086 | "php": "n != 1" 2087 | }, 2088 | "plurals": 2, 2089 | "cases": [ 2090 | "one", 2091 | "other" 2092 | ], 2093 | "examples": { 2094 | "one": "1", 2095 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2096 | } 2097 | }, 2098 | "nnh": { 2099 | "name": "Ngiemboon", 2100 | "formulas": { 2101 | "standard": "n != 1", 2102 | "php": "n != 1" 2103 | }, 2104 | "plurals": 2, 2105 | "cases": [ 2106 | "one", 2107 | "other" 2108 | ], 2109 | "examples": { 2110 | "one": "1", 2111 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2112 | } 2113 | }, 2114 | "no": { 2115 | "name": "Norwegian", 2116 | "formulas": { 2117 | "standard": "n != 1", 2118 | "php": "n != 1" 2119 | }, 2120 | "plurals": 2, 2121 | "cases": [ 2122 | "one", 2123 | "other" 2124 | ], 2125 | "examples": { 2126 | "one": "1", 2127 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2128 | } 2129 | }, 2130 | "nqo": { 2131 | "name": "N’Ko", 2132 | "formulas": { 2133 | "standard": "0", 2134 | "php": "0" 2135 | }, 2136 | "plurals": 1, 2137 | "cases": [ 2138 | "other" 2139 | ], 2140 | "examples": { 2141 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 2142 | } 2143 | }, 2144 | "nr": { 2145 | "name": "South Ndebele", 2146 | "formulas": { 2147 | "standard": "n != 1", 2148 | "php": "n != 1" 2149 | }, 2150 | "plurals": 2, 2151 | "cases": [ 2152 | "one", 2153 | "other" 2154 | ], 2155 | "examples": { 2156 | "one": "1", 2157 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2158 | } 2159 | }, 2160 | "nso": { 2161 | "name": "Northern Sotho", 2162 | "formulas": { 2163 | "standard": "n > 1", 2164 | "php": "n > 1" 2165 | }, 2166 | "plurals": 2, 2167 | "cases": [ 2168 | "one", 2169 | "other" 2170 | ], 2171 | "examples": { 2172 | "one": "0, 1", 2173 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 2174 | } 2175 | }, 2176 | "ny": { 2177 | "name": "Nyanja", 2178 | "formulas": { 2179 | "standard": "n != 1", 2180 | "php": "n != 1" 2181 | }, 2182 | "plurals": 2, 2183 | "cases": [ 2184 | "one", 2185 | "other" 2186 | ], 2187 | "examples": { 2188 | "one": "1", 2189 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2190 | } 2191 | }, 2192 | "nyn": { 2193 | "name": "Nyankole", 2194 | "formulas": { 2195 | "standard": "n != 1", 2196 | "php": "n != 1" 2197 | }, 2198 | "plurals": 2, 2199 | "cases": [ 2200 | "one", 2201 | "other" 2202 | ], 2203 | "examples": { 2204 | "one": "1", 2205 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2206 | } 2207 | }, 2208 | "om": { 2209 | "name": "Oromo", 2210 | "formulas": { 2211 | "standard": "n != 1", 2212 | "php": "n != 1" 2213 | }, 2214 | "plurals": 2, 2215 | "cases": [ 2216 | "one", 2217 | "other" 2218 | ], 2219 | "examples": { 2220 | "one": "1", 2221 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2222 | } 2223 | }, 2224 | "or": { 2225 | "name": "Odia", 2226 | "formulas": { 2227 | "standard": "n != 1", 2228 | "php": "n != 1" 2229 | }, 2230 | "plurals": 2, 2231 | "cases": [ 2232 | "one", 2233 | "other" 2234 | ], 2235 | "examples": { 2236 | "one": "1", 2237 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2238 | } 2239 | }, 2240 | "os": { 2241 | "name": "Ossetic", 2242 | "formulas": { 2243 | "standard": "n != 1", 2244 | "php": "n != 1" 2245 | }, 2246 | "plurals": 2, 2247 | "cases": [ 2248 | "one", 2249 | "other" 2250 | ], 2251 | "examples": { 2252 | "one": "1", 2253 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2254 | } 2255 | }, 2256 | "osa": { 2257 | "name": "Osage", 2258 | "formulas": { 2259 | "standard": "0", 2260 | "php": "0" 2261 | }, 2262 | "plurals": 1, 2263 | "cases": [ 2264 | "other" 2265 | ], 2266 | "examples": { 2267 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 2268 | } 2269 | }, 2270 | "pa": { 2271 | "name": "Punjabi", 2272 | "formulas": { 2273 | "standard": "n > 1", 2274 | "php": "n > 1" 2275 | }, 2276 | "plurals": 2, 2277 | "cases": [ 2278 | "one", 2279 | "other" 2280 | ], 2281 | "examples": { 2282 | "one": "0, 1", 2283 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 2284 | } 2285 | }, 2286 | "pap": { 2287 | "name": "Papiamento", 2288 | "formulas": { 2289 | "standard": "n != 1", 2290 | "php": "n != 1" 2291 | }, 2292 | "plurals": 2, 2293 | "cases": [ 2294 | "one", 2295 | "other" 2296 | ], 2297 | "examples": { 2298 | "one": "1", 2299 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2300 | } 2301 | }, 2302 | "pcm": { 2303 | "name": "Nigerian Pidgin", 2304 | "formulas": { 2305 | "standard": "n > 1", 2306 | "php": "n > 1" 2307 | }, 2308 | "plurals": 2, 2309 | "cases": [ 2310 | "one", 2311 | "other" 2312 | ], 2313 | "examples": { 2314 | "one": "0, 1", 2315 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 2316 | } 2317 | }, 2318 | "pl": { 2319 | "name": "Polish", 2320 | "formulas": { 2321 | "standard": "n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : 2", 2322 | "php": "(n == 1) ? 0 : ((n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) ? 1 : 2)" 2323 | }, 2324 | "plurals": 3, 2325 | "cases": [ 2326 | "one", 2327 | "few", 2328 | "other" 2329 | ], 2330 | "examples": { 2331 | "one": "1", 2332 | "few": "2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …", 2333 | "other": "0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 2334 | } 2335 | }, 2336 | "prg": { 2337 | "name": "Prussian", 2338 | "formulas": { 2339 | "standard": "n % 10 == 0 || n % 100 >= 11 && n % 100 <= 19 ? 0 : n % 10 == 1 && n % 100 != 11 ? 1 : 2", 2340 | "php": "(n % 10 == 0 || n % 100 >= 11 && n % 100 <= 19) ? 0 : ((n % 10 == 1 && n % 100 != 11) ? 1 : 2)" 2341 | }, 2342 | "plurals": 3, 2343 | "cases": [ 2344 | "zero", 2345 | "one", 2346 | "other" 2347 | ], 2348 | "examples": { 2349 | "zero": "0, 10~20, 30, 40, 50, 60, 100, 1000, 10000, 100000, 1000000, …", 2350 | "one": "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …", 2351 | "other": "2~9, 22~29, 102, 1002, …" 2352 | } 2353 | }, 2354 | "ps": { 2355 | "name": "Pashto", 2356 | "formulas": { 2357 | "standard": "n != 1", 2358 | "php": "n != 1" 2359 | }, 2360 | "plurals": 2, 2361 | "cases": [ 2362 | "one", 2363 | "other" 2364 | ], 2365 | "examples": { 2366 | "one": "1", 2367 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2368 | } 2369 | }, 2370 | "pt": { 2371 | "name": "Portuguese", 2372 | "formulas": { 2373 | "standard": "(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2", 2374 | "php": "(n == 0 || n == 1) ? 0 : ((n != 0 && n % 1000000 == 0) ? 1 : 2)" 2375 | }, 2376 | "plurals": 3, 2377 | "cases": [ 2378 | "one", 2379 | "many", 2380 | "other" 2381 | ], 2382 | "examples": { 2383 | "one": "0, 1", 2384 | "many": "1000000, 1c6, 2c6, 3c6, 4c6, 5c6, 6c6, …", 2385 | "other": "2~17, 100, 1000, 10000, 100000, 1c3, 2c3, 3c3, 4c3, 5c3, 6c3, …" 2386 | } 2387 | }, 2388 | "pt_PT": { 2389 | "name": "European Portuguese", 2390 | "territory": "Portugal", 2391 | "baseLanguage": "Portuguese", 2392 | "formulas": { 2393 | "standard": "n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2", 2394 | "php": "(n == 1) ? 0 : ((n != 0 && n % 1000000 == 0) ? 1 : 2)" 2395 | }, 2396 | "plurals": 3, 2397 | "cases": [ 2398 | "one", 2399 | "many", 2400 | "other" 2401 | ], 2402 | "examples": { 2403 | "one": "1", 2404 | "many": "1000000, 1c6, 2c6, 3c6, 4c6, 5c6, 6c6, …", 2405 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1c3, 2c3, 3c3, 4c3, 5c3, 6c3, …" 2406 | } 2407 | }, 2408 | "rm": { 2409 | "name": "Romansh", 2410 | "formulas": { 2411 | "standard": "n != 1", 2412 | "php": "n != 1" 2413 | }, 2414 | "plurals": 2, 2415 | "cases": [ 2416 | "one", 2417 | "other" 2418 | ], 2419 | "examples": { 2420 | "one": "1", 2421 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2422 | } 2423 | }, 2424 | "ro": { 2425 | "name": "Romanian", 2426 | "formulas": { 2427 | "standard": "n == 1 ? 0 : n == 0 || n != 1 && n % 100 >= 1 && n % 100 <= 19 ? 1 : 2", 2428 | "php": "(n == 1) ? 0 : ((n == 0 || n != 1 && n % 100 >= 1 && n % 100 <= 19) ? 1 : 2)" 2429 | }, 2430 | "plurals": 3, 2431 | "cases": [ 2432 | "one", 2433 | "few", 2434 | "other" 2435 | ], 2436 | "examples": { 2437 | "one": "1", 2438 | "few": "0, 2~16, 101, 1001, …", 2439 | "other": "20~35, 100, 1000, 10000, 100000, 1000000, …" 2440 | } 2441 | }, 2442 | "rof": { 2443 | "name": "Rombo", 2444 | "formulas": { 2445 | "standard": "n != 1", 2446 | "php": "n != 1" 2447 | }, 2448 | "plurals": 2, 2449 | "cases": [ 2450 | "one", 2451 | "other" 2452 | ], 2453 | "examples": { 2454 | "one": "1", 2455 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2456 | } 2457 | }, 2458 | "ru": { 2459 | "name": "Russian", 2460 | "formulas": { 2461 | "standard": "n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : 2", 2462 | "php": "(n % 10 == 1 && n % 100 != 11) ? 0 : ((n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) ? 1 : 2)" 2463 | }, 2464 | "plurals": 3, 2465 | "cases": [ 2466 | "one", 2467 | "few", 2468 | "other" 2469 | ], 2470 | "examples": { 2471 | "one": "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …", 2472 | "few": "2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …", 2473 | "other": "0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 2474 | } 2475 | }, 2476 | "rwk": { 2477 | "name": "Rwa", 2478 | "formulas": { 2479 | "standard": "n != 1", 2480 | "php": "n != 1" 2481 | }, 2482 | "plurals": 2, 2483 | "cases": [ 2484 | "one", 2485 | "other" 2486 | ], 2487 | "examples": { 2488 | "one": "1", 2489 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2490 | } 2491 | }, 2492 | "sah": { 2493 | "name": "Yakut", 2494 | "formulas": { 2495 | "standard": "0", 2496 | "php": "0" 2497 | }, 2498 | "plurals": 1, 2499 | "cases": [ 2500 | "other" 2501 | ], 2502 | "examples": { 2503 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 2504 | } 2505 | }, 2506 | "saq": { 2507 | "name": "Samburu", 2508 | "formulas": { 2509 | "standard": "n != 1", 2510 | "php": "n != 1" 2511 | }, 2512 | "plurals": 2, 2513 | "cases": [ 2514 | "one", 2515 | "other" 2516 | ], 2517 | "examples": { 2518 | "one": "1", 2519 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2520 | } 2521 | }, 2522 | "sat": { 2523 | "name": "Santali", 2524 | "formulas": { 2525 | "standard": "n == 1 ? 0 : n == 2 ? 1 : 2", 2526 | "php": "(n == 1) ? 0 : ((n == 2) ? 1 : 2)" 2527 | }, 2528 | "plurals": 3, 2529 | "cases": [ 2530 | "one", 2531 | "two", 2532 | "other" 2533 | ], 2534 | "examples": { 2535 | "one": "1", 2536 | "two": "2", 2537 | "other": "0, 3~17, 100, 1000, 10000, 100000, 1000000, …" 2538 | } 2539 | }, 2540 | "sc": { 2541 | "name": "Sardinian", 2542 | "formulas": { 2543 | "standard": "n != 1", 2544 | "php": "n != 1" 2545 | }, 2546 | "plurals": 2, 2547 | "cases": [ 2548 | "one", 2549 | "other" 2550 | ], 2551 | "examples": { 2552 | "one": "1", 2553 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2554 | } 2555 | }, 2556 | "scn": { 2557 | "name": "Sicilian", 2558 | "formulas": { 2559 | "standard": "n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2", 2560 | "php": "(n == 1) ? 0 : ((n != 0 && n % 1000000 == 0) ? 1 : 2)" 2561 | }, 2562 | "plurals": 3, 2563 | "cases": [ 2564 | "one", 2565 | "many", 2566 | "other" 2567 | ], 2568 | "examples": { 2569 | "one": "1", 2570 | "many": "1000000, 1c6, 2c6, 3c6, 4c6, 5c6, 6c6, …", 2571 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1c3, 2c3, 3c3, 4c3, 5c3, 6c3, …" 2572 | } 2573 | }, 2574 | "sd": { 2575 | "name": "Sindhi", 2576 | "formulas": { 2577 | "standard": "n != 1", 2578 | "php": "n != 1" 2579 | }, 2580 | "plurals": 2, 2581 | "cases": [ 2582 | "one", 2583 | "other" 2584 | ], 2585 | "examples": { 2586 | "one": "1", 2587 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2588 | } 2589 | }, 2590 | "sdh": { 2591 | "name": "Southern Kurdish", 2592 | "formulas": { 2593 | "standard": "n != 1", 2594 | "php": "n != 1" 2595 | }, 2596 | "plurals": 2, 2597 | "cases": [ 2598 | "one", 2599 | "other" 2600 | ], 2601 | "examples": { 2602 | "one": "1", 2603 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2604 | } 2605 | }, 2606 | "se": { 2607 | "name": "Northern Sami", 2608 | "formulas": { 2609 | "standard": "n == 1 ? 0 : n == 2 ? 1 : 2", 2610 | "php": "(n == 1) ? 0 : ((n == 2) ? 1 : 2)" 2611 | }, 2612 | "plurals": 3, 2613 | "cases": [ 2614 | "one", 2615 | "two", 2616 | "other" 2617 | ], 2618 | "examples": { 2619 | "one": "1", 2620 | "two": "2", 2621 | "other": "0, 3~17, 100, 1000, 10000, 100000, 1000000, …" 2622 | } 2623 | }, 2624 | "seh": { 2625 | "name": "Sena", 2626 | "formulas": { 2627 | "standard": "n != 1", 2628 | "php": "n != 1" 2629 | }, 2630 | "plurals": 2, 2631 | "cases": [ 2632 | "one", 2633 | "other" 2634 | ], 2635 | "examples": { 2636 | "one": "1", 2637 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2638 | } 2639 | }, 2640 | "ses": { 2641 | "name": "Koyraboro Senni", 2642 | "formulas": { 2643 | "standard": "0", 2644 | "php": "0" 2645 | }, 2646 | "plurals": 1, 2647 | "cases": [ 2648 | "other" 2649 | ], 2650 | "examples": { 2651 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 2652 | } 2653 | }, 2654 | "sg": { 2655 | "name": "Sango", 2656 | "formulas": { 2657 | "standard": "0", 2658 | "php": "0" 2659 | }, 2660 | "plurals": 1, 2661 | "cases": [ 2662 | "other" 2663 | ], 2664 | "examples": { 2665 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 2666 | } 2667 | }, 2668 | "sh": { 2669 | "name": "Serbo-Croatian", 2670 | "formulas": { 2671 | "standard": "n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : 2", 2672 | "php": "(n % 10 == 1 && n % 100 != 11) ? 0 : ((n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) ? 1 : 2)" 2673 | }, 2674 | "plurals": 3, 2675 | "cases": [ 2676 | "one", 2677 | "few", 2678 | "other" 2679 | ], 2680 | "examples": { 2681 | "one": "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …", 2682 | "few": "2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …", 2683 | "other": "0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 2684 | } 2685 | }, 2686 | "shi": { 2687 | "name": "Tachelhit", 2688 | "formulas": { 2689 | "standard": "n == 0 || n == 1 ? 0 : n >= 2 && n <= 10 ? 1 : 2", 2690 | "php": "(n == 0 || n == 1) ? 0 : ((n >= 2 && n <= 10) ? 1 : 2)" 2691 | }, 2692 | "plurals": 3, 2693 | "cases": [ 2694 | "one", 2695 | "few", 2696 | "other" 2697 | ], 2698 | "examples": { 2699 | "one": "0, 1", 2700 | "few": "2~10", 2701 | "other": "11~26, 100, 1000, 10000, 100000, 1000000, …" 2702 | } 2703 | }, 2704 | "si": { 2705 | "name": "Sinhala", 2706 | "formulas": { 2707 | "standard": "n > 1", 2708 | "php": "n > 1" 2709 | }, 2710 | "plurals": 2, 2711 | "cases": [ 2712 | "one", 2713 | "other" 2714 | ], 2715 | "examples": { 2716 | "one": "0, 1", 2717 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 2718 | } 2719 | }, 2720 | "sk": { 2721 | "name": "Slovak", 2722 | "formulas": { 2723 | "standard": "n == 1 ? 0 : n >= 2 && n <= 4 ? 1 : 2", 2724 | "php": "(n == 1) ? 0 : ((n >= 2 && n <= 4) ? 1 : 2)" 2725 | }, 2726 | "plurals": 3, 2727 | "cases": [ 2728 | "one", 2729 | "few", 2730 | "other" 2731 | ], 2732 | "examples": { 2733 | "one": "1", 2734 | "few": "2~4", 2735 | "other": "0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 2736 | } 2737 | }, 2738 | "sl": { 2739 | "name": "Slovenian", 2740 | "formulas": { 2741 | "standard": "n % 100 == 1 ? 0 : n % 100 == 2 ? 1 : (n % 100 == 3 || n % 100 == 4) ? 2 : 3", 2742 | "php": "(n % 100 == 1) ? 0 : ((n % 100 == 2) ? 1 : ((n % 100 == 3 || n % 100 == 4) ? 2 : 3))" 2743 | }, 2744 | "plurals": 4, 2745 | "cases": [ 2746 | "one", 2747 | "two", 2748 | "few", 2749 | "other" 2750 | ], 2751 | "examples": { 2752 | "one": "1, 101, 201, 301, 401, 501, 601, 701, 1001, …", 2753 | "two": "2, 102, 202, 302, 402, 502, 602, 702, 1002, …", 2754 | "few": "3, 4, 103, 104, 203, 204, 303, 304, 403, 404, 503, 504, 603, 604, 703, 704, 1003, …", 2755 | "other": "0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 2756 | } 2757 | }, 2758 | "sma": { 2759 | "name": "Southern Sami", 2760 | "formulas": { 2761 | "standard": "n == 1 ? 0 : n == 2 ? 1 : 2", 2762 | "php": "(n == 1) ? 0 : ((n == 2) ? 1 : 2)" 2763 | }, 2764 | "plurals": 3, 2765 | "cases": [ 2766 | "one", 2767 | "two", 2768 | "other" 2769 | ], 2770 | "examples": { 2771 | "one": "1", 2772 | "two": "2", 2773 | "other": "0, 3~17, 100, 1000, 10000, 100000, 1000000, …" 2774 | } 2775 | }, 2776 | "smi": { 2777 | "name": "Sami", 2778 | "formulas": { 2779 | "standard": "n == 1 ? 0 : n == 2 ? 1 : 2", 2780 | "php": "(n == 1) ? 0 : ((n == 2) ? 1 : 2)" 2781 | }, 2782 | "plurals": 3, 2783 | "cases": [ 2784 | "one", 2785 | "two", 2786 | "other" 2787 | ], 2788 | "examples": { 2789 | "one": "1", 2790 | "two": "2", 2791 | "other": "0, 3~17, 100, 1000, 10000, 100000, 1000000, …" 2792 | } 2793 | }, 2794 | "smj": { 2795 | "name": "Lule Sami", 2796 | "formulas": { 2797 | "standard": "n == 1 ? 0 : n == 2 ? 1 : 2", 2798 | "php": "(n == 1) ? 0 : ((n == 2) ? 1 : 2)" 2799 | }, 2800 | "plurals": 3, 2801 | "cases": [ 2802 | "one", 2803 | "two", 2804 | "other" 2805 | ], 2806 | "examples": { 2807 | "one": "1", 2808 | "two": "2", 2809 | "other": "0, 3~17, 100, 1000, 10000, 100000, 1000000, …" 2810 | } 2811 | }, 2812 | "smn": { 2813 | "name": "Inari Sami", 2814 | "formulas": { 2815 | "standard": "n == 1 ? 0 : n == 2 ? 1 : 2", 2816 | "php": "(n == 1) ? 0 : ((n == 2) ? 1 : 2)" 2817 | }, 2818 | "plurals": 3, 2819 | "cases": [ 2820 | "one", 2821 | "two", 2822 | "other" 2823 | ], 2824 | "examples": { 2825 | "one": "1", 2826 | "two": "2", 2827 | "other": "0, 3~17, 100, 1000, 10000, 100000, 1000000, …" 2828 | } 2829 | }, 2830 | "sms": { 2831 | "name": "Skolt Sami", 2832 | "formulas": { 2833 | "standard": "n == 1 ? 0 : n == 2 ? 1 : 2", 2834 | "php": "(n == 1) ? 0 : ((n == 2) ? 1 : 2)" 2835 | }, 2836 | "plurals": 3, 2837 | "cases": [ 2838 | "one", 2839 | "two", 2840 | "other" 2841 | ], 2842 | "examples": { 2843 | "one": "1", 2844 | "two": "2", 2845 | "other": "0, 3~17, 100, 1000, 10000, 100000, 1000000, …" 2846 | } 2847 | }, 2848 | "sn": { 2849 | "name": "Shona", 2850 | "formulas": { 2851 | "standard": "n != 1", 2852 | "php": "n != 1" 2853 | }, 2854 | "plurals": 2, 2855 | "cases": [ 2856 | "one", 2857 | "other" 2858 | ], 2859 | "examples": { 2860 | "one": "1", 2861 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2862 | } 2863 | }, 2864 | "so": { 2865 | "name": "Somali", 2866 | "formulas": { 2867 | "standard": "n != 1", 2868 | "php": "n != 1" 2869 | }, 2870 | "plurals": 2, 2871 | "cases": [ 2872 | "one", 2873 | "other" 2874 | ], 2875 | "examples": { 2876 | "one": "1", 2877 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2878 | } 2879 | }, 2880 | "sq": { 2881 | "name": "Albanian", 2882 | "formulas": { 2883 | "standard": "n != 1", 2884 | "php": "n != 1" 2885 | }, 2886 | "plurals": 2, 2887 | "cases": [ 2888 | "one", 2889 | "other" 2890 | ], 2891 | "examples": { 2892 | "one": "1", 2893 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2894 | } 2895 | }, 2896 | "sr": { 2897 | "name": "Serbian", 2898 | "formulas": { 2899 | "standard": "n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : 2", 2900 | "php": "(n % 10 == 1 && n % 100 != 11) ? 0 : ((n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) ? 1 : 2)" 2901 | }, 2902 | "plurals": 3, 2903 | "cases": [ 2904 | "one", 2905 | "few", 2906 | "other" 2907 | ], 2908 | "examples": { 2909 | "one": "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …", 2910 | "few": "2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …", 2911 | "other": "0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 2912 | } 2913 | }, 2914 | "ss": { 2915 | "name": "Swati", 2916 | "formulas": { 2917 | "standard": "n != 1", 2918 | "php": "n != 1" 2919 | }, 2920 | "plurals": 2, 2921 | "cases": [ 2922 | "one", 2923 | "other" 2924 | ], 2925 | "examples": { 2926 | "one": "1", 2927 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2928 | } 2929 | }, 2930 | "ssy": { 2931 | "name": "Saho", 2932 | "formulas": { 2933 | "standard": "n != 1", 2934 | "php": "n != 1" 2935 | }, 2936 | "plurals": 2, 2937 | "cases": [ 2938 | "one", 2939 | "other" 2940 | ], 2941 | "examples": { 2942 | "one": "1", 2943 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2944 | } 2945 | }, 2946 | "st": { 2947 | "name": "Southern Sotho", 2948 | "formulas": { 2949 | "standard": "n != 1", 2950 | "php": "n != 1" 2951 | }, 2952 | "plurals": 2, 2953 | "cases": [ 2954 | "one", 2955 | "other" 2956 | ], 2957 | "examples": { 2958 | "one": "1", 2959 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2960 | } 2961 | }, 2962 | "su": { 2963 | "name": "Sundanese", 2964 | "formulas": { 2965 | "standard": "0", 2966 | "php": "0" 2967 | }, 2968 | "plurals": 1, 2969 | "cases": [ 2970 | "other" 2971 | ], 2972 | "examples": { 2973 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 2974 | } 2975 | }, 2976 | "sv": { 2977 | "name": "Swedish", 2978 | "formulas": { 2979 | "standard": "n != 1", 2980 | "php": "n != 1" 2981 | }, 2982 | "plurals": 2, 2983 | "cases": [ 2984 | "one", 2985 | "other" 2986 | ], 2987 | "examples": { 2988 | "one": "1", 2989 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 2990 | } 2991 | }, 2992 | "sw": { 2993 | "name": "Swahili", 2994 | "formulas": { 2995 | "standard": "n != 1", 2996 | "php": "n != 1" 2997 | }, 2998 | "plurals": 2, 2999 | "cases": [ 3000 | "one", 3001 | "other" 3002 | ], 3003 | "examples": { 3004 | "one": "1", 3005 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3006 | } 3007 | }, 3008 | "syr": { 3009 | "name": "Syriac", 3010 | "formulas": { 3011 | "standard": "n != 1", 3012 | "php": "n != 1" 3013 | }, 3014 | "plurals": 2, 3015 | "cases": [ 3016 | "one", 3017 | "other" 3018 | ], 3019 | "examples": { 3020 | "one": "1", 3021 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3022 | } 3023 | }, 3024 | "ta": { 3025 | "name": "Tamil", 3026 | "formulas": { 3027 | "standard": "n != 1", 3028 | "php": "n != 1" 3029 | }, 3030 | "plurals": 2, 3031 | "cases": [ 3032 | "one", 3033 | "other" 3034 | ], 3035 | "examples": { 3036 | "one": "1", 3037 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3038 | } 3039 | }, 3040 | "te": { 3041 | "name": "Telugu", 3042 | "formulas": { 3043 | "standard": "n != 1", 3044 | "php": "n != 1" 3045 | }, 3046 | "plurals": 2, 3047 | "cases": [ 3048 | "one", 3049 | "other" 3050 | ], 3051 | "examples": { 3052 | "one": "1", 3053 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3054 | } 3055 | }, 3056 | "teo": { 3057 | "name": "Teso", 3058 | "formulas": { 3059 | "standard": "n != 1", 3060 | "php": "n != 1" 3061 | }, 3062 | "plurals": 2, 3063 | "cases": [ 3064 | "one", 3065 | "other" 3066 | ], 3067 | "examples": { 3068 | "one": "1", 3069 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3070 | } 3071 | }, 3072 | "th": { 3073 | "name": "Thai", 3074 | "formulas": { 3075 | "standard": "0", 3076 | "php": "0" 3077 | }, 3078 | "plurals": 1, 3079 | "cases": [ 3080 | "other" 3081 | ], 3082 | "examples": { 3083 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 3084 | } 3085 | }, 3086 | "ti": { 3087 | "name": "Tigrinya", 3088 | "formulas": { 3089 | "standard": "n > 1", 3090 | "php": "n > 1" 3091 | }, 3092 | "plurals": 2, 3093 | "cases": [ 3094 | "one", 3095 | "other" 3096 | ], 3097 | "examples": { 3098 | "one": "0, 1", 3099 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 3100 | } 3101 | }, 3102 | "tig": { 3103 | "name": "Tigre", 3104 | "formulas": { 3105 | "standard": "n != 1", 3106 | "php": "n != 1" 3107 | }, 3108 | "plurals": 2, 3109 | "cases": [ 3110 | "one", 3111 | "other" 3112 | ], 3113 | "examples": { 3114 | "one": "1", 3115 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3116 | } 3117 | }, 3118 | "tk": { 3119 | "name": "Turkmen", 3120 | "formulas": { 3121 | "standard": "n != 1", 3122 | "php": "n != 1" 3123 | }, 3124 | "plurals": 2, 3125 | "cases": [ 3126 | "one", 3127 | "other" 3128 | ], 3129 | "examples": { 3130 | "one": "1", 3131 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3132 | } 3133 | }, 3134 | "tl": { 3135 | "name": "Tagalog", 3136 | "formulas": { 3137 | "standard": "n != 1 && n != 2 && n != 3 && (n % 10 == 4 || n % 10 == 6 || n % 10 == 9)", 3138 | "php": "n != 1 && n != 2 && n != 3 && (n % 10 == 4 || n % 10 == 6 || n % 10 == 9)" 3139 | }, 3140 | "plurals": 2, 3141 | "cases": [ 3142 | "one", 3143 | "other" 3144 | ], 3145 | "examples": { 3146 | "one": "0~3, 5, 7, 8, 10~13, 15, 17, 18, 20, 21, 100, 1000, 10000, 100000, 1000000, …", 3147 | "other": "4, 6, 9, 14, 16, 19, 24, 26, 104, 1004, …" 3148 | } 3149 | }, 3150 | "tn": { 3151 | "name": "Tswana", 3152 | "formulas": { 3153 | "standard": "n != 1", 3154 | "php": "n != 1" 3155 | }, 3156 | "plurals": 2, 3157 | "cases": [ 3158 | "one", 3159 | "other" 3160 | ], 3161 | "examples": { 3162 | "one": "1", 3163 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3164 | } 3165 | }, 3166 | "to": { 3167 | "name": "Tongan", 3168 | "formulas": { 3169 | "standard": "0", 3170 | "php": "0" 3171 | }, 3172 | "plurals": 1, 3173 | "cases": [ 3174 | "other" 3175 | ], 3176 | "examples": { 3177 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 3178 | } 3179 | }, 3180 | "tpi": { 3181 | "name": "Tok Pisin", 3182 | "formulas": { 3183 | "standard": "0", 3184 | "php": "0" 3185 | }, 3186 | "plurals": 1, 3187 | "cases": [ 3188 | "other" 3189 | ], 3190 | "examples": { 3191 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 3192 | } 3193 | }, 3194 | "tr": { 3195 | "name": "Turkish", 3196 | "formulas": { 3197 | "standard": "n != 1", 3198 | "php": "n != 1" 3199 | }, 3200 | "plurals": 2, 3201 | "cases": [ 3202 | "one", 3203 | "other" 3204 | ], 3205 | "examples": { 3206 | "one": "1", 3207 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3208 | } 3209 | }, 3210 | "ts": { 3211 | "name": "Tsonga", 3212 | "formulas": { 3213 | "standard": "n != 1", 3214 | "php": "n != 1" 3215 | }, 3216 | "plurals": 2, 3217 | "cases": [ 3218 | "one", 3219 | "other" 3220 | ], 3221 | "examples": { 3222 | "one": "1", 3223 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3224 | } 3225 | }, 3226 | "tzm": { 3227 | "name": "Central Atlas Tamazight", 3228 | "formulas": { 3229 | "standard": "n >= 2 && (n < 11 || n > 99)", 3230 | "php": "n >= 2 && (n < 11 || n > 99)" 3231 | }, 3232 | "plurals": 2, 3233 | "cases": [ 3234 | "one", 3235 | "other" 3236 | ], 3237 | "examples": { 3238 | "one": "0, 1, 11~24", 3239 | "other": "2~10, 100~106, 1000, 10000, 100000, 1000000, …" 3240 | } 3241 | }, 3242 | "ug": { 3243 | "name": "Uyghur", 3244 | "formulas": { 3245 | "standard": "n != 1", 3246 | "php": "n != 1" 3247 | }, 3248 | "plurals": 2, 3249 | "cases": [ 3250 | "one", 3251 | "other" 3252 | ], 3253 | "examples": { 3254 | "one": "1", 3255 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3256 | } 3257 | }, 3258 | "uk": { 3259 | "name": "Ukrainian", 3260 | "formulas": { 3261 | "standard": "n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : 2", 3262 | "php": "(n % 10 == 1 && n % 100 != 11) ? 0 : ((n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) ? 1 : 2)" 3263 | }, 3264 | "plurals": 3, 3265 | "cases": [ 3266 | "one", 3267 | "few", 3268 | "other" 3269 | ], 3270 | "examples": { 3271 | "one": "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …", 3272 | "few": "2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …", 3273 | "other": "0, 5~19, 100, 1000, 10000, 100000, 1000000, …" 3274 | } 3275 | }, 3276 | "ur": { 3277 | "name": "Urdu", 3278 | "formulas": { 3279 | "standard": "n != 1", 3280 | "php": "n != 1" 3281 | }, 3282 | "plurals": 2, 3283 | "cases": [ 3284 | "one", 3285 | "other" 3286 | ], 3287 | "examples": { 3288 | "one": "1", 3289 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3290 | } 3291 | }, 3292 | "uz": { 3293 | "name": "Uzbek", 3294 | "formulas": { 3295 | "standard": "n != 1", 3296 | "php": "n != 1" 3297 | }, 3298 | "plurals": 2, 3299 | "cases": [ 3300 | "one", 3301 | "other" 3302 | ], 3303 | "examples": { 3304 | "one": "1", 3305 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3306 | } 3307 | }, 3308 | "ve": { 3309 | "name": "Venda", 3310 | "formulas": { 3311 | "standard": "n != 1", 3312 | "php": "n != 1" 3313 | }, 3314 | "plurals": 2, 3315 | "cases": [ 3316 | "one", 3317 | "other" 3318 | ], 3319 | "examples": { 3320 | "one": "1", 3321 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3322 | } 3323 | }, 3324 | "vec": { 3325 | "name": "Venetian", 3326 | "formulas": { 3327 | "standard": "n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2", 3328 | "php": "(n == 1) ? 0 : ((n != 0 && n % 1000000 == 0) ? 1 : 2)" 3329 | }, 3330 | "plurals": 3, 3331 | "cases": [ 3332 | "one", 3333 | "many", 3334 | "other" 3335 | ], 3336 | "examples": { 3337 | "one": "1", 3338 | "many": "1000000, 1c6, 2c6, 3c6, 4c6, 5c6, 6c6, …", 3339 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1c3, 2c3, 3c3, 4c3, 5c3, 6c3, …" 3340 | } 3341 | }, 3342 | "vi": { 3343 | "name": "Vietnamese", 3344 | "formulas": { 3345 | "standard": "0", 3346 | "php": "0" 3347 | }, 3348 | "plurals": 1, 3349 | "cases": [ 3350 | "other" 3351 | ], 3352 | "examples": { 3353 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 3354 | } 3355 | }, 3356 | "vo": { 3357 | "name": "Volapük", 3358 | "formulas": { 3359 | "standard": "n != 1", 3360 | "php": "n != 1" 3361 | }, 3362 | "plurals": 2, 3363 | "cases": [ 3364 | "one", 3365 | "other" 3366 | ], 3367 | "examples": { 3368 | "one": "1", 3369 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3370 | } 3371 | }, 3372 | "vun": { 3373 | "name": "Vunjo", 3374 | "formulas": { 3375 | "standard": "n != 1", 3376 | "php": "n != 1" 3377 | }, 3378 | "plurals": 2, 3379 | "cases": [ 3380 | "one", 3381 | "other" 3382 | ], 3383 | "examples": { 3384 | "one": "1", 3385 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3386 | } 3387 | }, 3388 | "wa": { 3389 | "name": "Walloon", 3390 | "formulas": { 3391 | "standard": "n > 1", 3392 | "php": "n > 1" 3393 | }, 3394 | "plurals": 2, 3395 | "cases": [ 3396 | "one", 3397 | "other" 3398 | ], 3399 | "examples": { 3400 | "one": "0, 1", 3401 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 3402 | } 3403 | }, 3404 | "wae": { 3405 | "name": "Walser", 3406 | "formulas": { 3407 | "standard": "n != 1", 3408 | "php": "n != 1" 3409 | }, 3410 | "plurals": 2, 3411 | "cases": [ 3412 | "one", 3413 | "other" 3414 | ], 3415 | "examples": { 3416 | "one": "1", 3417 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3418 | } 3419 | }, 3420 | "wo": { 3421 | "name": "Wolof", 3422 | "formulas": { 3423 | "standard": "0", 3424 | "php": "0" 3425 | }, 3426 | "plurals": 1, 3427 | "cases": [ 3428 | "other" 3429 | ], 3430 | "examples": { 3431 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 3432 | } 3433 | }, 3434 | "xh": { 3435 | "name": "Xhosa", 3436 | "formulas": { 3437 | "standard": "n != 1", 3438 | "php": "n != 1" 3439 | }, 3440 | "plurals": 2, 3441 | "cases": [ 3442 | "one", 3443 | "other" 3444 | ], 3445 | "examples": { 3446 | "one": "1", 3447 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3448 | } 3449 | }, 3450 | "xog": { 3451 | "name": "Soga", 3452 | "formulas": { 3453 | "standard": "n != 1", 3454 | "php": "n != 1" 3455 | }, 3456 | "plurals": 2, 3457 | "cases": [ 3458 | "one", 3459 | "other" 3460 | ], 3461 | "examples": { 3462 | "one": "1", 3463 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3464 | } 3465 | }, 3466 | "yi": { 3467 | "name": "Yiddish", 3468 | "formulas": { 3469 | "standard": "n != 1", 3470 | "php": "n != 1" 3471 | }, 3472 | "plurals": 2, 3473 | "cases": [ 3474 | "one", 3475 | "other" 3476 | ], 3477 | "examples": { 3478 | "one": "1", 3479 | "other": "0, 2~16, 100, 1000, 10000, 100000, 1000000, …" 3480 | } 3481 | }, 3482 | "yo": { 3483 | "name": "Yoruba", 3484 | "formulas": { 3485 | "standard": "0", 3486 | "php": "0" 3487 | }, 3488 | "plurals": 1, 3489 | "cases": [ 3490 | "other" 3491 | ], 3492 | "examples": { 3493 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 3494 | } 3495 | }, 3496 | "yue": { 3497 | "name": "Cantonese", 3498 | "formulas": { 3499 | "standard": "0", 3500 | "php": "0" 3501 | }, 3502 | "plurals": 1, 3503 | "cases": [ 3504 | "other" 3505 | ], 3506 | "examples": { 3507 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 3508 | } 3509 | }, 3510 | "zh": { 3511 | "name": "Chinese", 3512 | "formulas": { 3513 | "standard": "0", 3514 | "php": "0" 3515 | }, 3516 | "plurals": 1, 3517 | "cases": [ 3518 | "other" 3519 | ], 3520 | "examples": { 3521 | "other": "0~15, 100, 1000, 10000, 100000, 1000000, …" 3522 | } 3523 | }, 3524 | "zu": { 3525 | "name": "Zulu", 3526 | "formulas": { 3527 | "standard": "n > 1", 3528 | "php": "n > 1" 3529 | }, 3530 | "plurals": 2, 3531 | "cases": [ 3532 | "one", 3533 | "other" 3534 | ], 3535 | "examples": { 3536 | "one": "0, 1", 3537 | "other": "2~17, 100, 1000, 10000, 100000, 1000000, …" 3538 | } 3539 | } 3540 | } --------------------------------------------------------------------------------