From 88309b16dfad7202315977e8171692f4c82ead3a Mon Sep 17 00:00:00 2001 From: darkrose Date: Fri, 10 Apr 2015 04:09:36 +1000 Subject: [PATCH] gettext without gettext --- cmake/Modules/FindGettextLib.cmake | 51 +- src/CMakeLists.txt | 23 +- src/character_creator.cpp | 2 +- src/content_clothesitem.cpp | 2 +- src/content_craftitem.cpp | 2 +- src/content_mapnode.cpp | 2 +- src/content_mapnode_circuit.cpp | 2 +- src/content_mapnode_door.cpp | 2 +- src/content_mapnode_farm.cpp | 2 +- src/content_mapnode_furniture.cpp | 2 +- src/content_mapnode_plants.cpp | 2 +- src/content_mapnode_slab.cpp | 2 +- src/content_mapnode_special.cpp | 2 +- src/content_mapnode_stair.cpp | 2 +- src/content_nodemeta.h | 2 +- src/content_toolitem.cpp | 2 +- src/game.cpp | 2 +- src/gettext.h | 48 -- src/guiDeathScreen.cpp | 2 +- src/guiFormSpecMenu.cpp | 2 +- src/guiKeyChangeMenu.h | 2 +- src/guiMainMenu.cpp | 2 +- src/guiMessageMenu.cpp | 2 +- src/guiPasswordChange.cpp | 2 +- src/guiPauseMenu.cpp | 2 +- src/guiTextInputMenu.cpp | 2 +- src/intl.cpp | 720 +++++++++++++++++++++++++++++ src/intl.h | 20 + src/main.cpp | 4 +- src/mapnode.cpp | 2 +- src/path.cpp | 26 +- src/settings.h | 1 + 32 files changed, 788 insertions(+), 153 deletions(-) delete mode 100644 src/gettext.h create mode 100644 src/intl.cpp create mode 100644 src/intl.h diff --git a/cmake/Modules/FindGettextLib.cmake b/cmake/Modules/FindGettextLib.cmake index 84d4af8..720c4ea 100644 --- a/cmake/Modules/FindGettextLib.cmake +++ b/cmake/Modules/FindGettextLib.cmake @@ -6,60 +6,13 @@ SET(CUSTOM_GETTEXT_PATH "${PROJECT_SOURCE_DIR}/../../gettext" # by default SET(GETTEXT_FOUND FALSE) -FIND_PATH(GETTEXT_INCLUDE_DIR - NAMES libintl.h - PATHS "${CUSTOM_GETTEXT_PATH}/include" - DOC "gettext include directory") - FIND_PROGRAM(GETTEXT_MSGFMT NAMES msgfmt PATHS "${CUSTOM_GETTEXT_PATH}/bin" DOC "path to msgfmt") -# modern Linux, as well as Mac, seem to not need require special linking -# they do not because gettext is part of glibc -# TODO check the requirements on other BSDs and older Linux -IF (WIN32) - IF(MSVC) - SET(GETTEXT_LIB_NAMES - libintl.lib intl.lib libintl3.lib intl3.lib) - ELSE() - SET(GETTEXT_LIB_NAMES - libintl.dll.a intl.dll.a libintl3.dll.a intl3.dll.a) - ENDIF() - FIND_LIBRARY(GETTEXT_LIBRARY - NAMES ${GETTEXT_LIB_NAMES} - PATHS "${CUSTOM_GETTEXT_PATH}/lib" - DOC "gettext *intl*.lib") - FIND_FILE(GETTEXT_DLL - NAMES libintl.dll intl.dll libintl3.dll intl3.dll - PATHS "${CUSTOM_GETTEXT_PATH}/bin" "${CUSTOM_GETTEXT_PATH}/lib" - DOC "gettext *intl*.dll") - FIND_FILE(GETTEXT_ICONV_DLL - NAMES libiconv2.dll - PATHS "${CUSTOM_GETTEXT_PATH}/bin" "${CUSTOM_GETTEXT_PATH}/lib" - DOC "gettext *iconv*.lib") -ENDIF(WIN32) - -IF(GETTEXT_INCLUDE_DIR AND GETTEXT_MSGFMT) - IF (WIN32) - # in the Win32 case check also for the extra linking requirements - IF(GETTEXT_LIBRARY AND GETTEXT_DLL AND GETTEXT_ICONV_DLL) - SET(GETTEXT_FOUND TRUE) - ENDIF() - ELSE(WIN32) - # *BSD variants require special linkage as they don't use glibc - IF(${CMAKE_SYSTEM_NAME} MATCHES "BSD") - SET(GETTEXT_LIBRARY "intl") - ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "BSD") - IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - SET(GETTEXT_LIBRARY "${CUSTOM_GETTEXT_PATH}/lib/libintl.dylib") - ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - SET(GETTEXT_FOUND TRUE) - ENDIF(WIN32) -ENDIF() - -IF(GETTEXT_FOUND) +IF(GETTEXT_MSGFMT) + SET(GETTEXT_FOUND TRUE) SET(GETTEXT_PO_PATH ${CMAKE_SOURCE_DIR}/po) SET(GETTEXT_MO_BUILD_PATH ${CMAKE_BINARY_DIR}/locale//LC_MESSAGES) SET(GETTEXT_MO_DEST_PATH ${SHAREDIR}/../locale//LC_MESSAGES) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index db317c0..bffb500 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,13 +14,7 @@ set(USE_GETTEXT 0) find_package(GettextLib) if(GETTEXT_FOUND AND ENABLE_GETTEXT) - message(STATUS "gettext include path: ${GETTEXT_INCLUDE_DIR}") message(STATUS "gettext msgfmt path: ${GETTEXT_MSGFMT}") - if(WIN32) - message(STATUS "gettext library: ${GETTEXT_LIBRARY}") - message(STATUS "gettext dll: ${GETTEXT_DLL}") - message(STATUS "gettext iconv dll: ${GETTEXT_ICONV_DLL}") - endif() set(USE_GETTEXT 1) message(STATUS "GetText enabled; locales found: ${GETTEXT_AVAILABLE_LOCALES}") elseif(GETTEXT_FOUND AND NOT ENABLE_GETTEXT) @@ -198,6 +192,7 @@ set(common_SRCS content_mapnode_util.cpp content_list.cpp content_nodebox.cpp + intl.cpp auth.cpp collision.cpp nodemetadata.cpp @@ -286,7 +281,6 @@ include_directories( ${CMAKE_BUILD_TYPE} ${PNG_INCLUDE_DIR} ${AUDIO_INCLUDE_DIRS} - ${GETTEXT_INCLUDE_DIR} ${JTHREAD_INCLUDE_DIR} ${SQLITE3_INCLUDE_DIR} ) @@ -316,7 +310,6 @@ if(BUILD_CLIENT) ${PNG_LIBRARIES} ${X11_LIBRARIES} ${AUDIO_LIBRARIES} - ${GETTEXT_LIBRARY} ${JTHREAD_LIBRARY} ${SQLITE3_LIBRARY} ${PLATFORM_LIBS} @@ -427,11 +420,9 @@ if(BUILD_CLIENT) install(FILES ${images} DESTINATION ${SHAREDIR}/textures) file(GLOB skins "${CMAKE_CURRENT_SOURCE_DIR}/../data/textures/skins/*.png") install(FILES ${skins} DESTINATION ${SHAREDIR}/textures/skins) - file(GLOB models -"${CMAKE_CURRENT_SOURCE_DIR}/../data/models/*.b3d") + file(GLOB models "${CMAKE_CURRENT_SOURCE_DIR}/../data/models/*.b3d") install(FILES ${models} DESTINATION ${SHAREDIR}/models) - file(GLOB modelsx -"${CMAKE_CURRENT_SOURCE_DIR}/../data/models/*.x") + file(GLOB modelsx "${CMAKE_CURRENT_SOURCE_DIR}/../data/models/*.x") install(FILES ${modelsx} DESTINATION ${SHAREDIR}/models) file(GLOB sounds "${CMAKE_CURRENT_SOURCE_DIR}/../data/sounds/*.ogg") install(FILES ${sounds} DESTINATION ${SHAREDIR}/sounds) @@ -470,14 +461,6 @@ if(BUILD_CLIENT) install(FILES ${OPENAL_DLL} DESTINATION ${BINDIR}) endif() endif(USE_AUDIO) - if(USE_GETTEXT) - if(DEFINED GETTEXT_DLL) - install(FILES ${GETTEXT_DLL} DESTINATION ${BINDIR}) - endif() - if(DEFINED GETTEXT_ICONV_DLL) - install(FILES ${GETTEXT_ICONV_DLL} DESTINATION ${BINDIR}) - endif() - endif(USE_GETTEXT) endif() if(APPLE) # TODO: install Irrlicht.framework into app bundle! diff --git a/src/character_creator.cpp b/src/character_creator.cpp index 0e3fc5b..6896880 100644 --- a/src/character_creator.cpp +++ b/src/character_creator.cpp @@ -34,7 +34,7 @@ #include "utility.h" #include "gui_colours.h" -#include "gettext.h" +#include "intl.h" GUICharDefMenu::GUICharDefMenu( IrrlichtDevice* device, diff --git a/src/content_clothesitem.cpp b/src/content_clothesitem.cpp index 4e1b3e4..e28dcc2 100644 --- a/src/content_clothesitem.cpp +++ b/src/content_clothesitem.cpp @@ -23,7 +23,7 @@ #include "content_list.h" #include "content_mapnode.h" #include -#include "gettext.h" +#include "intl.h" std::map g_content_clothesitem_features; diff --git a/src/content_craftitem.cpp b/src/content_craftitem.cpp index 907cd9e..d0c93ac 100644 --- a/src/content_craftitem.cpp +++ b/src/content_craftitem.cpp @@ -4,7 +4,7 @@ #include "content_mob.h" #include "content_list.h" #include -#include "gettext.h" +#include "intl.h" std::map g_content_craftitem_features; diff --git a/src/content_mapnode.cpp b/src/content_mapnode.cpp index 6543d70..ff5394c 100644 --- a/src/content_mapnode.cpp +++ b/src/content_mapnode.cpp @@ -39,7 +39,7 @@ #ifndef SERVER #include "tile.h" #endif -#include "gettext.h" +#include "intl.h" #define WATER_ALPHA 160 diff --git a/src/content_mapnode_circuit.cpp b/src/content_mapnode_circuit.cpp index 395c8bc..8e9d171 100644 --- a/src/content_mapnode_circuit.cpp +++ b/src/content_mapnode_circuit.cpp @@ -24,7 +24,7 @@ #include "content_list.h" #include "content_craft.h" #include "content_nodemeta.h" -#include "gettext.h" +#include "intl.h" void content_mapnode_circuit(bool repeat) { diff --git a/src/content_mapnode_door.cpp b/src/content_mapnode_door.cpp index ad50b4a..b960340 100644 --- a/src/content_mapnode_door.cpp +++ b/src/content_mapnode_door.cpp @@ -24,7 +24,7 @@ #include "content_list.h" #include "content_craft.h" #include "content_nodemeta.h" -#include "gettext.h" +#include "intl.h" void content_mapnode_door(bool repeat) { diff --git a/src/content_mapnode_farm.cpp b/src/content_mapnode_farm.cpp index 580e4a6..a389331 100644 --- a/src/content_mapnode_farm.cpp +++ b/src/content_mapnode_farm.cpp @@ -26,7 +26,7 @@ #include "content_craftitem.h" #include "content_nodemeta.h" #include "settings.h" -#include "gettext.h" +#include "intl.h" void content_mapnode_farm(bool repeat) { diff --git a/src/content_mapnode_furniture.cpp b/src/content_mapnode_furniture.cpp index 672f6a4..3add3ae 100644 --- a/src/content_mapnode_furniture.cpp +++ b/src/content_mapnode_furniture.cpp @@ -24,7 +24,7 @@ #include "content_list.h" #include "content_craft.h" #include "content_nodemeta.h" -#include "gettext.h" +#include "intl.h" void content_mapnode_furniture(bool repeat) { diff --git a/src/content_mapnode_plants.cpp b/src/content_mapnode_plants.cpp index ebc13a7..de07506 100644 --- a/src/content_mapnode_plants.cpp +++ b/src/content_mapnode_plants.cpp @@ -28,7 +28,7 @@ #include "content_craft.h" #include "content_nodemeta.h" #include "settings.h" -#include "gettext.h" +#include "intl.h" void content_mapnode_plants(bool repeat) { diff --git a/src/content_mapnode_slab.cpp b/src/content_mapnode_slab.cpp index c6c8c96..05d4199 100644 --- a/src/content_mapnode_slab.cpp +++ b/src/content_mapnode_slab.cpp @@ -24,7 +24,7 @@ #include "content_list.h" #include "content_craft.h" #include "content_nodemeta.h" -#include "gettext.h" +#include "intl.h" void content_mapnode_slab(bool repeat) { diff --git a/src/content_mapnode_special.cpp b/src/content_mapnode_special.cpp index faa70a1..3eadfdb 100644 --- a/src/content_mapnode_special.cpp +++ b/src/content_mapnode_special.cpp @@ -24,7 +24,7 @@ #include "content_list.h" #include "content_craft.h" #include "content_nodemeta.h" -#include "gettext.h" +#include "intl.h" void content_mapnode_special(bool repeat) { diff --git a/src/content_mapnode_stair.cpp b/src/content_mapnode_stair.cpp index bbf7e0b..986adf0 100644 --- a/src/content_mapnode_stair.cpp +++ b/src/content_mapnode_stair.cpp @@ -24,7 +24,7 @@ #include "content_list.h" #include "content_craft.h" #include "content_nodemeta.h" -#include "gettext.h" +#include "intl.h" void content_mapnode_stair(bool repeat) { diff --git a/src/content_nodemeta.h b/src/content_nodemeta.h index b0bfd71..b649fdb 100644 --- a/src/content_nodemeta.h +++ b/src/content_nodemeta.h @@ -27,7 +27,7 @@ #define CONTENT_NODEMETA_HEADER #include "nodemetadata.h" -#include "gettext.h" +#include "intl.h" class ServerEnvironment; class Inventory; diff --git a/src/content_toolitem.cpp b/src/content_toolitem.cpp index 086d820..0b6e30d 100644 --- a/src/content_toolitem.cpp +++ b/src/content_toolitem.cpp @@ -24,7 +24,7 @@ #include "content_list.h" #include "content_mapnode.h" #include -#include "gettext.h" +#include "intl.h" std::map g_content_toolitem_features; diff --git a/src/game.cpp b/src/game.cpp index 010d238..175abc7 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -45,7 +45,7 @@ #include "settings.h" #include "profiler.h" #include "mainmenumanager.h" -#include "gettext.h" +#include "intl.h" #include "log.h" #include "filesys.h" #include "path.h" diff --git a/src/gettext.h b/src/gettext.h deleted file mode 100644 index 17ec3a9..0000000 --- a/src/gettext.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef GETTEXT_HEADER -#include "config.h" // for USE_GETTEXT -#include - -#if USE_GETTEXT -#include -#else -#define gettext(String) String -#define ngettext(String1,String2,Int) String2 -#endif - -#define _(String) gettext(String) -#define gettext_noop(String) String -#define N_(String) gettext_noop (String) - -inline void init_gettext(const char *path) { -#if USE_GETTEXT - // don't do this if MSVC compiler is used, it gives an assertion fail - #ifndef _MSC_VER - setlocale(LC_MESSAGES, ""); - setlocale(LC_CTYPE, ""); - #endif - bindtextdomain(PROJECT_NAME, path); - textdomain(PROJECT_NAME); -#endif -} - -inline wchar_t* chartowchar_t(const char *str) -{ - size_t l = strlen(str)+1; - wchar_t* nstr = new wchar_t[l]; - mbstowcs(nstr, str, l); - return nstr; -} - -inline wchar_t* wgettext(const char *str) -{ - wchar_t *r = chartowchar_t(gettext(str)); - return r; -} - -inline wchar_t* wngettext(const char *str1, const char *str2, int n) -{ - wchar_t *r = chartowchar_t(ngettext(str1,str2,n)); - return r; -} -#define GETTEXT_HEADER -#endif diff --git a/src/guiDeathScreen.cpp b/src/guiDeathScreen.cpp index 81a307e..dc2b75a 100644 --- a/src/guiDeathScreen.cpp +++ b/src/guiDeathScreen.cpp @@ -32,7 +32,7 @@ #include #include #include -#include "gettext.h" +#include "intl.h" #include "client.h" #include "gui_colours.h" diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index 7e3b720..4007d6e 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -39,7 +39,7 @@ #include "tile.h" // ITextureSource #include "path.h" #include "gui_colours.h" -#include "gettext.h" +#include "intl.h" #if USE_FREETYPE #include "intlGUIEditBox.h" #endif diff --git a/src/guiKeyChangeMenu.h b/src/guiKeyChangeMenu.h index 0b30e60..fba5417 100644 --- a/src/guiKeyChangeMenu.h +++ b/src/guiKeyChangeMenu.h @@ -32,7 +32,7 @@ #include "utility.h" #include "modalMenu.h" #include "client.h" -#include "gettext.h" +#include "intl.h" #include "keycode.h" #include diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp index 11ad52d..f4632bb 100644 --- a/src/guiMainMenu.cpp +++ b/src/guiMainMenu.cpp @@ -45,7 +45,7 @@ #endif #include "sound.h" -#include "gettext.h" +#include "intl.h" GUIMainMenu::GUIMainMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, diff --git a/src/guiMessageMenu.cpp b/src/guiMessageMenu.cpp index 4125f08..9f73be2 100644 --- a/src/guiMessageMenu.cpp +++ b/src/guiMessageMenu.cpp @@ -33,7 +33,7 @@ #include #include -#include "gettext.h" +#include "intl.h" #include "gui_colours.h" GUIMessageMenu::GUIMessageMenu(gui::IGUIEnvironment* env, diff --git a/src/guiPasswordChange.cpp b/src/guiPasswordChange.cpp index bc7c208..a5df805 100644 --- a/src/guiPasswordChange.cpp +++ b/src/guiPasswordChange.cpp @@ -33,7 +33,7 @@ #include #include #include -#include "gettext.h" +#include "intl.h" #include "gui_colours.h" #if USE_FREETYPE #include "intlGUIEditBox.h" diff --git a/src/guiPauseMenu.cpp b/src/guiPauseMenu.cpp index 3943a48..0400775 100644 --- a/src/guiPauseMenu.cpp +++ b/src/guiPauseMenu.cpp @@ -35,7 +35,7 @@ #include #include -#include "gettext.h" +#include "intl.h" #include "gui_colours.h" GUIPauseMenu::GUIPauseMenu(gui::IGUIEnvironment* env, diff --git a/src/guiTextInputMenu.cpp b/src/guiTextInputMenu.cpp index bd92734..df79a07 100644 --- a/src/guiTextInputMenu.cpp +++ b/src/guiTextInputMenu.cpp @@ -32,7 +32,7 @@ #include #include #include -#include "gettext.h" +#include "intl.h" #include "gui_colours.h" #if USE_FREETYPE #include "intlGUIEditBox.h" diff --git a/src/intl.cpp b/src/intl.cpp new file mode 100644 index 0000000..96595a3 --- /dev/null +++ b/src/intl.cpp @@ -0,0 +1,720 @@ +/************************************************************************ +* gettext.cpp +* voxelands - 3d voxel world sandbox game +* Copyright (C) Lisa 'darkrose' Milne 2013-2015 +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +************************************************************************/ + +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#include +#endif + +#include "intl.h" +#include "path.h" + +typedef struct intl_s { + void *mo_data; + int inv_endian; + int str_count; + int tbl_offset[2]; + int hash_count; + int hash_offset; + uint32_t plurals; + char* plural; +} intl_t; + +#define INTL_MO_MAGIC 0x950412DE +#define INTL_MO_MAGIC_REV 0xDE120495 +#define INTL_ORIG 0 +#define INTL_TRANS 1 + +/* remove whitespace from beginning and end of a string */ +static char* intl_trim(char* str) +{ + int32_t l; + + while (*str && isspace(*str)) { + str++; + } + + l = strlen(str); + while (--l > -1 && (!str[l] || isspace(str[l]))) { + str[l] = 0; + } + + return str; +} + +static uint32_t intl_eval_preceed(char* str, char* p, char** b) +{ + int mul = 1; + uint32_t r = 0; + while (isdigit(*p) || (mul == 1 && isspace(*p))) { + r += ((*p)-48)*mul; + mul *= 10; + if (p == str) + break; + p--; + } + if (p != str) + p++; + *b = p; + return r; +} + +/* returns 1 if str contains only a number */ +static uint32_t intl_eval_check(char* str) +{ + char buff[50]; + uint32_t v = strtoul(str,NULL,10); + if (!str || !str[0]) + return 1; + sprintf(buff,"%u",v); + if (!strcmp(buff,str)) + return 1; + return 0; +} + +/* evaluate a plurals expression to an integer value */ +static uint32_t intl_eval(char* str) +{ + char buff[1024]; + char* qc; + char* cc; + char* ac; + char* oc; + str = intl_trim(str); + if (intl_eval_check(str)) + return strtoul(str,NULL,10); + + if (str[0] == '(') { + int o = 1; + int c = 0; + uint32_t r; + int i; + for (i=1; str[i]; i++) { + switch (str[i]) { + case '(': + o++; + break; + case ')': + c++; + break; + default:; + } + if (o == c) + break; + } + c = str[i]; + str[i] = 0; + r = intl_eval(str+1); + str[i] = c; + if (c) { + sprintf(buff,"%u%s",r,str+i+1); + }else{ + sprintf(buff,"%u",r); + } + if (intl_eval_check(buff)) + return strtoul(buff,NULL,10); + return intl_eval(buff); + } + + qc = strchr(str,'?'); + if (qc) { + int o = 1; + int c = 0; + uint32_t r; + int i; + for (i=1; qc[i]; i++) { + switch (qc[i]) { + case '?': + o++; + break; + case ':': + c++; + break; + default:; + } + if (o == c) + break; + } + if (o != c) + return 0; + cc = qc+i; + *qc = 0; + r = intl_eval(str); + *qc = '?'; + if (r) { + *cc = 0; + strcpy(buff,qc+1); + *cc = ':'; + }else{ + strcpy(buff,cc+1); + } + if (intl_eval_check(buff)) + return strtoul(buff,NULL,10); + return intl_eval(buff); + } + + ac = strstr(str,"&&"); + oc = strstr(str,"||"); + if (ac && (!oc || ac < oc)) { + uint32_t r; + *ac = 0; + r = intl_eval(str); + *ac = '&'; + if (!r) + return 0; + strcpy(buff,ac+2); + if (intl_eval_check(buff)) + return strtoul(buff,NULL,10); + return intl_eval(buff); + } + + if (oc) { + uint32_t r; + *oc = 0; + r = intl_eval(str); + *oc = '|'; + if (r) + return 1; + strcpy(buff,oc+2); + if (intl_eval_check(buff)) + return strtoul(buff,NULL,10); + return intl_eval(buff); + } + + ac = strchr(str,'='); + if (ac) { + uint32_t r1; + uint32_t r2; + uint32_t r3 = 0; + char* e; + char* n; + char ec; + if (*(ac-1) == '!') { + e = ac-1; + n = ac+1; + }else if (*(ac-1) == '>') { + e = ac-1; + n = ac+1; + }else if (*(ac-1) == '<') { + e = ac-1; + n = ac+1; + }else if (*(ac+1) == '=') { + e = ac; + n = ac+2; + }else{ + return 0; + } + ec = *e; + *e = 0; + r1 = intl_eval(str); + r2 = intl_eval(n); + *e = ec; + switch (ec) { + case '!': + r3 = (r1 != r2); + break; + case '>': + r3 = (r1 >= r2); + break; + case '<': + r3 = (r1 <= r2); + break; + case '=': + r3 = (r1 == r2); + break; + } + return r3; + } + + ac = strchr(str,'<'); + if (ac) { + uint32_t r1; + uint32_t r2; + *ac = 0; + r1 = intl_eval(str); + r2 = intl_eval(ac+1); + *ac = '<'; + return (r1'); + if (ac) { + uint32_t r1; + uint32_t r2; + *ac = 0; + r1 = intl_eval(str); + r2 = intl_eval(ac+1); + *ac = '>'; + return (r1>r2); + } + + ac = strchr(str,'*'); + oc = strchr(str,'/'); + if (!ac && !oc) { + ac = strchr(str,'%'); + if (!ac) { + ac = strchr(str,'+'); + oc = strchr(str,'-'); + if (oc && (!ac || oc < ac)) + ac = oc; + } + }else if (oc && (!ac || oc < ac)) { + ac = oc; + } + if (ac) { + uint32_t r1; + uint32_t r2; + uint32_t r3 = 0; + char* b; + char* e; + + if (ac == str) + return 0; + + r1 = intl_eval_preceed(str,ac-1,&b); + r2 = strtoul(ac+1,&e,10); + switch(*ac) { + case '*': + r3 = (r1*r2); + break; + case '/': + if (!r2) { + r3 = 0; + }else{ + r3 = (r1/r2); + } + break; + case '%': + if (!r2) { + r3 = 0; + }else{ + r3 = (r1%r2); + } + break; + case '+': + r3 = (r1+r2); + break; + case '-': + r3 = (r1-r2); + break; + } + + if (b != str) { + char bc = *b; + *b = 0; + sprintf(buff,"%s%u%s",str,r3,e); + *b = bc; + }else if (*e) { + sprintf(buff,"%u%s",r3,e); + }else{ + return r3; + } + if (intl_eval_check(buff)) + return strtoul(buff,NULL,10); + return intl_eval(buff); + } + + if (intl_eval_check(str)) + return strtoul(str,NULL,10); + + return 0; +} + +/* get the plural index based on n */ +static uint32_t intl_getplural(intl_t *intl, int n) +{ + char buff[1024]; + char nb[50]; + int i; + int k; + int o; + uint32_t r; + + if (!intl->plural) + return 0; + + sprintf(nb,"%d",n); + + for (o=0,i=7; intl->plural[i]; i++) { + if (o > 1022) + return 0; + if (intl->plural[i] == 'n') { + for (k=0; nb[k]; k++) { + buff[o++] = nb[k]; + if (o > 1022) + return 0; + } + continue; + } + buff[o++] = intl->plural[i]; + } + buff[o] = 0; + + r = intl_eval(buff); + if (r >= intl->plurals) + return 0; + return r; +} + +/* basic hashpjw used by lots of things, including mo files */ +static uint32_t intl_hash(const char *str_param) +{ + uint32_t hval = 0; + uint32_t g; + const char *s = str_param; + + while (*s) { + hval <<= 4; + hval += (unsigned char) *s++; + g = hval & ((uint32_t) 0xf << 28); + if (g != 0) { + hval ^= g >> 24; + hval ^= g; + } + } + + return hval; +} + +/* read a 32 bit int with correct endianness */ +static uint32_t intl_readint(intl_t *intl, int offset) +{ + uint8_t *p; + uint32_t r = *((uint32_t*)(((char*)intl->mo_data) + offset)); + + if (!intl->inv_endian) + return r; + + p = (uint8_t*)&r; + return (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]; +} + +/* get a string from either the original strings table INTL_ORIG + * or the translated strings tables INTL_TRANS */ +static char* intl_getstr(intl_t *intl, int type, int index, int *length) +{ + uint32_t offset = intl->tbl_offset[type] + (8 * index); + *length = intl_readint(intl,offset); + offset = intl_readint(intl,offset+4); + + return ((char *)intl->mo_data) + offset; +} + +/* get the language code (en_GB etc) */ +static void intl_getlang(char* buff, int size) +{ +#ifdef _WIN32 + char l1[4]; + char l2[4]; + + if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, (LPSTR)l1, 4)) + strcpy(l1,"en"); + if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME , (LPSTR)l2, 4)) + l2[0] = 0; + + if (l1[0] && l2[0]) { + snprintf(buff,size,"%s_%s",l1,l2); + }else if (l1[0]) { + strncpy(buff,l1,size); + }else{ + strncpy(buff,"en",size); + } +#else + char* lang = getenv("LANG"); + if (!lang || !lang[0]) + lang = (char*)"en"; + strncpy(buff,lang,size); +#endif +} + +/* essentially gettext() but with a per-mo-file option */ +char* intl_lookup(intl_t *intl, char *s, int *length) +{ + uint32_t h; + int hp; + int hp_o; + int inc; + uint32_t i; + char* l; + int len; + + if (!intl || !intl->mo_data) + return s; + + h = intl_hash(s); + hp = h % intl->hash_count; + hp_o = hp; + inc = 1 + (h % (intl->hash_count - 2)); + + while (1) { + i = intl->hash_offset+(4*hp); + i = intl_readint(intl, i); + if (!i) + break; + + /* entries in the table are stored +1 so that 0 means empty */ + i--; + + l = intl_getstr(intl,INTL_ORIG,i,&len); + if (!strcmp(l,s)) { + l = intl_getstr(intl,INTL_TRANS,i,&len); + if (length) + *length = len; + return l; + } + + hp += inc; + hp %= intl->hash_count; + + if (hp == hp_o) + break; + } + + if (length) + *length = strlen(s); + return s; +} + +/* as per intl_lookup, but for ngettext() */ +char* intl_nlookup(intl_t *intl, char* s1, char* s2, int n) +{ + char* s = s1; + int p; + int i; + int length; + int sl; + int cl; + int tl; + char* l; + char* c = NULL; + + if (!s2) + return intl_lookup(intl,s1,NULL); + + if (!intl || !intl->mo_data) { + if (n == 1) + return s1; + return s2; + } + + l = intl_lookup(intl,s,&length); + if (intl->plurals < 2) + return l; + sl = strlen(l); + if (sl == length) + return l; + + p = intl_getplural(intl,n); + if (!p) + return l; + + tl = sl; + + for (i=0; i= length) + return l; + c = l+tl+1; + cl = strlen(c); + tl += cl+1; + } + + return c; +} + +/* initialise an intl_t by loading in an mo file */ +int intl_init(intl_t *intl, const char* file) +{ + char fbuff[2048]; + char lbuff[128]; + FILE *f; + int length; + uint32_t magic; + char* head; + char* p; + char* e; + std::string path; + + intl_getlang(lbuff,128); + + intl->mo_data = NULL; + intl->str_count = 0; + intl->tbl_offset[INTL_ORIG] = 0; + intl->tbl_offset[INTL_TRANS] = 0; + intl->hash_count = 0; + intl->hash_offset = 0; + intl->plurals = 1; + intl->plural = NULL; + + path = std::string("translation-")+lbuff; + path = getPath(path.c_str(),file,true); + if (path == "") { + char* u = strchr(lbuff,'_'); + if (!u) + return 1; + *u = 0; + + path = std::string("translation-")+lbuff; + path = getPath(path.c_str(),file,true); + if (path == "") + return 1; + } + + f = fopen(path.c_str(), "rb"); + if (!f) + return 2; + + fseek(f, 0, SEEK_END); + length = ftell(f); + fseek(f, 0, SEEK_SET); + + if (length < 24) { + fclose(f); + return 3; + } + + intl->mo_data = malloc(length); + if (!intl->mo_data) { + fclose(f); + return 4; + } + + if (length != (int)fread(intl->mo_data, 1, length, f)) { + fclose(f); + free(intl->mo_data); + intl->mo_data = NULL; + return 5; + } + fclose(f); + + magic = ((uint32_t*)intl->mo_data)[0]; + + if (magic == INTL_MO_MAGIC) { + intl->inv_endian = 0; + }else if (magic == INTL_MO_MAGIC_REV) { + intl->inv_endian = 1; + }else{ + free(intl->mo_data); + intl->mo_data = NULL; + return 6; + } + + intl->str_count = intl_readint(intl, 8); + intl->tbl_offset[INTL_ORIG] = intl_readint(intl, 12); + intl->tbl_offset[INTL_TRANS] = intl_readint(intl, 16); + intl->hash_count = intl_readint(intl, 20); + intl->hash_offset = intl_readint(intl, 24); + + if (!intl->hash_count) { + free(intl->mo_data); + intl->mo_data = NULL; + return 7; + } + + head = intl_lookup(intl,(char*)"",NULL); + if (!head) + return 0; + + p = strstr(head,"Plural-Forms:"); + if (!p) + return 0; + + head = p; + e = strchr(head,'\n'); + if (e) + *e = 0; + + strncpy(fbuff,head+13,2048); + if (e) + *e = '\n'; + head = fbuff; + + p = strstr(head,"nplurals="); + if (!p) + return 0; + e = strchr(p,';'); + if (e) + *e = 0; + + intl->plurals = strtol(p+9,NULL,10); + if (e) + *e = ';'; + + p = strstr(head,"plural="); + if (!p) + return 0; + e = strchr(p,';'); + if (e) + *e = 0; + + intl->plural = strdup(p); + if (e) + *e = ';'; + + return 0; +} + +/* the normal stuff */ +static intl_t intl; +char* gettext(const char *s) +{ + return intl_lookup(&intl,(char*)s,NULL); +} +char* ngettext(const char* s1, const char* s2, int n) +{ + return intl_nlookup(&intl,(char*)s1,(char*)s2,n); +} + +wchar_t *mb2wc(const char *src) +{ + mbstate_t state; + int l = strlen(src)+1; + memset(&state, '\0', sizeof (state)); + wchar_t *buff = new wchar_t[l]; + size_t n = mbsrtowcs(buff, &src, l, &state); + printf("%ld\n",(int64_t)n); + buff[n] = L'\0'; + return buff; +} + +wchar_t* wgettext(const char *str) +{ + char* s = intl_lookup(&intl,(char*)str,NULL); + printf("%s\n",s); + return mb2wc(s); +} + +wchar_t* wngettext(const char *str1, const char *str2, int n) +{ + char* s = intl_nlookup(&intl,(char*)str1,(char*)str2,n); + return mb2wc(s); +} + +void init_gettext() +{ +#ifndef _MSC_VER + setlocale(LC_MESSAGES, ""); + setlocale(LC_CTYPE, ""); +#endif + intl_init(&intl,"voxelands.mo"); +} diff --git a/src/intl.h b/src/intl.h new file mode 100644 index 0000000..bb19221 --- /dev/null +++ b/src/intl.h @@ -0,0 +1,20 @@ +#ifndef GETTEXT_HEADER +#define GETTEXT_HEADER +#include + +char* gettext(const char *s); +char* ngettext(const char* s1, const char* s2, int n); +void init_gettext(); + +inline wchar_t* chartowchar_t(const char *str) +{ + size_t l = strlen(str)+1; + wchar_t* nstr = new wchar_t[l]; + mbstowcs(nstr, str, l); + return nstr; +} + +wchar_t* wgettext(const char *str); +wchar_t* wngettext(const char *str1, const char *str2, int n); + +#endif diff --git a/src/main.cpp b/src/main.cpp index bd9fd7d..1a96335 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -63,7 +63,7 @@ #include "keycode.h" #include "tile.h" #include "defaultsettings.h" -#include "gettext.h" +#include "intl.h" #include "settings.h" #include "profiler.h" #include "log.h" @@ -825,7 +825,7 @@ int main(int argc, char *argv[]) // Create user data directory fs::CreateDir(porting::path_userdata); - init_gettext((porting::path_data+DIR_DELIM+".."+DIR_DELIM+"locale").c_str()); + init_gettext(); // Initialize debug streams #ifdef RUN_IN_PLACE diff --git a/src/mapnode.cpp b/src/mapnode.cpp index 8ca67d1..ac34545 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -206,7 +206,7 @@ ContentFeatures & content_features(MapNode &n) #ifndef SERVER #include "common_irrlicht.h" #include "game.h" -#include "gettext.h" +#include "intl.h" void init_mapnode(video::IVideoDriver* driver) #else void init_mapnode() diff --git a/src/path.cpp b/src/path.cpp index 92b8583..3e9dc22 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -141,26 +141,32 @@ std::string getPath(const char* tp, const std::string &filename, bool must_exist rel_path += std::string("sounds")+DIR_DELIM+filename; }else if (type == "font") { rel_path += std::string("fonts")+DIR_DELIM+filename; + }else if (type.substr(0,11) == "translation") { + std::string lang = type.substr(12); + type = "translation"; + rel_path += std::string("locale")+DIR_DELIM+lang+DIR_DELIM+filename; }else{ rel_path += filename; } /* check from data_path */ - std::string data_path = g_settings->get("data_path"); - if (data_path != "") { - std::string testpath = data_path + DIR_DELIM + rel_path; - if (type == "model" || type == "html" || type == "sound" || type == "skin" || type == "font") { - if (fs::PathExists(testpath)) - fullpath = std::string(testpath); - }else{ - fullpath = getImagePath(testpath); + if (g_settings->exists("data_path")) { + std::string data_path = g_settings->get("data_path"); + if (data_path != "") { + std::string testpath = data_path + DIR_DELIM + rel_path; + if (type == "model" || type == "html" || type == "sound" || type == "skin" || type == "font" || type == "translation") { + if (fs::PathExists(testpath)) + fullpath = std::string(testpath); + }else{ + fullpath = getImagePath(testpath); + } } } /* check from user data directory */ if (fullpath == "") { std::string testpath = porting::path_userdata + DIR_DELIM + rel_path; - if (type == "model" || type == "html" || type == "sound" || type == "skin" || type == "font") { + if (type == "model" || type == "html" || type == "sound" || type == "skin" || type == "font" || type == "translation") { if (fs::PathExists(testpath)) fullpath = std::string(testpath); }else{ @@ -171,7 +177,7 @@ std::string getPath(const char* tp, const std::string &filename, bool must_exist /* check from default data directory */ if (fullpath == "") { std::string testpath = porting::path_data + DIR_DELIM + rel_path; - if (type == "model" || type == "html" || type == "sound" || type == "skin" || type == "font") { + if (type == "model" || type == "html" || type == "sound" || type == "skin" || type == "font" || type == "translation") { if (fs::PathExists(testpath)) fullpath = std::string(testpath); }else{ diff --git a/src/settings.h b/src/settings.h index 49aecb9..3062aa4 100644 --- a/src/settings.h +++ b/src/settings.h @@ -412,6 +412,7 @@ public: n = m_defaults.find(name); if(n == NULL) { + printf("Not Found: %s\n",name.c_str()); infostream<<"Settings: Setting not found: \"" <