Added language formatting
This commit is contained in:
@ -4,5 +4,10 @@
|
|||||||
"name": "English",
|
"name": "English",
|
||||||
"code": "en"
|
"code": "en"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"npc": {
|
||||||
|
"text": "Hello, {{ name }}."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,6 +8,7 @@
|
|||||||
#include "npc.h"
|
#include "npc.h"
|
||||||
#include "ui/uitextbox.h"
|
#include "ui/uitextbox.h"
|
||||||
#include "locale/language.h"
|
#include "locale/language.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
|
||||||
void npcInit(entity_t *entity) {
|
void npcInit(entity_t *entity) {
|
||||||
|
|
||||||
@ -24,5 +25,29 @@ void npcInteract(entity_t *player, entity_t *self) {
|
|||||||
// "of Darth Plagueis the Wise? He was a dark lord of the Sith, "
|
// "of Darth Plagueis the Wise? He was a dark lord of the Sith, "
|
||||||
// "so powerful and so wise he could use the Force to influence the midichlorians"
|
// "so powerful and so wise he could use the Force to influence the midichlorians"
|
||||||
// );
|
// );
|
||||||
uiTextboxSetText(languageGet("meta.language.name"));
|
const char_t *name = "Dom";
|
||||||
|
const char_t *key = "name";
|
||||||
|
uint16_t len = languageFormat(
|
||||||
|
"test.npc.text",
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
(const char_t *[]){ key },
|
||||||
|
(const char_t *[]){ name },
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
char_t *buffer = malloc(sizeof(char_t) + len + 1);
|
||||||
|
assertNotNull(buffer, "Failed to allocate buffer for NPC text");
|
||||||
|
|
||||||
|
languageFormat(
|
||||||
|
"test.npc.text",
|
||||||
|
buffer,
|
||||||
|
len + 1,
|
||||||
|
(const char_t *[]){ key },
|
||||||
|
(const char_t *[]){ name },
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
uiTextboxSetText(buffer);
|
||||||
|
free(buffer);
|
||||||
}
|
}
|
@ -19,10 +19,8 @@ const char_t * languageGet(const char_t *key) {
|
|||||||
assertTrue(LANGUAGE.current < LANGUAGE_COUNT, "Invalid language index");
|
assertTrue(LANGUAGE.current < LANGUAGE_COUNT, "Invalid language index");
|
||||||
|
|
||||||
int16_t keyIndex = -1;
|
int16_t keyIndex = -1;
|
||||||
for(uint16_t i = 0; i < LANGUAGE_COUNT; i++) {
|
for(uint16_t i = 0; i < LANGUAGE_COUNTS[LANGUAGE.current]; i++) {
|
||||||
if(strcmp(LANGUAGE_KEYS[LANGUAGE.current][i], key) != 0) {
|
if(strcmp(LANGUAGE_KEYS[LANGUAGE.current][i], key) != 0) continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
keyIndex = i;
|
keyIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -35,13 +33,113 @@ uint16_t langaugeGetLength(const char_t *key) {
|
|||||||
assertTrue(LANGUAGE.current < LANGUAGE_COUNT, "Invalid language index");
|
assertTrue(LANGUAGE.current < LANGUAGE_COUNT, "Invalid language index");
|
||||||
|
|
||||||
int16_t keyIndex = -1;
|
int16_t keyIndex = -1;
|
||||||
for(uint16_t i = 0; i < LANGUAGE_COUNT; i++) {
|
for(uint16_t i = 0; i < LANGUAGE_COUNTS[LANGUAGE.current]; i++) {
|
||||||
if(strcmp(LANGUAGE_KEYS[LANGUAGE.current][i], key) != 0) {
|
if(strcmp(LANGUAGE_KEYS[LANGUAGE.current][i], key) != 0) continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
keyIndex = i;
|
keyIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
assertTrue(keyIndex != -1, "Key not found in language");
|
assertTrue(keyIndex != -1, "Key not found in language");
|
||||||
return strlen(LANGUAGE_VALUES[LANGUAGE.current][keyIndex]);
|
return strlen(LANGUAGE_VALUES[LANGUAGE.current][keyIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t languageFormat(
|
||||||
|
const char_t *key,
|
||||||
|
char_t *buffer,
|
||||||
|
uint16_t buffSize,
|
||||||
|
const char_t **keys,
|
||||||
|
const char_t **values,
|
||||||
|
const uint16_t valueCount
|
||||||
|
) {
|
||||||
|
if(buffer != NULL) {
|
||||||
|
assertTrue(buffSize > 0, "Buffer size must be greater than 0");
|
||||||
|
} else {
|
||||||
|
assertTrue(buffSize == 0, "Buffer size must be 0 if buffer is NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNotNull(key, "Key cannot be NULL");
|
||||||
|
assertNotNull(keys, "Keys cannot be NULL");
|
||||||
|
assertNotNull(values, "Values cannot be NULL");
|
||||||
|
|
||||||
|
const char_t *val = languageGet(key);
|
||||||
|
assertNotNull(val, "Value for key cannot be NULL");
|
||||||
|
|
||||||
|
char_t c;
|
||||||
|
uint16_t i = 0;
|
||||||
|
uint16_t j = 0;
|
||||||
|
uint8_t k = 0;
|
||||||
|
bool_t inBraces = false;
|
||||||
|
char_t braceBuffer[64] = {0};
|
||||||
|
|
||||||
|
#define bufferChar(c) ( \
|
||||||
|
(buffer ? (buffer[j++] = c) : (j++)), \
|
||||||
|
assertTrue(buffer ? j < buffSize : true, "Buffer overflow") \
|
||||||
|
)
|
||||||
|
|
||||||
|
while((c = val[i++]) != '\0') {
|
||||||
|
if(c == '{' && val[i] == '{') {
|
||||||
|
goto startBraces;
|
||||||
|
} else if(c == '}' && val[i] == '}') {
|
||||||
|
goto endBraces;
|
||||||
|
} else if(inBraces) {
|
||||||
|
goto braceBuffering;
|
||||||
|
} else {
|
||||||
|
goto character;
|
||||||
|
}
|
||||||
|
|
||||||
|
character: {
|
||||||
|
bufferChar(c);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
braceBuffering: {
|
||||||
|
assertFalse(val[i] == '\0', "Unexpected end of string.");
|
||||||
|
braceBuffer[k++] = c;
|
||||||
|
assertTrue(k < sizeof(braceBuffer), "Brace buffer overflow");
|
||||||
|
if(val[i] == ' ') i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
startBraces: {
|
||||||
|
assertFalse(inBraces, "Nested braces are not allowed");
|
||||||
|
|
||||||
|
inBraces = true;
|
||||||
|
i++;
|
||||||
|
k = 0;
|
||||||
|
assertFalse(val[i] == '\0', "Unexpected end of string.");
|
||||||
|
if(val[i] == ' ') i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
endBraces: {
|
||||||
|
assertTrue(inBraces, "Unmatched closing brace found");
|
||||||
|
|
||||||
|
inBraces = false;
|
||||||
|
i++;
|
||||||
|
braceBuffer[k] = '\0';
|
||||||
|
|
||||||
|
uint16_t l;
|
||||||
|
for(l = 0; l < valueCount; l++) {
|
||||||
|
if(strcmp(braceBuffer, keys[l]) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const char_t *replacement = values[l];
|
||||||
|
|
||||||
|
uint16_t r = 0;
|
||||||
|
while((c = replacement[r++]) != '\0') {
|
||||||
|
bufferChar(c);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
assertTrue(l < valueCount, "No string replacement found!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(buffer){
|
||||||
|
assertTrue(j < buffSize, "Buffer overflow");
|
||||||
|
buffer[j] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
assertFalse(inBraces, "Unmatched opening brace found");
|
||||||
|
return j;
|
||||||
}
|
}
|
@ -36,4 +36,30 @@ const char_t * languageGet(const char_t *key);
|
|||||||
* @param key The key for the language string.
|
* @param key The key for the language string.
|
||||||
* @return The length of the language string associated with the key.
|
* @return The length of the language string associated with the key.
|
||||||
*/
|
*/
|
||||||
uint16_t langaugeGetLength(const char_t *key);
|
uint16_t langaugeGetLength(const char_t *key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a language string with given keys and values.
|
||||||
|
*
|
||||||
|
* This function replaces placeholders in the language string with the provided
|
||||||
|
* values based on the keys.
|
||||||
|
*
|
||||||
|
* If buffer is NULL, the function will instead calculate the length of the
|
||||||
|
* formatted string.
|
||||||
|
*
|
||||||
|
* @param key The key for the language string to format.
|
||||||
|
* @param buffer The buffer to store the formatted string.
|
||||||
|
* @param buffSize The size of the buffer.
|
||||||
|
* @param keys An array of keys to replace in the language string.
|
||||||
|
* @param values An array of values corresponding to the keys.
|
||||||
|
* @param valueCount The number of key-value pairs.
|
||||||
|
* @return The number of characters written to the buffer.
|
||||||
|
*/
|
||||||
|
uint16_t languageFormat(
|
||||||
|
const char_t *key,
|
||||||
|
char_t *buffer,
|
||||||
|
uint16_t buffSize,
|
||||||
|
const char_t **keys,
|
||||||
|
const char_t **values,
|
||||||
|
const uint16_t valueCount
|
||||||
|
);
|
Reference in New Issue
Block a user