+ fmt.assertf(pos_ok, "%T.%s has `%s` defined as %q but cannot be parsed a base-10 integer >= 0.",
+ model_type, field.name, SUBTAG_POS, pos_str, loc = loc)
+ fmt.assertf(!bit_array.get(&positionals_assigned_so_far, pos_value), "%T.%s has `%s` set to #%i, but that position has already been assigned to another flag.",
+ model_type, field.name, SUBTAG_POS, pos_value, loc = loc)
+ if requirement, is_required := get_struct_subtag(args_tag, SUBTAG_REQUIRED); is_required {
+ fmt.assertf(!reflect.is_boolean(field.type), "%T.%s is a required boolean. This is disallowed.",
+ model_type, field.name, loc = loc)
+
+ fmt.assertf(field.name != INTERNAL_VARIADIC_FLAG, "%T.%s is defined as required. This is disallowed.",
+ model_type, field.name, loc = loc)
+
+ if len(requirement) > 0 {
+ if required_min, required_max, ok = parse_requirements(requirement); ok {
+ #partial switch specific_type_info in field.type.variant {
+ case runtime.Type_Info_Dynamic_Array:
+ fmt.assertf(required_min != required_max, "%T.%s has `%s` defined as %q, but the minimum and maximum are the same. Increase the maximum by 1 for an exact number of arguments: (%i<%i)",
+ model_type,
+ field.name,
+ SUBTAG_REQUIRED,
+ requirement,
+ required_min,
+ 1 + required_max,
+ loc = loc)
+
+ fmt.assertf(required_min < required_max, "%T.%s has `%s` defined as %q, but the minimum and maximum are swapped.",
+ model_type, field.name, SUBTAG_REQUIRED, requirement, loc = loc)
+
+ case:
+ fmt.panicf("%T.%s has `%s` defined as %q, but ranges are only supported on dynamic arrays.",
+ model_type, field.name, SUBTAG_REQUIRED, requirement, loc = loc)
+ }
+ } else {
+ fmt.panicf("%T.%s has `%s` defined as %q, but it cannot be parsed as a valid range.",
+ model_type, field.name, SUBTAG_REQUIRED, requirement, loc = loc)
+ }
+ }
+ }
+
+ if length, is_variadic := get_struct_subtag(args_tag, SUBTAG_VARIADIC); is_variadic {
+ if value, parse_ok := strconv.parse_u64_of_base(length, 10); parse_ok {
+ fmt.assertf(value > 0,
+ "%T.%s has `%s` set to %i. It must be greater than zero.",
+ model_type, field.name, value, SUBTAG_VARIADIC, loc = loc)
+ fmt.assertf(value != 1,
+ "%T.%s has `%s` set to 1. This has no effect.",
+ model_type, field.name, SUBTAG_VARIADIC, loc = loc)
+ }
+
+ #partial switch specific_type_info in field.type.variant {
+ case runtime.Type_Info_Dynamic_Array:
+ fmt.assertf(style != .Odin,
+ "%T.%s has `%s` defined, but this only makes sense in UNIX-style parsing mode.",
+ model_type, field.name, SUBTAG_VARIADIC, loc = loc)
+ case:
+ fmt.panicf("%T.%s has `%s` defined, but this only makes sense on dynamic arrays.",
+ model_type, field.name, SUBTAG_VARIADIC, loc = loc)
+ }
+ }
+
+ allowed_to_define_file_perms: bool = ---
+ #partial switch specific_type_info in field.type.variant {
Generates a random 32 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
Generates a random 32 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
-Inputs:
-- r: The random number generator to use, or nil for the global generator
Generates a random 64 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
Generates a random 64 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
-Inputs:
-- r: The random number generator to use, or nil for the global generator
Generates a random 128 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
Generates a random 128 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
-Inputs:
-- r: The random number generator to use, or nil for the global generator
Generates a random 31 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
Generates a random 31 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
The sign bit will always be set to 0, thus all generated numbers will be positive.
The sign bit will always be set to 0, thus all generated numbers will be positive.
-Inputs:
-- r: The random number generator to use, or nil for the global generator
Generates a random 63 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
Generates a random 63 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
The sign bit will always be set to 0, thus all generated numbers will be positive.
The sign bit will always be set to 0, thus all generated numbers will be positive.
-Inputs:
-- r: The random number generator to use, or nil for the global generator
Generates a random 127 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
Generates a random 127 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
The sign bit will always be set to 0, thus all generated numbers will be positive.
The sign bit will always be set to 0, thus all generated numbers will be positive.
-Inputs:
-- r: The random number generator to use, or nil for the global generator
Generates a random 31 bit value in the range `[0, n)` using the provided random number generator. If no generator is provided the global random number generator will be used.
Generates a random 31 bit value in the range `[0, n)` using the provided random number generator. If no generator is provided the global random number generator will be used.
Inputs:
Inputs:
- n: The upper bound of the generated number, this value is exclusive
- n: The upper bound of the generated number, this value is exclusive
-- r: The random number generator to use, or nil for the global generator
Returns:
Returns:
- val: A random 31 bit value in the range `[0, n)`
- val: A random 31 bit value in the range `[0, n)`
Generates a random double floating point value in the range `[0, 1)` using the provided random number generator. If no generator is provided the global random number generator will be used.
Generates a random double floating point value in the range `[0, 1)` using the provided random number generator. If no generator is provided the global random number generator will be used.
-Inputs:
-- r: The random number generator to use, or nil for the global generator
-
Returns:
Returns:
- val: A random double floating point value in the range `[0, 1)`
- val: A random double floating point value in the range `[0, 1)`
Generates a random single floating point value in the range `[0, 1)` using the provided random number generator. If no generator is provided the global random number generator will be used.
Generates a random single floating point value in the range `[0, 1)` using the provided random number generator. If no generator is provided the global random number generator will be used.
-Inputs:
-- r: The random number generator to use, or nil for the global generator
-
Returns:
Returns:
- val: A random single floating point value in the range `[0, 1)`
- val: A random single floating point value in the range `[0, 1)`
Generates a random double floating point value in the range `[low, high)` using the provided random number generator. If no generator is provided the global random number generator will be used.
Generates a random double floating point value in the range `[low, high)` using the provided random number generator. If no generator is provided the global random number generator will be used.
@@ -594,7 +428,6 @@ WARNING: Panics if `high < low`
Inputs:
Inputs:
- low: The lower bounds of the value, this value is inclusive
- low: The lower bounds of the value, this value is inclusive
- high: The upper bounds of the value, this value is exclusive
- high: The upper bounds of the value, this value is exclusive
-- r: The random number generator to use, or nil for the global generator
Returns:
Returns:
- val: A random double floating point value in the range [low, high)
- val: A random double floating point value in the range [low, high)
-It has one call to get a translation: `get`, which the user can alias into something like `T`.
-
-`get`, referred to as `T` here, has a few different signatures.
-All of them will return the key if the entry can't be found in the active translation catalog.
-
-- `T(key)` returns the translation of `key`.
-- `T(key, n)` returns a pluralized translation of `key` according to value `n`.
-
-- `T(section, key)` returns the translation of `key` in `section`.
-- `T(section, key, n)` returns a pluralized translation of `key` in `section` according to value `n`.
-
-By default lookup take place in the global `i18n.ACTIVE` catalog for ease of use.
-If you want to override which translation to use, for example in a language preview dialog, you can use the following:
-
-- `T(key, n, catalog)` returns the pluralized version of `key` from explictly supplied catalog.
-- `T(section, key, n, catalog)` returns the pluralized version of `key` in `section` from explictly supplied catalog.
+The `i18n` package is a flexible and easy to use way to localise applications.
+
+It has two calls to get a translation: `get()` and `get_n()`, which the user can alias into something like `T` and `Tn`
+with statements like:
+ T :: i18n.get
+ Tn :: i18n.get_n.
+
+`get()` is used for retrieving the translation of sentences which **never** change in form,
+like for instance "Connection established" or "All temporary files have been deleted".
+Note that the number (singular, dual, plural, whatever else) is not relevant: the sentence is fixed and it will have only one possible translation in any other language.
+
+`get_n()` is used for retrieving the translations of sentences which change according to the number of items referenced.
+The various signatures of `get_n()` have one more parameter, `n`, which will receive that number and be used
+to select the correct form according to the pluralizer attached to the message catalogue when initially loaded;
+for instance, to summarise a rather complex matter, some languages use the singular form when referring to 0 items and some use the (only in their case) plural forms;
+also, languages may have more or less quantifier forms than a single singular form and a universal plural form:
+for instance, Chinese has just one form for any quantity, while Welsh may have up to 6 different forms for specific different quantities.
+
+Both `get()` and `get_n()`, referred to as `T` and `Tn` here, have several different signatures.
+All of them will return the key if the entry can't be found in the active translation catalogue.
+By default lookup take place in the global `i18n.ACTIVE` catalogue for ease of use, unless a specific catalogue is supplied.
+
+- `T(key)` returns the translation of `key`.
+- `T(key, catalog)` returns the translation of `key` from explictly supplied catalogue.
+- `T(section, key)` returns the translation of `key` in `section`.
+- `T(section, key, catalog)` returns the translation of `key` in `section` from explictly supplied catalogue.
+
+- `Tn(key, n)` returns the translation of `key` according to number of items `n`.
+- `Tn(key, n, catalog)` returns the translation of `key` from explictly supplied catalogue.
+- `Tn(section, key, n)` returns the translation of `key` in `section` according to number of items `n`.
+- `Tn(section, key, n, catalog)` returns the translation of `key` in `section` according to number of items `n` from explictly supplied catalogue.
If a catalog has translation contexts or sections, then omitting it in the above calls looks up in section "".
If a catalog has translation contexts or sections, then omitting it in the above calls looks up in section "".
-The default pluralization rule is n != 1, which is to say that passing n == 1 (or not passing n) returns the singular form.
-Passing n != 1 returns plural form 1.
+The default pluralization rule is `n != 1`, which is to say that passing `n == 1` returns the singular form (in slot 0).
+Passing `n != 1` returns the plural form in slot 1 (if any).
Should a language not conform to this rule, you can pass a pluralizer procedure to the catalog parser.
Should a language not conform to this rule, you can pass a pluralizer procedure to the catalog parser.
-This is a procedure that maps an integer to an integer, taking a value and returning which plural slot should be used.
+This is a procedure that maps an integer to an integer, taking a quantity and returning which plural slot should be used.
You can also assign it to a loaded catalog after parsing, of course.
You can also assign it to a loaded catalog after parsing, of course.
@@ -34,24 +47,21 @@ Example:
import "core:fmt"
import "core:fmt"
import "core:text/i18n"
import "core:text/i18n"
- T :: i18n.get
+ T :: i18n.get
+ Tn :: i18n.get_n
mo :: proc() {
mo :: proc() {
using fmt
using fmt
err: i18n.Error
err: i18n.Error
- /*
- Parse MO file and set it as the active translation so we can omit `get`'s "catalog" parameter.
- */
+ // Parse MO file and set it as the active translation so we can omit `get`'s "catalog" parameter.
- - 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.
+ Two ways to use:
+ - get_by_slot(key, slot), which returns the requested plural from the active catalogue, or
+ - get_by_slot(key, slot, catalog) to grab text from a specific loaded catalogue.
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.
+ - section: the catalogue section (sometime also called 'context') from which to lookup the translation
+
+ Inputs:
+ - key: the string to translate.
+ - slot: the translation slot to choose (slots refer to plural forms specific for each language and their meaning changes from catalogue to catalogue).
+ - catalog: the catalogue to use for the translation (defaults to i18n.ACTIVE)
+
+ Returns: the translated string, or the original `key` if no translation was found.
- - get_by_slot(key), which defaults to the singular form and i18n.ACTIVE catalog, or
+ Two ways to use:
- get_by_slot(key, slot), which returns the requested plural from the 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.
- 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.
If a file format parser doesn't (yet) support plural slots, each of the slots will point at the same string.
+
+ Inputs:
+ - section: the catalogue section (sometime also called 'context') from which to lookup the translation
+ - key: the string to translate.
+ - slot: the translation slot to choose (slots refer to plural forms specific for each language and their meaning changes from catalogue to catalogue).
+ - catalog: the catalogue to use for the translation (defaults to i18n.ACTIVE)
+
+ Returns: the translated string or the original `key` if no translation was found.
testing.expectf(t, val == test.val, "Expected key `%v` from section `%v`'s form for value `%v` to equal `%v`, got `%v`", test.key, test.section, test.n, test.val, val, loc=loc)
testing.expectf(t, val == test.val, "Expected key `%v` from section `%v`'s form for value `%v` to equal `%v`, got `%v`", test.key, test.section, test.n, test.val, val, loc=loc)