lang
This commit is contained in:
@@ -5,7 +5,11 @@
|
|||||||
|
|
||||||
set(DUSK_GAME_ASSETS_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL ${DUSK_CACHE_TARGET})
|
set(DUSK_GAME_ASSETS_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL ${DUSK_CACHE_TARGET})
|
||||||
|
|
||||||
add_subdirectory(palette)# Palette asset needs to be added before any images.
|
# Palette asset needs to be added before any images.
|
||||||
|
add_subdirectory(palette)
|
||||||
|
|
||||||
|
# Languages need to be added before anything that uses text.
|
||||||
|
add_subdirectory(locale)
|
||||||
|
|
||||||
add_subdirectory(entity)
|
add_subdirectory(entity)
|
||||||
add_subdirectory(map)
|
add_subdirectory(map)
|
||||||
|
|||||||
6
assets/locale/CMakeLists.txt
Normal file
6
assets/locale/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Copyright (c) 2025 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
add_asset(LANGUAGE en_US.po)
|
||||||
10
assets/locale/en_US.po
Normal file
10
assets/locale/en_US.po
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Language: us\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
|
msgid "test.test"
|
||||||
|
msgstr "Hello this is a test."
|
||||||
@@ -16,9 +16,14 @@ typedef enum {
|
|||||||
ASSET_TYPE_COUNT,
|
ASSET_TYPE_COUNT,
|
||||||
} assettype_t;
|
} assettype_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ASSET_LOAD_STRAT_ENTIRE,
|
||||||
|
} assetloadstrat_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char_t *header;
|
const char_t *header;
|
||||||
const size_t dataSize;
|
const size_t dataSize;
|
||||||
|
const assetloadstrat_t loadStrategy;
|
||||||
errorret_t (*load)(void *data, void *output);
|
errorret_t (*load)(void *data, void *output);
|
||||||
} assettypedef_t;
|
} assettypedef_t;
|
||||||
|
|
||||||
@@ -29,12 +34,14 @@ static const assettypedef_t ASSET_TYPE_DEFINITIONS[ASSET_TYPE_COUNT] = {
|
|||||||
|
|
||||||
[ASSET_TYPE_PALETTE_IMAGE] = {
|
[ASSET_TYPE_PALETTE_IMAGE] = {
|
||||||
.header = "DPI",
|
.header = "DPI",
|
||||||
|
.loadStrategy = ASSET_LOAD_STRAT_ENTIRE,
|
||||||
.dataSize = sizeof(assetpaletteimage_t),
|
.dataSize = sizeof(assetpaletteimage_t),
|
||||||
.load = assetPaletteImageLoad
|
.load = assetPaletteImageLoad
|
||||||
},
|
},
|
||||||
|
|
||||||
[ASSET_TYPE_ALPHA_IMAGE] = {
|
[ASSET_TYPE_ALPHA_IMAGE] = {
|
||||||
.header = "DAI",
|
.header = "DAI",
|
||||||
|
.loadStrategy = ASSET_LOAD_STRAT_ENTIRE,
|
||||||
.dataSize = sizeof(assetalphaimage_t),
|
.dataSize = sizeof(assetalphaimage_t),
|
||||||
.load = assetAlphaImageLoad
|
.load = assetAlphaImageLoad
|
||||||
},
|
},
|
||||||
|
|||||||
23
src/asset/type/assetlanguage.h
Normal file
23
src/asset/type/assetlanguage.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "locale/language/keys.h"
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
typedef struct {
|
||||||
|
uint32_t chunk;
|
||||||
|
uint32_t offset;
|
||||||
|
uint32_t length;
|
||||||
|
} assetlanguagestring_t;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
typedef struct {
|
||||||
|
assetlanguagestring_t strings[LANG_KEY_COUNT];
|
||||||
|
} assetlanguageheader_t;
|
||||||
|
#pragma pack(pop)
|
||||||
18
src/locale/language/languagechunk.h
Normal file
18
src/locale/language/languagechunk.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
|
||||||
|
#define LANG_CHUNK_STRING_COUNT 64
|
||||||
|
#define LANG_CHUNK_STRING_MAX_LENGTH DUSK_LANG_STRING_MAX_LENGTH
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
typedef struct {
|
||||||
|
char_t
|
||||||
|
} languagechunk_t;
|
||||||
|
#pragma pack(pop)
|
||||||
@@ -74,7 +74,7 @@ int32_t stringFormatVA(
|
|||||||
char_t *dest,
|
char_t *dest,
|
||||||
const size_t destSize,
|
const size_t destSize,
|
||||||
const char_t *format,
|
const char_t *format,
|
||||||
const va_list args
|
va_list args
|
||||||
) {
|
) {
|
||||||
assertNotNull(format, "format must not be NULL");
|
assertNotNull(format, "format must not be NULL");
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ int32_t stringFormatVA(
|
|||||||
char_t *dest,
|
char_t *dest,
|
||||||
const size_t destSize,
|
const size_t destSize,
|
||||||
const char_t *format,
|
const char_t *format,
|
||||||
const va_list args
|
va_list args
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from args import inputAssets, args
|
|||||||
from processasset import processAsset
|
from processasset import processAsset
|
||||||
from processpalette import processPaletteList
|
from processpalette import processPaletteList
|
||||||
from processtileset import processTilesetList
|
from processtileset import processTilesetList
|
||||||
|
from processlanguage import processLanguageList
|
||||||
from assethelpers import getBuiltAssetsRelativePath
|
from assethelpers import getBuiltAssetsRelativePath
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ with zipfile.ZipFile(outputFileName, 'w') as zipf:
|
|||||||
# Generate additional headers.
|
# Generate additional headers.
|
||||||
processPaletteList()
|
processPaletteList()
|
||||||
processTilesetList()
|
processTilesetList()
|
||||||
|
processLanguageList()
|
||||||
|
|
||||||
# Finalize build
|
# Finalize build
|
||||||
if args.build_type == 'header':
|
if args.build_type == 'header':
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from processimage import processImage
|
|||||||
from processpalette import processPalette
|
from processpalette import processPalette
|
||||||
from processtileset import processTileset
|
from processtileset import processTileset
|
||||||
from processmap import processMap
|
from processmap import processMap
|
||||||
|
from processlanguage import processLanguage
|
||||||
|
|
||||||
processedAssets = []
|
processedAssets = []
|
||||||
|
|
||||||
@@ -22,6 +23,8 @@ def processAsset(asset):
|
|||||||
return processTileset(asset)
|
return processTileset(asset)
|
||||||
elif t == 'map':
|
elif t == 'map':
|
||||||
return processMap(asset)
|
return processMap(asset)
|
||||||
|
elif t == 'language':
|
||||||
|
return processLanguage(asset)
|
||||||
else:
|
else:
|
||||||
print(f"Error: Unknown asset type '{asset['type']}' for path '{asset['path']}'")
|
print(f"Error: Unknown asset type '{asset['type']}' for path '{asset['path']}'")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
73
tools/assetstool/processlanguage.py
Normal file
73
tools/assetstool/processlanguage.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from args import args
|
||||||
|
from assetcache import assetCache, assetGetCache
|
||||||
|
from assethelpers import getAssetRelativePath
|
||||||
|
import polib
|
||||||
|
import re
|
||||||
|
|
||||||
|
LANGUAGE_DATA = {}
|
||||||
|
LANGUAGE_KEYS = []
|
||||||
|
|
||||||
|
def processLanguageList():
|
||||||
|
# Language keys header data
|
||||||
|
headerKeys = "// Auto-generated language keys header file.\n"
|
||||||
|
headerKeys += "#pragma once\n"
|
||||||
|
headerKeys += "#include \"dusk.h\"\n\n"
|
||||||
|
|
||||||
|
keyIndex = 0
|
||||||
|
for key in LANGUAGE_KEYS:
|
||||||
|
headerKeys += f"#define {getLanguageVariableName(key)} {keyIndex}\n"
|
||||||
|
keyIndex += 1
|
||||||
|
|
||||||
|
for lang in LANGUAGE_DATA:
|
||||||
|
if key not in LANGUAGE_DATA[lang]:
|
||||||
|
print(f"Warning: Missing translation for key '{key}' in language '{lang}'")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
headerKeys += f"\n#define LANG_KEY_COUNT {len(LANGUAGE_KEYS)}\n"
|
||||||
|
|
||||||
|
# Write out the language keys header file
|
||||||
|
outputFile = os.path.join(args.headers_dir, "locale", "language", "keys.h")
|
||||||
|
os.makedirs(os.path.dirname(outputFile), exist_ok=True)
|
||||||
|
with open(outputFile, "w") as f:
|
||||||
|
f.write(headerKeys)
|
||||||
|
|
||||||
|
def getLanguageVariableName(languageKey):
|
||||||
|
# Take the language key, prepend LANG_, uppercase, replace any non symbols
|
||||||
|
# with _
|
||||||
|
key = languageKey.strip().upper()
|
||||||
|
key = re.sub(r'[^A-Z0-9]', '_', key)
|
||||||
|
return f"LANG_{key}"
|
||||||
|
|
||||||
|
def processLanguage(asset):
|
||||||
|
cache = assetGetCache(asset['path'])
|
||||||
|
if cache is not None:
|
||||||
|
return cache
|
||||||
|
|
||||||
|
# Load PO File
|
||||||
|
po = polib.pofile(asset['path'])
|
||||||
|
|
||||||
|
langName = po.metadata.get('Language')
|
||||||
|
if langName not in LANGUAGE_DATA:
|
||||||
|
LANGUAGE_DATA[langName] = {}
|
||||||
|
|
||||||
|
for entry in po:
|
||||||
|
key = entry.msgid
|
||||||
|
val = entry.msgstr
|
||||||
|
|
||||||
|
if key not in LANGUAGE_KEYS:
|
||||||
|
LANGUAGE_KEYS.append(key)
|
||||||
|
|
||||||
|
if key not in LANGUAGE_DATA[langName]:
|
||||||
|
LANGUAGE_DATA[langName][key] = val
|
||||||
|
else:
|
||||||
|
print(f"Error: Duplicate translation key '{key}' in language '{langName}'")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
outLanguageData = {
|
||||||
|
'data': po,
|
||||||
|
'path': asset['path'],
|
||||||
|
'files': []
|
||||||
|
}
|
||||||
|
return assetCache(asset['path'], outLanguageData)
|
||||||
Reference in New Issue
Block a user