[i18n] Allow multiple sections.

This commit is contained in:
Jeroen van Rijn
2022-04-28 20:12:32 +02:00
parent 2fae6eda23
commit ba23bfb7b9
2 changed files with 54 additions and 14 deletions
+6 -5
View File
@@ -57,6 +57,10 @@ parse_mo_from_slice :: proc(data: []u8, pluralizer: proc(int) -> int = nil, allo
translation.pluralize = pluralizer translation.pluralize = pluralizer
strings.intern_init(&translation.intern, allocator, allocator) 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 { for n := u32(0); n < count; n += 1 {
/* /*
Grab string's original length and offset. 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 { for k in keys {
interned_key := strings.intern_get(&translation.intern, string(k)) interned_key := strings.intern_get(&translation.intern, string(k))
interned_vals: [MAX_PLURALS]string = {} interned_vals := make([]string, len(keys))
last_val: string last_val: string
i := 0 i := 0
@@ -103,10 +107,7 @@ parse_mo_from_slice :: proc(data: []u8, pluralizer: proc(int) -> int = nil, allo
last_val = interned_vals[i] last_val = interned_vals[i]
i += 1 i += 1
} }
for ; i < MAX_PLURALS; i += 1 { section[interned_key] = interned_vals
interned_vals[i] = last_val
}
translation.k_v[interned_key] = interned_vals
} }
delete(vals) delete(vals)
delete(keys) delete(keys)
+48 -9
View File
@@ -15,18 +15,19 @@ import "core:strings"
- Support for more translation catalog file formats. - Support for more translation catalog file formats.
*/ */
MAX_PLURALS :: 10
/* /*
Currently active catalog. Currently active catalog.
*/ */
ACTIVE: ^Translation 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. The main data structure. This can be generated from various different file formats, as long as we have a parser for them.
*/ */
Translation :: struct { Translation :: struct {
k_v: map[string][MAX_PLURALS]string, k_v: map[string]map[string][]string,
intern: strings.Intern, intern: strings.Intern,
pluralize: proc(number: int) -> int, pluralize: proc(number: int) -> int,
@@ -64,7 +65,7 @@ Error :: enum {
- get(key, number), which returns the appropriate plural from the active catalog, or - 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(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. A lot of languages use singular for 1 item and plural for 0 or more than 1 items. This is our default pluralize rule.
*/ */
@@ -76,6 +77,25 @@ get :: proc(key: string, number := 0, catalog: ^Translation = ACTIVE) -> (value:
return get_by_slot(key, plural, catalog) 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.
*/
plural := 1 if number != 1 else 0
if catalog.pluralize != nil {
plural = catalog.pluralize(number)
}
return get_by_slot(key, plural, catalog)
}
get :: proc{get_single_section, get_by_section}
/* /*
Several ways to use: Several ways to use:
- get_by_slot(key), which defaults to the singular form and i18n.ACTIVE catalog, or - get_by_slot(key), which defaults to the singular form and i18n.ACTIVE catalog, or
@@ -84,10 +104,22 @@ 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. 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) { get_by_slot_single_section :: proc(key: string, slot := 0, catalog: ^Translation = ACTIVE) -> (value: string) {
if catalog == nil { return get_by_slot_by_section("", key, slot, catalog)
}
/*
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_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 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. Return the translation from the requested slot if this key is known, else return the key.
*/ */
if translations, ok := catalog.k_v[key]; ok { if translations, ok := catalog.k_v[section][key]; ok {
plural := min(max(0, slot), MAX_PLURALS - 1) plural := min(max(0, slot), len(catalog.k_v[section][key]) - 1)
return translations[plural] return translations[plural]
} }
return key return key
} }
get_by_slot :: proc{get_by_slot_single_section, get_by_slot_by_section}
/* /*
Same for destroy: Same for destroy:
@@ -110,6 +143,12 @@ get_by_slot :: proc(key: string, slot := 0, catalog: ^Translation = ACTIVE) -> (
destroy :: proc(catalog: ^Translation = ACTIVE) { destroy :: proc(catalog: ^Translation = ACTIVE) {
if catalog != nil { if catalog != nil {
strings.intern_destroy(&catalog.intern) 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) delete(catalog.k_v)
free(catalog) free(catalog)
} }