From 5a76b3c7c5175908bdce9f087c00287118800234 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 17 May 2024 12:00:20 +0200 Subject: [PATCH] Fix .mo parser: Number of plurals Fixes #3591 Added plur.mo to tests --- core/text/i18n/gettext.odin | 6 +- tests/core/assets/I18N/plur.mo | Bin 0 -> 537 bytes tests/core/text/i18n/test_core_text_i18n.odin | 84 ++++++++++++------ 3 files changed, 62 insertions(+), 28 deletions(-) create mode 100644 tests/core/assets/I18N/plur.mo diff --git a/core/text/i18n/gettext.odin b/core/text/i18n/gettext.odin index d5537a19c..6b8f52861 100644 --- a/core/text/i18n/gettext.odin +++ b/core/text/i18n/gettext.odin @@ -93,15 +93,15 @@ parse_mo_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTIONS, plur keys := bytes.split(key, zero) vals := bytes.split(val, zero) - - if len(keys) != len(vals) || max(len(keys), len(vals)) > MAX_PLURALS { + + if (len(keys) != 1 && len(keys) != 2) || len(vals) > MAX_PLURALS { return translation, .MO_File_Incorrect_Plural_Count } for k in keys { interned_key, _ := strings.intern_get(&translation.intern, string(k)) - interned_vals := make([]string, len(keys)) + interned_vals := make([]string, len(vals)) last_val: string i := 0 diff --git a/tests/core/assets/I18N/plur.mo b/tests/core/assets/I18N/plur.mo new file mode 100644 index 0000000000000000000000000000000000000000..03243c0f61a58b54dc2d88a99b23e1755c85af78 GIT binary patch literal 537 zcmca7#4?qEfq{XEfq_AWfq_AXfq{XQfq}sWB*?(P5Wv8|Ai==E5Xiv5ki^Kqkj22j z;LX6maF&sQff1?(M1k~!Xa?WZ;^M^gR6{sJzaXcyC^3g2peR2pHMvCBGetKnwWv5V zKhH|RK+jOmfGfaXHz>6%6C|YTl30>zrC?-WWTI&klbYcJi D633I5 literal 0 HcmV?d00001 diff --git a/tests/core/text/i18n/test_core_text_i18n.odin b/tests/core/text/i18n/test_core_text_i18n.odin index ec632d432..d59f5a382 100644 --- a/tests/core/text/i18n/test_core_text_i18n.odin +++ b/tests/core/text/i18n/test_core_text_i18n.odin @@ -38,44 +38,75 @@ Test :: struct { Test_Suite :: struct { file: string, loader: proc(string, i18n.Parse_Options, proc(int) -> int, mem.Allocator) -> (^i18n.Translation, i18n.Error), + plural: proc(int) -> int, err: i18n.Error, options: i18n.Parse_Options, tests: []Test, } +// Custom pluralizer for plur.mo +plur_mo_pluralizer :: proc(n: int) -> (slot: int) { + switch { + case n == 1: return 0 + case n != 0 && n % 1_000_000 == 0: return 1 + case: return 2 + } +} + TESTS := []Test_Suite{ + { + file = "assets/I18N/plur.mo", + loader = i18n.parse_mo_file, + plural = plur_mo_pluralizer, + tests = { + // These are in the catalog. + {"", "Message1", "This is message 1", 1}, + {"", "Message1", "This is message 1 - plural A", 1_000_000}, + {"", "Message1", "This is message 1 - plural B", 42}, + {"", "Message1/plural", "This is message 1", 1}, + {"", "Message1/plural", "This is message 1 - plural A", 1_000_000}, + {"", "Message1/plural", "This is message 1 - plural B", 42}, + + // This isn't in the catalog, so should ruturn the key. + {"", "Come visit us on Discord!", "Come visit us on Discord!", 1}, + }, + }, + { file = "assets/I18N/nl_NL.mo", loader = i18n.parse_mo_file, + plural = nil, // Default pluralizer tests = { // These are in the catalog. - { "", "There are 69,105 leaves here.", "Er zijn hier 69.105 bladeren.", 1 }, - { "", "Hellope, World!", "Hallo, Wereld!", 1 }, - { "", "There is %d leaf.\n", "Er is %d blad.\n", 1 }, - { "", "There are %d leaves.\n", "Er is %d blad.\n", 1 }, - { "", "There is %d leaf.\n", "Er zijn %d bladeren.\n", 42 }, - { "", "There are %d leaves.\n", "Er zijn %d bladeren.\n", 42 }, + {"", "There are 69,105 leaves here.", "Er zijn hier 69.105 bladeren.", 1}, + {"", "Hellope, World!", "Hallo, Wereld!", 1}, + {"", "There is %d leaf.\n", "Er is %d blad.\n", 1}, + {"", "There are %d leaves.\n", "Er is %d blad.\n", 1}, + {"", "There is %d leaf.\n", "Er zijn %d bladeren.\n", 42}, + {"", "There are %d leaves.\n", "Er zijn %d bladeren.\n", 42}, // This isn't in the catalog, so should ruturn the key. - { "", "Come visit us on Discord!", "Come visit us on Discord!", 1 }, + {"", "Come visit us on Discord!", "Come visit us on Discord!", 1}, }, }, + // QT Linguist with default loader options. { file = "assets/I18N/nl_NL-qt-ts.ts", loader = i18n.parse_qt_linguist_file, + plural = nil, // Default pluralizer tests = { // These are in the catalog. - { "Page", "Text for translation", "Tekst om te vertalen", 1}, - { "Page", "Also text to translate", "Ook tekst om te vertalen", 1}, - { "installscript", "99 bottles of beer on the wall", "99 flessen bier op de muur", 1}, - { "apple_count", "%d apple(s)", "%d appel", 1}, - { "apple_count", "%d apple(s)", "%d appels", 42}, + {"Page", "Text for translation", "Tekst om te vertalen", 1}, + {"Page", "Also text to translate", "Ook tekst om te vertalen", 1}, + {"installscript", "99 bottles of beer on the wall", "99 flessen bier op de muur", 1}, + {"apple_count", "%d apple(s)", "%d appel", 1}, + {"apple_count", "%d apple(s)", "%d appels", 42}, // These aren't in the catalog, so should ruturn the key. - { "", "Come visit us on Discord!", "Come visit us on Discord!", 1 }, - { "Fake_Section", "Come visit us on Discord!", "Come visit us on Discord!", 1 }, + {"", "Come visit us on Discord!", "Come visit us on Discord!", 1}, + {"Fake_Section", "Come visit us on Discord!", "Come visit us on Discord!", 1}, }, }, @@ -83,21 +114,22 @@ TESTS := []Test_Suite{ { file = "assets/I18N/nl_NL-qt-ts.ts", loader = i18n.parse_qt_linguist_file, + plural = nil, // Default pluralizer options = {merge_sections = true}, tests = { // All of them are now in section "", lookup with original section should return the key. - { "", "Text for translation", "Tekst om te vertalen", 1}, - { "", "Also text to translate", "Ook tekst om te vertalen", 1}, - { "", "99 bottles of beer on the wall", "99 flessen bier op de muur", 1}, - { "", "%d apple(s)", "%d appel", 1}, - { "", "%d apple(s)", "%d appels", 42}, + {"", "Text for translation", "Tekst om te vertalen", 1}, + {"", "Also text to translate", "Ook tekst om te vertalen", 1}, + {"", "99 bottles of beer on the wall", "99 flessen bier op de muur", 1}, + {"", "%d apple(s)", "%d appel", 1}, + {"", "%d apple(s)", "%d appels", 42}, // All of them are now in section "", lookup with original section should return the key. - { "Page", "Text for translation", "Text for translation", 1}, - { "Page", "Also text to translate", "Also text to translate", 1}, - { "installscript", "99 bottles of beer on the wall", "99 bottles of beer on the wall", 1}, - { "apple_count", "%d apple(s)", "%d apple(s)", 1}, - { "apple_count", "%d apple(s)", "%d apple(s)", 42}, + {"Page", "Text for translation", "Text for translation", 1}, + {"Page", "Also text to translate", "Also text to translate", 1}, + {"installscript", "99 bottles of beer on the wall", "99 bottles of beer on the wall", 1}, + {"apple_count", "%d apple(s)", "%d apple(s)", 1}, + {"apple_count", "%d apple(s)", "%d apple(s)", 42}, }, }, @@ -105,6 +137,7 @@ TESTS := []Test_Suite{ { file = "assets/I18N/duplicate-key.ts", loader = i18n.parse_qt_linguist_file, + plural = nil, // Default pluralizer options = {merge_sections = true}, err = .Duplicate_Key, }, @@ -113,6 +146,7 @@ TESTS := []Test_Suite{ { file = "assets/I18N/duplicate-key.ts", loader = i18n.parse_qt_linguist_file, + plural = nil, // Default pluralizer }, } @@ -122,7 +156,7 @@ tests :: proc(t: ^testing.T) { err: i18n.Error for suite in TESTS { - cat, err = suite.loader(suite.file, suite.options, nil, context.allocator) + cat, err = suite.loader(suite.file, suite.options, suite.plural, context.allocator) msg := fmt.tprintf("Expected loading %v to return %v, got %v", suite.file, suite.err, err) expect(t, err == suite.err, msg)