aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeroen van Rijn <Kelimion@users.noreply.github.com>2022-04-28 20:12:32 +0200
committerJeroen van Rijn <Kelimion@users.noreply.github.com>2022-04-28 20:12:32 +0200
commitba23bfb7b9eb32eb9bcf22b0364a25b6ae32203e (patch)
tree002a86819be5a78fd2e3c61aadb33f4fd1af9365
parent2fae6eda2321881ccf8d942e2c27e6a7c29aebfd (diff)
[i18n] Allow multiple sections.
-rw-r--r--core/i18n/gettext.odin11
-rw-r--r--core/i18n/i18n.odin57
2 files changed, 54 insertions, 14 deletions
diff --git a/core/i18n/gettext.odin b/core/i18n/gettext.odin
index 7918e217e..70c922cfb 100644
--- a/core/i18n/gettext.odin
+++ b/core/i18n/gettext.odin
@@ -57,6 +57,10 @@ parse_mo_from_slice :: proc(data: []u8, pluralizer: proc(int) -> int = nil, allo
translation.pluralize = pluralizer
strings.intern_init(&translation.intern, allocator, allocator)
+ // Gettext MO files only have one section.
+ translation.k_v[""] = {}
+ section := &translation.k_v[""]
+
for n := u32(0); n < count; n += 1 {
/*
Grab string's original length and offset.
@@ -94,7 +98,7 @@ parse_mo_from_slice :: proc(data: []u8, pluralizer: proc(int) -> int = nil, allo
for k in keys {
interned_key := strings.intern_get(&translation.intern, string(k))
- interned_vals: [MAX_PLURALS]string = {}
+ interned_vals := make([]string, len(keys))
last_val: string
i := 0
@@ -103,10 +107,7 @@ parse_mo_from_slice :: proc(data: []u8, pluralizer: proc(int) -> int = nil, allo
last_val = interned_vals[i]
i += 1
}
- for ; i < MAX_PLURALS; i += 1 {
- interned_vals[i] = last_val
- }
- translation.k_v[interned_key] = interned_vals
+ section[interned_key] = interned_vals
}
delete(vals)
delete(keys)
diff --git a/core/i18n/i18n.odin b/core/i18n/i18n.odin
index 7c72f9858..1ee19c2b4 100644
--- a/core/i18n/i18n.odin
+++ b/core/i18n/i18n.odin
@@ -15,18 +15,19 @@ import "core:strings"
- Support for more translation catalog file formats.
*/
-MAX_PLURALS :: 10
-
/*
Currently active catalog.
*/
ACTIVE: ^Translation
+// Allow between 1 and 255 plural forms. Default: 10.
+MAX_PLURALS :: min(max(#config(ODIN_i18N_MAX_PLURAL_FORMS, 10), 1), 255)
+
/*
The main data structure. This can be generated from various different file formats, as long as we have a parser for them.
*/
Translation :: struct {
- k_v: map[string][MAX_PLURALS]string,
+ k_v: map[string]map[string][]string,
intern: strings.Intern,
pluralize: proc(number: int) -> int,
@@ -64,7 +65,25 @@ Error :: enum {
- get(key, number), which returns the appropriate plural from the active catalog, or
- get(key, number, catalog) to grab text from a specific one.
*/
-get :: proc(key: string, number := 0, catalog: ^Translation = ACTIVE) -> (value: string) {
+get_single_section :: proc(key: string, number := 0, catalog: ^Translation = ACTIVE) -> (value: string) {
+ /*
+ A lot of languages use singular for 1 item and plural for 0 or more than 1 items. This is our default pluralize rule.
+ */
+ plural := 1 if number != 1 else 0
+
+ if catalog.pluralize != nil {
+ plural = catalog.pluralize(number)
+ }
+ return get_by_slot(key, plural, catalog)
+}
+
+/*
+ Several ways to use:
+ - get(section, key), which defaults to the singular form and i18n.ACTIVE catalog, or
+ - get(section, key, number), which returns the appropriate plural from the active catalog, or
+ - get(section, key, number, catalog) to grab text from a specific one.
+*/
+get_by_section :: proc(section, key: string, number := 0, catalog: ^Translation = ACTIVE) -> (value: string) {
/*
A lot of languages use singular for 1 item and plural for 0 or more than 1 items. This is our default pluralize rule.
*/
@@ -75,6 +94,19 @@ get :: proc(key: string, number := 0, catalog: ^Translation = ACTIVE) -> (value:
}
return get_by_slot(key, plural, catalog)
}
+get :: proc{get_single_section, get_by_section}
+
+/*
+ Several ways to use:
+ - get_by_slot(key), which defaults to the singular form and i18n.ACTIVE catalog, or
+ - get_by_slot(key, slot), which returns the requested plural from the active catalog, or
+ - get_by_slot(key, slot, catalog) to grab text from a specific one.
+
+ If a file format parser doesn't (yet) support plural slots, each of the slots will point at the same string.
+*/
+get_by_slot_single_section :: proc(key: string, slot := 0, catalog: ^Translation = ACTIVE) -> (value: string) {
+ return get_by_slot_by_section("", key, slot, catalog)
+}
/*
Several ways to use:
@@ -84,10 +116,10 @@ get :: proc(key: string, number := 0, catalog: ^Translation = ACTIVE) -> (value:
If a file format parser doesn't (yet) support plural slots, each of the slots will point at the same string.
*/
-get_by_slot :: proc(key: string, slot := 0, catalog: ^Translation = ACTIVE) -> (value: string) {
- if catalog == nil {
+get_by_slot_by_section :: proc(section, key: string, slot := 0, catalog: ^Translation = ACTIVE) -> (value: string) {
+ if catalog == nil || section not_in catalog.k_v {
/*
- Return the key if the catalog catalog hasn't been initialized yet.
+ Return the key if the catalog catalog hasn't been initialized yet, or the section is not present.
*/
return key
}
@@ -95,12 +127,13 @@ get_by_slot :: proc(key: string, slot := 0, catalog: ^Translation = ACTIVE) -> (
/*
Return the translation from the requested slot if this key is known, else return the key.
*/
- if translations, ok := catalog.k_v[key]; ok {
- plural := min(max(0, slot), MAX_PLURALS - 1)
+ if translations, ok := catalog.k_v[section][key]; ok {
+ plural := min(max(0, slot), len(catalog.k_v[section][key]) - 1)
return translations[plural]
}
return key
}
+get_by_slot :: proc{get_by_slot_single_section, get_by_slot_by_section}
/*
Same for destroy:
@@ -110,6 +143,12 @@ get_by_slot :: proc(key: string, slot := 0, catalog: ^Translation = ACTIVE) -> (
destroy :: proc(catalog: ^Translation = ACTIVE) {
if catalog != nil {
strings.intern_destroy(&catalog.intern)
+ for section in &catalog.k_v {
+ for key in &catalog.k_v[section] {
+ delete(catalog.k_v[section][key])
+ }
+ delete(catalog.k_v[section])
+ }
delete(catalog.k_v)
free(catalog)
}