282 lines
9.3 KiB
C
282 lines
9.3 KiB
C
/**
|
|
* Copyright (c) 2026 Dominic Masters
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#pragma once
|
|
#include "asset/assetfile.h"
|
|
|
|
typedef struct assetloading_s assetloading_t;
|
|
typedef struct assetentry_s assetentry_t;
|
|
|
|
/** Input passed to the locale loader — currently unused. */
|
|
typedef struct { void *nothing; } assetlocaleloaderinput_t;
|
|
|
|
typedef enum {
|
|
ASSET_LOCALE_LOADER_STATE_INITIAL,
|
|
ASSET_LOCALE_LOADER_STATE_LOAD_HEADER,
|
|
ASSET_LOCALE_LOADER_STATE_DONE
|
|
} assetlocaleloaderstate_t;
|
|
|
|
/** Per-slot scratch data used while the locale file is loading. */
|
|
typedef struct {
|
|
assetlocaleloaderstate_t state;
|
|
} assetlocaleloaderloading_t;
|
|
|
|
/** Maximum number of distinct plural forms a locale file may declare. */
|
|
#define ASSET_LOCALE_FILE_PLURAL_FORM_COUNT 6
|
|
|
|
/**
|
|
* Comparison operator used in a plural-form expression.
|
|
*
|
|
* Each condition in the plural-form header is evaluated as
|
|
* `n <op> value`
|
|
* where `n` is the runtime plural count.
|
|
*/
|
|
typedef enum {
|
|
ASSET_LOCALE_PLURAL_OP_EQUAL,
|
|
ASSET_LOCALE_PLURAL_OP_NOT_EQUAL,
|
|
ASSET_LOCALE_PLURAL_OP_LESS,
|
|
ASSET_LOCALE_PLURAL_OP_LESS_EQUAL,
|
|
ASSET_LOCALE_PLURAL_OP_GREATER,
|
|
ASSET_LOCALE_PLURAL_OP_GREATER_EQUAL
|
|
} assetlocalepluraloperation_t;
|
|
|
|
/**
|
|
* Discriminator tag for a locale string argument.
|
|
* @see assetlocalearg_t
|
|
*/
|
|
typedef enum {
|
|
ASSET_LOCALE_ARG_STRING,
|
|
ASSET_LOCALE_ARG_INT,
|
|
ASSET_LOCALE_ARG_FLOAT
|
|
} assetlocaleargtype_t;
|
|
|
|
/**
|
|
* A single typed argument for locale string substitution.
|
|
*
|
|
* Used with @ref assetLocaleGetStringWithArgs to fill `%s`, `%d`, or `%f`
|
|
* placeholders inside a translated string.
|
|
*/
|
|
typedef struct {
|
|
/** Which union member is active. */
|
|
assetlocaleargtype_t type;
|
|
union {
|
|
const char_t *stringValue;
|
|
int32_t intValue;
|
|
float_t floatValue;
|
|
};
|
|
} assetlocalearg_t;
|
|
|
|
/**
|
|
* Runtime state for an open locale file.
|
|
*
|
|
* Loaded once by @ref assetLocaleLoaderSync and kept alive for the lifetime
|
|
* of the asset entry. The plural-form fields are populated from the PO header
|
|
* by @ref assetLocaleParseHeader.
|
|
*
|
|
* Plural evaluation works as a linear scan over `pluralStateCount - 1`
|
|
* conditions; if none match, `pluralDefaultIndex` is returned.
|
|
*/
|
|
typedef struct {
|
|
/** Underlying file handle used to rewind and re-read the PO data. */
|
|
assetfile_t file;
|
|
|
|
/** Comparison operator for each conditional plural clause. */
|
|
assetlocalepluraloperation_t pluralOps[ASSET_LOCALE_FILE_PLURAL_FORM_COUNT];
|
|
|
|
/** Right-hand value for each conditional plural clause. */
|
|
int32_t pluralValues[ASSET_LOCALE_FILE_PLURAL_FORM_COUNT];
|
|
|
|
/** Form index returned when the corresponding condition is true. */
|
|
int32_t pluralIndices[ASSET_LOCALE_FILE_PLURAL_FORM_COUNT];
|
|
|
|
/** Total number of plural forms declared by `nplurals=`. */
|
|
uint8_t pluralStateCount;
|
|
|
|
/** Form index used when no conditional clause matches. */
|
|
uint8_t pluralDefaultIndex;
|
|
} assetlocalefile_t;
|
|
|
|
/** Convenience alias — the loaded output type of a locale asset entry. */
|
|
typedef assetlocalefile_t assetlocaleoutput_t;
|
|
|
|
/**
|
|
* Asynchronous loader callback. Opens the locale file, reads the PO header,
|
|
* and parses plural-form rules. All I/O happens here so the main thread is
|
|
* not blocked. Sets entry state to `ASSET_ENTRY_STATE_PENDING_SYNC` on
|
|
* success or `ASSET_ENTRY_STATE_ERROR` on failure.
|
|
*
|
|
* @param loading The loading slot for this asset entry.
|
|
* @return OK on success, error otherwise.
|
|
*/
|
|
errorret_t assetLocaleLoaderAsync(assetloading_t *loading);
|
|
|
|
/**
|
|
* Synchronous loader callback. Confirms the async phase completed and marks
|
|
* the entry as `ASSET_ENTRY_STATE_LOADED`.
|
|
*
|
|
* @param loading The loading slot for this asset entry.
|
|
* @return OK on success, error otherwise.
|
|
*/
|
|
errorret_t assetLocaleLoaderSync(assetloading_t *loading);
|
|
|
|
/**
|
|
* Dispose callback. Closes the open file handle and zeros the locale data.
|
|
*
|
|
* @param entry The asset entry to dispose.
|
|
* @return OK on success, error otherwise.
|
|
*/
|
|
errorret_t assetLocaleDispose(assetentry_t *entry);
|
|
|
|
/**
|
|
* Parses the `Plural-Forms:` line from a PO header string and populates the
|
|
* plural-form fields of `localeFile`. If no `Plural-Forms:` key is present
|
|
* the function returns OK without modifying the struct.
|
|
*
|
|
* Supports the `nplurals=N; plural=(<expr>);` syntax where `<expr>` is a
|
|
* chain of `n <op> value ? index : ...` ternary conditions ending with a
|
|
* fallback index.
|
|
*
|
|
* @param localeFile Struct whose plural fields will be filled in.
|
|
* @param headerBuffer The raw PO header string (msgstr of msgid "").
|
|
* @param headerBufferSize Size of `headerBuffer` in bytes.
|
|
* @return OK on success, error if the plural expression is malformed.
|
|
*/
|
|
errorret_t assetLocaleParseHeader(
|
|
assetlocalefile_t *localeFile,
|
|
char_t *headerBuffer,
|
|
const size_t headerBufferSize
|
|
);
|
|
|
|
/**
|
|
* Evaluates which plural form index to use for a given count.
|
|
*
|
|
* Walks the conditions stored in `file` in order; returns the index for the
|
|
* first condition that matches `pluralCount`. Falls back to
|
|
* `file->pluralDefaultIndex` if none match.
|
|
*
|
|
* @param file Locale file with parsed plural rules.
|
|
* @param pluralCount The runtime count (e.g. number of items).
|
|
* @return Zero-based plural form index.
|
|
*/
|
|
uint8_t assetLocaleEvaluatePlural(
|
|
assetlocalefile_t *file,
|
|
const int32_t pluralCount
|
|
);
|
|
|
|
/**
|
|
* Advances the line reader past blank lines, comment lines (starting with
|
|
* `#`), and lines containing only spaces. Stops at the first content line or
|
|
* end of file.
|
|
*
|
|
* @param reader Line reader positioned at the current line.
|
|
* @param lineBuffer Output buffer that the reader writes each line into.
|
|
* @return OK on success, error if reading fails.
|
|
*/
|
|
errorret_t assetLocaleLineSkipBlanks(
|
|
assetfilelinereader_t *reader,
|
|
uint8_t *lineBuffer
|
|
);
|
|
|
|
/**
|
|
* Reads a PO quoted string value from the current line and any immediately
|
|
* following continuation lines that begin with `"`.
|
|
*
|
|
* Handles standard C escape sequences (`\n`, `\t`, `\\`, `\"`).
|
|
*
|
|
* @param reader Line reader positioned at the line containing the opening
|
|
* quote (e.g. `msgstr "..."`).
|
|
* @param lineBuffer Buffer the reader fills on each @ref assetFileLineReaderNext
|
|
* call; also used to detect continuation lines.
|
|
* @param stringBuffer Destination for the unescaped string content.
|
|
* @param stringBufferSize Capacity of `stringBuffer` in bytes.
|
|
* @return OK on success, error if a quote or escape sequence is malformed or
|
|
* the buffer would overflow.
|
|
*/
|
|
errorret_t assetLocaleLineUnbuffer(
|
|
assetfilelinereader_t *reader,
|
|
uint8_t *lineBuffer,
|
|
uint8_t *stringBuffer,
|
|
const size_t stringBufferSize
|
|
);
|
|
|
|
/**
|
|
* Looks up a translated string by message ID from the open locale file.
|
|
*
|
|
* Rewinds the file and scans from the beginning on every call. For plural
|
|
* entries (`msgid_plural`) the `pluralCount` is evaluated against the loaded
|
|
* plural rules to select the correct `msgstr[N]` form.
|
|
*
|
|
* @param file Locale file to search. Must be open.
|
|
* @param messageId PO message ID to find (`""` retrieves the header entry).
|
|
* @param pluralCount Count used to select the plural form (ignored for
|
|
* singular entries).
|
|
* @param stringBuffer Buffer to receive the translated string.
|
|
* @param stringBufferSize Capacity of `stringBuffer` in bytes.
|
|
* @return OK on success, error if the message ID is not found or I/O fails.
|
|
*/
|
|
errorret_t assetLocaleGetString(
|
|
assetlocalefile_t *file,
|
|
const char_t *messageId,
|
|
const int32_t pluralCount,
|
|
char_t *stringBuffer,
|
|
const size_t stringBufferSize
|
|
);
|
|
|
|
/**
|
|
* Looks up a translated string and formats it with a `printf`-style variadic
|
|
* argument list.
|
|
*
|
|
* Retrieves the raw format string via @ref assetLocaleGetString then applies
|
|
* `vsnprintf` with the provided arguments.
|
|
*
|
|
* @param file Locale file to search. Must be open.
|
|
* @param messageId PO message ID to find.
|
|
* @param pluralCount Count used to select the plural form.
|
|
* @param buffer Buffer to receive the formatted string.
|
|
* @param bufferSize Capacity of `buffer` in bytes.
|
|
* @param ... Format arguments matching the specifiers in the translated
|
|
* string.
|
|
* @return OK on success, error if the message is not found or formatting
|
|
* fails.
|
|
*/
|
|
errorret_t assetLocaleGetStringWithVA(
|
|
assetlocalefile_t *file,
|
|
const char_t *messageId,
|
|
const int32_t pluralCount,
|
|
char_t *buffer,
|
|
const size_t bufferSize,
|
|
...
|
|
);
|
|
|
|
/**
|
|
* Looks up a translated string and substitutes typed arguments into its
|
|
* `%s`, `%d`, and `%f` placeholders.
|
|
*
|
|
* Unlike @ref assetLocaleGetStringWithVA this variant accepts an explicit
|
|
* array of @ref assetlocalearg_t values, which is safer to use from
|
|
* generated or scripted call sites.
|
|
*
|
|
* @param file Locale file to search. Must be open.
|
|
* @param messageId PO message ID to find.
|
|
* @param pluralCount Count used to select the plural form.
|
|
* @param buffer Buffer to receive the formatted string.
|
|
* @param bufferSize Capacity of `buffer` in bytes.
|
|
* @param args Array of typed arguments; may be NULL when `argCount` is 0.
|
|
* @param argCount Number of elements in `args`.
|
|
* @return OK on success, error if the message is not found, an argument type
|
|
* mismatches the specifier, or the buffer would overflow.
|
|
*/
|
|
errorret_t assetLocaleGetStringWithArgs(
|
|
assetlocalefile_t *file,
|
|
const char_t *messageId,
|
|
const int32_t pluralCount,
|
|
char_t *buffer,
|
|
const size_t bufferSize,
|
|
const assetlocalearg_t *args,
|
|
const size_t argCount
|
|
);
|