├── 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 | }
--------------------------------------------------------------------------------