Ver Fonte

feat: 支持中文输出

使用HuanGetText汉化系统
SongZihuan há 3 anos atrás
pai
commit
b73df1c58f

+ 18 - 6
CMakeLists.txt

@@ -45,6 +45,8 @@ endif()
 set(PRINT_DIR_INFO ON CACHE BOOL "Print install and build dirs info.")
 set(_print_dir ${PRINT_DIR_INFO})
 
+include(tool)
+
 include(CFEP)  # 搜索CFEP.cmake
 if (_print_dir)
     set(PRINT_DIR_INFO OFF CACHE BOOL "Print install and build dirs info." FORCE)
@@ -52,10 +54,10 @@ if (_print_dir)
 else()
     wi_set_install_dir_quiet(NAMES aFunlang)  # 设置安装路径
 endif()
-file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/${INSTALL_LOG})  # 安装log输出的目录
-file(TO_NATIVE_PATH ${INSTALL_LOG} _INSTALL_LOG_NATIVE)
-string(REPLACE "\\" "\\\\" INSTALL_LOG_NATIVE ${_INSTALL_LOG_NATIVE})  # 转义
-unset(_INSTALL_LOG_NATIVE)
+
+to_native_path(${INSTALL_LOG} INSTALL_LOGDIR_NATIVE)
+to_native_path(${INSTALL_LOCALSTATEDIR} INSTALL_LOCALSTATEDIR_NATIVE)
+to_native_path(${INSTALL_LANGDIR} INSTALL_LANGDIR_NATIVE)
 
 include(filename)
 
@@ -74,7 +76,9 @@ set(base_compile_definitions
     aFunMajorVersion=${PROJECT_VERSION_MAJOR}
     aFunMinorVersion=${PROJECT_VERSION_MINOR}
     aFunPatchVersion=${PROJECT_VERSION_PATCH}
-    aFunLogDir="${INSTALL_LOG_NATIVE}")  # 默认的预定义宏
+    aFunLogDir="${INSTALL_LOGDIR_NATIVE}"
+    aFunVarLibDir="${INSTALL_LOCALSTATEDIR_NATIVE}"
+    aFunLangDir="${INSTALL_LANGDIR_NATIVE}")  # 默认的预定义宏
 
 if (WIN32 OR CYGWIN)
     list(APPEND base_compile_definitions aFunWIN32=1)
@@ -91,6 +95,7 @@ endif()
 add_compile_definitions(${base_compile_definitions})
 
 include(${CMAKE_CURRENT_LIST_DIR}/deps/deps.cmake)  # 安装依赖
+include(${CMAKE_CURRENT_LIST_DIR}/lang/hgt.cmake)  # 安装lang库
 add_subdirectory(src)
 include(aFunHeader)
 
@@ -150,7 +155,14 @@ install(FILES
         ${CMAKE_BINARY_DIR}/cmake-tmp/aFunlangConfigVersion.cmake
         DESTINATION ${INSTALL_CMAKEDIR})
 
-install(CODE "file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/${INSTALL_LOG})")  # 创建log目录
+function(install_directory dir)
+    file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/${dir})  # 安装log输出的目录
+    install(CODE "file(MAKE_DIRECTORY ${CMAKE_INSTALL_PREFIX}/${dir})")  # 创建log目录
+endfunction()
+
+install_directory(${INSTALL_LOG})
+install_directory(${INSTALL_LOCALSTATEDIR})
+install_directory(${INSTALL_LANGDIR})
 
 set(BUILD_TEST FALSE CACHE BOOL "Enable run test program")
 set(_build_test ${BUILD_TEST})

+ 9 - 1
cmake/CMakeFindExternalProject/InstallDir.cmake

@@ -35,12 +35,16 @@ function(wi_set_install_dir_quiet)
         set(DEF_INSTALL_CMAKEDIR cmake)
         set(DEF_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
         set(DEF_INSTALL_RESOURCEDIR ${CMAKE_INSTALL_DATAROOTDIR})  # 关联文件
+        set(DEF_INSTALL_LOCALSTATEDIR ${CMAKE_INSTALL_LOCALSTATEDIR}/lib)
+        set(DEF_INSTALL_LANGDIR ${DEF_INSTALL_LOCALSTATEDIR}/lang)
         set(DEF_INSTALL_LOG ${CMAKE_INSTALL_LOCALSTATEDIR}/log)
     else()
         # unix类系统(Unix, Linux, MacOS, Cygwin等)把cmake文件安装到指定的系统的cmake文件夹中
         set(DEF_INSTALL_CMAKEDIR ${CMAKE_INSTALL_DATAROOTDIR}/cmake/${_names})
         set(DEF_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR}/${_names})
         set(DEF_INSTALL_RESOURCEDIR ${CMAKE_INSTALL_DATAROOTDIR}/${_names})  # 关联文件 CMAKE_INSTALL_DATAROOTDIR指: share
+        set(DEF_INSTALL_LOCALSTATEDIR ${CMAKE_INSTALL_LOCALSTATEDIR}/lib/${_names})
+        set(DEF_INSTALL_LANGDIR ${DEF_INSTALL_LOCALSTATEDIR}/lang)
         set(DEF_INSTALL_LOG ${CMAKE_INSTALL_LOCALSTATEDIR}/log/${_names})
     endif()
 
@@ -50,11 +54,15 @@ function(wi_set_install_dir_quiet)
     set(INSTALL_CMAKEDIR ${DEF_INSTALL_CMAKEDIR} CACHE PATH "Installation directory for CMake files")
     set(INSTALL_INCLUDEDIR ${DEF_INSTALL_INCLUDEDIR} CACHE PATH "Installation directory for header files")
     set(INSTALL_RESOURCEDIR ${DEF_INSTALL_RESOURCEDIR} CACHE PATH "Installation directory for resource files")  # 关联文件
-    set(INSTALL_LOG ${DEF_INSTALL_LOG} CACHE PATH "Installation directory for log files")  # 关联文件
+    set(INSTALL_LOCALSTATEDIR ${DEF_INSTALL_LOCALSTATEDIR} CACHE PATH "Installation directory for lib files")
+    set(INSTALL_LANGDIR ${DEF_INSTALL_LANGDIR} CACHE PATH "Installation directory for lang files")
+    set(INSTALL_LOG ${DEF_INSTALL_LOG} CACHE PATH "Installation directory for log files")
 
     unset(DEF_INSTALL_CMAKEDIR)
     unset(DEF_INSTALL_INCLUDEDIR)
     unset(DEF_INSTALL_RESOURCEDIR)
+    unset(DEF_INSTALL_LOCALSTATEDIR)
+    unset(DEF_INSTALL_LANGDIR)
     unset(DEF_INSTALL_LOG)
 endfunction()
 

+ 8 - 0
cmake/tool.cmake

@@ -0,0 +1,8 @@
+function(to_native_path in out)
+    if (WIN32 AND NOT CYGWIN)
+        string(REPLACE "/" "\\\\" _out ${in})  # 转义
+        set(${out} ${_out} PARENT_SCOPE)
+    else()
+        set(${out} ${in} PARENT_SCOPE)
+    endif()
+endfunction()

+ 12 - 1
include/core/core_init.h

@@ -5,7 +5,18 @@
 #include <setjmp.h>
 #include "tool.h"
 
+typedef struct aFunCoreInitInfo aFunCoreInitInfo;
+struct aFunCoreInitInfo {
+    char *base_dir;
+
+    LogFactoryPrintConsole pc;
+    bool fe;
+    bool se;
+    jmp_buf *buf;
+    LogLevel level;
+};
+
 AFUN_CORE_EXPORT extern Logger *aFunCoreLogger;
-AFUN_CORE_EXPORT bool aFunCoreInit(char *log_dir, LogFactoryPrintConsole print_console, bool fe, bool se, jmp_buf *buf, LogLevel level);
+AFUN_CORE_EXPORT bool aFunCoreInit(aFunCoreInitInfo *info);
 
 #endif //AFUN_INIT_H

+ 0 - 1
include/main.h

@@ -2,7 +2,6 @@
 #define AFUN_MAIN_H
 
 extern char *base_path;
-extern char *log_path;
 extern Logger *aFunlangLogger;
 
 #endif //AFUN_MAIN_H

+ 10 - 2
include/runtime/aFunlang.h

@@ -2,10 +2,18 @@
 #define AFUN_AFUNLANG_H
 #include "aFunlangExport.h"
 #include "aFunCore.h"
-
 #include "runtime.h"
 
-AFUN_LANG_EXPORT bool aFunInit(char *log_dir, LogFactoryPrintConsole print_console, jmp_buf *buf, LogLevel level);
+typedef struct aFunInitInfo aFunInitInfo;
+struct aFunInitInfo {
+    char *base_dir;
+
+    LogFactoryPrintConsole pc;
+    jmp_buf *buf;
+    LogLevel level;
+};
+
+AFUN_LANG_EXPORT bool aFunInit(aFunInitInfo *info);
 
 AFUN_LANG_EXPORT af_Environment *creatAFunEnvironment(int argc, char **argv);
 AFUN_LANG_EXPORT void destructAFunEnvironment(af_Environment *env);

+ 1 - 0
include/tool/tool.h

@@ -25,5 +25,6 @@
 #include "log.h"
 
 #include "fflags.h"
+#include "_ht.h"
 
 #endif //AFUN_TOOL_H

+ 89 - 0
lang/hgt.cmake

@@ -0,0 +1,89 @@
+find_package(Python3 REQUIRED)
+
+set(hgt_dir ${CMAKE_BINARY_DIR}/hgt)
+file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/hgt)
+
+to_native_path(${hgt_dir} hgt_dir_n)
+to_native_path(${CMAKE_CURRENT_LIST_DIR}/tr tr_n)
+to_native_path(${CMAKE_SOURCE_DIR}/src src_n)
+
+set(HGT_COMMAND
+    "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_LIST_DIR}/hgt.py"
+    ${hgt_dir_n}
+    baseHTExport
+    BASEHT_EXPORT
+    ${tr_n}
+    ${src_n})
+
+unset(hgt_dir_n)
+unset(tr_n)
+unset(src_n)
+
+add_custom_target(hgt ALL)
+add_custom_command(TARGET hgt POST_BUILD
+                   COMMAND ${HGT_COMMAND}
+                   COMMENT "The hgt_generate_file: base")
+
+execute_process(COMMAND ${HGT_COMMAND}
+                RESULT_VARIABLE re
+                OUTPUT_VARIABLE output
+                ERROR_VARIABLE  errput)
+
+if (re)
+    # 运行失败
+    message(FATAL_ERROR "hgt error[${re}]\n${errput}")
+endif()
+
+add_library(hgt-base SHARED)
+target_sources(hgt-base
+               PRIVATE ${hgt_dir}/_ht.c
+               PUBLIC $<BUILD_INTERFACE:${hgt_dir}/_ht.h> $<INSTALL_INTERFACE:${INSTALL_INCLUDEDIR}/_ht.h>)
+target_link_libraries(hgt-base ${dlfcn_lib})
+add_dependencies(hgt-base hgt)
+set(hgt-lib hgt-base)
+
+include(GenerateExportHeader)
+generate_export_header(hgt-base EXPORT_FILE_NAME "${hgt_dir}/baseHTExport.h" BASE_NAME "baseHT")
+
+target_include_directories(hgt-base PUBLIC
+                           $<BUILD_INTERFACE:${hgt_dir}>
+                           $<INSTALL_INTERFACE:${INSTALL_INCLUDEDIR}>)
+set_target_properties(hgt-base PROPERTIES PUBLIC_HEADER "${hgt_dir}/_ht.h")
+
+install(TARGETS hgt-base
+        EXPORT aFunlang
+        RUNTIME DESTINATION ${INSTALL_BINDIR} COMPONENT runtime
+        ARCHIVE DESTINATION ${INSTALL_LIBDIR} COMPONENT dev
+        LIBRARY DESTINATION ${INSTALL_LIBDIR} COMPONENT runtime
+        PUBLIC_HEADER DESTINATION ${INSTALL_INCLUDEDIR} COMPONENT dev
+        PRIVATE_HEADER DESTINATION ${INSTALL_INCLUDEDIR} COMPONENT unsafe-dev)
+
+file(GLOB tr
+     LIST_DIRECTORIES FALSE
+     ${hgt_dir}/tr/*.c)
+
+function(build_lang)
+    foreach(src IN LISTS tr)
+        set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${INSTALL_LANGDIR})
+        set(CMAKE_LIBRARY_OUTPUT_DIRECTORY  ${CMAKE_BINARY_DIR}/${INSTALL_LANGDIR})
+
+        cmake_path(GET src STEM name)
+        add_library(hgt-${name} SHARED ${src})
+        add_dependencies(hgt-${name} hgt-base)
+        set_target_properties(hgt-${name} PROPERTIES OUTPUT_NAME "${name}")
+
+        target_compile_definitions(hgt-${name} PRIVATE hgt_base_EXPORTS)
+        target_include_directories(hgt-${name} PUBLIC ${hgt_dir})
+
+        install(TARGETS hgt-${name}
+                RUNTIME DESTINATION ${INSTALL_LANGDIR} COMPONENT runtime
+                LIBRARY DESTINATION ${INSTALL_LANGDIR} COMPONENT runtime)
+    endforeach()
+endfunction()
+
+build_lang()  # 使用函数防止 CMAKE_RUNTIME_OUTPUT_DIRECTORY 影响外部
+
+add_custom_command(TARGET hgt-zh_cn
+                   COMMAND ${CMAKE_COMMAND} -E echo "$<TARGET_FILE_NAME:hgt-zh_cn>" ">>" "LANG"
+                   WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/${INSTALL_LANGDIR}"
+                   COMMENT "Creat file ${CMAKE_BINARY_DIR}/${INSTALL_LANGDIR}/LANG")

+ 153 - 0
lang/hgt.py

@@ -0,0 +1,153 @@
+import os
+import sys
+import re
+
+from typing import List, Tuple, Dict
+
+FileList = List[str]
+FlatList = Dict[str, Tuple[str]]
+
+
+class RunError(Exception):
+    def __init__(self, message="RunError: run as module is not allowed."):
+        self.message = message
+
+
+class ArgError(Exception):
+    def __init__(self, message="ArgError: just 6 arg is allowed."):
+        self.message = message
+
+
+if __name__ != '__main__':
+    raise RunError()
+
+argv = sys.argv
+if len(argv) < 6:
+    print(f"{len(argv)}")
+    raise ArgError()
+
+output_dir = argv[1]
+export_h = argv[2]
+export = argv[3]
+translation = argv[4]
+input_dir = argv[5:]
+translation_output = os.path.join(output_dir, "tr")
+base_tr_file = os.path.join(translation, "base.py")
+
+try:
+    os.makedirs(output_dir)
+except OSError:
+    ...
+
+try:
+    os.makedirs(translation_output)
+except OSError:
+    ...
+
+base_tr = {}
+if os.path.exists(base_tr_file):
+    with open(base_tr_file, "r", encoding="utf-8") as f:
+        code = f.read()
+        exec(code, base_tr, base_tr)
+
+
+def getFileFromPath(paths: List[str], file_type_: str) -> FileList:
+    tmp: FileList = []
+    for path in paths:
+        for file_path, dir_names, file_names in os.walk(path):
+            for names in file_names:
+                file_type = os.path.splitext(names)[-1]
+                if file_type == file_type_:
+                    tmp.append(os.path.join(file_path, names))
+    return tmp
+
+
+file_list: FileList = getFileFromPath(input_dir, '.c')
+pattern = re.compile(r'HT_getText\(([\S]+),[\s]*(\"[^\"]*\")\)')
+flat_list: FlatList = {}
+for file in file_list:
+    with open(file, "r", encoding="utf-8") as f:
+        data = f.readline()
+        while data:
+            result: List[str] = pattern.findall(data)
+            for i in result:
+                if i[0] in flat_list and flat_list[i[0]][0] != "":  # 若果是空字串则可以覆盖
+                    continue
+
+                if i[0] not in flat_list:
+                    tmp = i[1]
+                    if tmp == "\"<base-tr>\"":
+                        tmp = f"\"{base_tr.get(i[0], None)}\""
+                    default_ = tmp.replace('\n', '\\n').replace('\r', '')
+                    flat_list[i[0]] = (default_,)  # 生成一个数组
+            data = f.readline()
+
+with open(os.path.join(output_dir, f"_ht.c"), "w", encoding="utf-8") as fc:
+    with open(os.path.join(output_dir, f"_ht.h"), "w", encoding="utf-8") as fh:
+        head = f'''/*
+ * File: _ht.c/_ht.c
+ * The file is automatically generated by Huan-GetText
+ * Encoding: utf-8
+ */'''
+        fc.write(head + '\n\n')
+        fh.write(head + '\n\n')
+
+        fh.write(f"#include \"{export_h}.h\"\n")
+        fc.write(f"#include \"_ht.h\"\n")
+        fh.write("#define HT_getText(name, ...) (HT_TEXT_ ## name)\n")
+        fh.write(f"{export} int HT_initGetText(char *lang);\n")
+        fc.write("#undef HT_getText\n")
+
+        for i in flat_list:
+            fc.write(f"{export} const char *HT_TEXT_{i} = {flat_list[i][0]};\n")
+            fh.write(f"{export} const char *HT_TEXT_{i};\n")
+
+        fc.write('''\n
+/* define initGetText */
+/* need dlfcn or dlfcn-win32 */
+
+#include "dlfcn.h"
+#include "stdlib.h"
+static void *handle = NULL;
+
+static void destructExit(void) {
+    dlclose(handle);
+}
+
+int HT_initGetText(char *lang) {
+    if (lang == NULL || handle != NULL)
+        return 2;
+
+    handle = dlopen(lang, RTLD_NOW);
+    if (handle == NULL)
+        return 1;
+    atexit(destructExit);
+    char **tmp;\n\n''')
+
+        for i in flat_list:
+            fc.write(f'''    tmp = dlsym(handle, "HT__TEXT_{i}");\n''')
+            fc.write(f'''    if (tmp != NULL) HT_TEXT_{i} = *tmp;\n\n''')
+
+        fc.write('    return 0;\n}\n')
+
+for i in flat_list:
+    print(f"TEXT: {i}")
+
+translation_list: FileList = getFileFromPath([translation], '.py')
+for t in translation_list:
+    name = os.path.splitext(os.path.split(t)[-1])[0]
+    if name == 'base':
+        continue
+
+    print(f"tr: {name}")
+    with open(t, "r", encoding="utf-8") as f:
+        code = f.read()
+        var = {}
+        for i in flat_list:
+            var[i] = flat_list[i][0][1:-2]  # [1:-2] 去除引号
+        exec(code, var, var)
+        with open(os.path.join(translation_output, f"{name}.c"), "w", encoding="utf-8") as fc:
+            fc.write(f"#include \"{export_h}.h\"\n")
+            for i in flat_list:  # 根据 flat_list 生成变量
+                res = var[i].replace('\n', '\\n').replace('\r', '')
+                fc.write(f"{export} const char *const HT__TEXT_{i} = \"{res}\";\n")

+ 23 - 0
lang/tr/base.py

@@ -0,0 +1,23 @@
+HELP_INFO = r'''[option]
+   help
+       -v Show    version info
+       -h Show    help page
+   run
+       The first parameter is the running file
+       The following parameters are command line parameters
+   cl
+       -e/--eval    Run argument as code
+       -f/--file    Run file (.aun or .aub)
+       -s/--source    Run source file
+       -b/--byte    Run byte-code file
+       --no-aub    Do not save byte-code after run source file
+       --no-cl    Do not use command line
+       The free parameter before -- will be interpreted as the -f parameter
+       The free parameter after -- will be interpreted as the command line parameters
+   build
+       -o File output path
+       -p File output dir
+       -f Force build
+
+[Github page]
+   <https://github.com/aFun-org/aFunlang>'''

+ 23 - 0
lang/tr/zh_cn.py

@@ -0,0 +1,23 @@
+HELP_INFO = r'''[参数]
+   help
+       -v 显示版本信息
+       -h 显示帮助信息
+   run
+       第一个参数为运行文件
+       其后参数为命令行参数
+   cl
+       -e/--eval    运行指定代码(来自参数)
+       -f/--file    运行文件 (.aun or .aub)
+       -s/--source  运行源代码
+       -b/--byte    运行字节码
+       --no-aub     不保存字节码
+       --no-cl      不进入命令行模式
+       -- 前的自由参数被解析为-f参数
+       -- 后的自由参数被解析为命令行参数
+   build
+       -o 文件输出位置(完整目录, 仅支持单个文件编译)
+       -p 文件输出位置(文件夹, 支持多个文件编译)
+       -f 强制编译
+
+[Github page]
+   <https://github.com/aFun-org/aFunlang>'''

+ 0 - 5
src/CMakeLists.txt

@@ -15,11 +15,6 @@ file(GLOB include_h
      LIST_DIRECTORIES FALSE
      "${PROJECT_SOURCE_DIR}/include/*.h")
 
-file(READ ${CMAKE_CURRENT_LIST_DIR}/help-info _help-info)
-string(REPLACE "\n" "\\n" help-info "${_help-info}")
-configure_file(${CMAKE_CURRENT_LIST_DIR}/help_info.c.in ${CMAKE_BINARY_DIR}/help_info.c @ONLY)
-list(APPEND source "${CMAKE_BINARY_DIR}/help_info.c")
-
 option(AFUN_BUILD_ALL "Build all types of aFun" ON)
 set(_build_all ${AFUN_BUILD_ALL})
 

+ 57 - 8
src/core/core_init.c

@@ -10,23 +10,72 @@
 static Logger aFunCoreLogger_;
 Logger *aFunCoreLogger = &aFunCoreLogger_;
 
-bool aFunCoreInit(char *log_dir, LogFactoryPrintConsole print_console, bool fe, bool se, jmp_buf *buf, LogLevel level) {
+char *log_path = NULL;
+char *lang_path = NULL;
+char *varlib_path = NULL;
+
+static void destructCoreExit(void) {
+    free(log_path);
+    free(lang_path);
+    free(varlib_path);
+}
+
+bool aFunCoreInit(aFunCoreInitInfo *info) {
+    if (info == NULL) {
+        static aFunCoreInitInfo info_default = {.base_dir="",
+                                                .pc=log_pc_all,
+                                                .fe=true,
+                                                .se=true,
+                                                .buf=NULL,
+                                                .level=log_info};
+        info = &info_default;
+    }
+
     getEndian();
     if (setlocale(LC_ALL, "") == NULL)
         return false;
-    if (log_dir == NULL)
+    if (info->base_dir == NULL)
         return false;
-    char *log = strJoin(log_dir, "aFunlang", false, false);
-    bool re = initLogSystem(log, print_console);
+
+    log_path = strJoin(info->base_dir, SEP aFunLogDir SEP, false, false);
+    lang_path = strJoin(info->base_dir, SEP aFunLangDir SEP, false, false);
+    varlib_path = strJoin(info->base_dir, SEP aFunVarLibDir SEP, false, false);
+    atexit(destructCoreExit);
+
+    char *log = strJoin(log_path, "aFunlang", false, false);
+    bool re = initLogSystem(log, info->pc);
     free(log);
     if (re == 0)
         return false;
 
-    initLogger(aFunCoreLogger, "aFunlang-core", level);
-    aFunCoreLogger->process_send_error = fe;
-    aFunCoreLogger->process_fatal_error = se;
-    aFunCoreLogger->buf = buf;
+    initLogger(aFunCoreLogger, "aFunlang-core", info->level);
+    aFunCoreLogger->process_send_error = info->fe;
+    aFunCoreLogger->process_fatal_error = info->se;
+    aFunCoreLogger->buf = info->buf;
 
     writeInfoLog(aFunCoreLogger, log_default, "aFunCore init success");
+    writeInfoLog(aFunCoreLogger, log_default, "aFunCore log path: %s", log_path);
+    writeInfoLog(aFunCoreLogger, log_default, "aFunCore var/lib path: %s", varlib_path);
+    writeInfoLog(aFunCoreLogger, log_default, "aFunCore lang path: %s", lang_path);
+
+    char LANG[100] = {0};
+    char *LANG_path = strJoin(lang_path, "LANG", false, false);
+    FILE *LANG_file = fopen(LANG_path, "r");
+    writeDebugLog(aFunCoreLogger, log_default, "LANG_path = %s", LANG_path);
+    if (LANG_file != NULL) {
+        fgets(LANG, 100, LANG_file);
+        if (LANG[strlen(LANG) - 1] == '\n')
+            LANG[strlen(LANG) - 1] = NUL;  // 去除`\n`
+        writeDebugLog(aFunCoreLogger, log_default, "LANG = %s", LANG);
+
+        char *LANG_lib = strJoin(lang_path, LANG, false, false);
+        if (HT_initGetText(LANG_lib) == 0)
+            writeInfoLog(aFunCoreLogger, log_default, "aFunCore lang init: %s", LANG_lib);
+        free(LANG_lib);
+        fclose(LANG_file);
+    } else
+        HT_initGetText(NULL);
+    free(LANG_path);
+
     return true;
 }

+ 0 - 18
src/help-info

@@ -1,18 +0,0 @@
-[option]
-    help
-        -v Show    version info
-        -h Show    help page
-    run
-        -e/--eval    Run argument as code
-        -f/--file    Run file (.aun or .aub)
-        -s/--source    Run source file
-        -b/--byte    Run byte-code file
-        --no-aub    Do not save byte-code after run source file
-        --no-cl    Do not use command line
-    build
-        -o File output path
-        -p File output dir
-        -f Force build
-
-[Github page]
-    <https://github.com/aFun-org/aFunlang>

+ 0 - 1
src/help_info.c.in

@@ -1 +0,0 @@
-const char *const help_info = "@help-info@";

+ 8 - 8
src/main.c

@@ -41,16 +41,12 @@ static int mainRun(ff_FFlags *ff);
 static int mainCL(ff_FFlags *ff);
 static int mainBuild(ff_FFlags *ff);
 
-extern const char *const help_info;
-
 char *base_path = NULL;
-char *log_path = NULL;
 static Logger aFunlangLogger_;
 Logger *aFunlangLogger = &aFunlangLogger_;
 
 void freeBaseName(void) {
     free(base_path);
-    free(log_path);
 }
 
 int main(int argc, char **argv) {
@@ -58,13 +54,17 @@ int main(int argc, char **argv) {
     base_path = getExedir(*argv, 1);
     if (base_path == NULL)
         goto INIT_ERROR;
-    log_path = strJoin(base_path, SEP aFunLogDir SEP, false, false);
     atexit(freeBaseName);
 
     if (setjmp(main_buf) == 1)
         return EXIT_FAILURE;
 
-    if (!aFunInit(log_path, log_pc_all, &main_buf, log_debug)) {
+    aFunInitInfo info = {.base_dir=base_path,
+                         .level=log_debug,
+                         .buf=&main_buf,
+                         .pc=log_pc_all};
+
+    if (!aFunInit(&info)) {
 INIT_ERROR:
         printf_stderr(0, "aFunlang init error.");
         return EXIT_FAILURE;
@@ -99,7 +99,7 @@ INIT_ERROR:
 static void printVersion(void) {
     printf_stdout(0, "aFunlang at %s\n", name);
     printf_stdout(0, "version: " aFunVersion "\n");
-    printf_stdout(strlen(aFunDescription), aFunDescription "\n");
+    fputs_stdout(aFunDescription "\n");
 }
 
 static void printWelcomeInfo(void) {
@@ -110,7 +110,7 @@ static void printWelcomeInfo(void) {
 
 static void printHelp(void) {
     printf_stdout(0, "aFunlang Usage:\n");
-    printf_stdout(strlen(help_info), "%s\n", help_info);
+    printf_stdout(strlen(HT_getText(HELP_INFO, "<base-tr>")), "%s\n", HT_getText(HELP_INFO, ""));
 }
 
 /*

+ 17 - 2
src/runtime/aFunlang.c

@@ -5,11 +5,26 @@
 static int runCode_(FilePath name, af_Parser *parser, int mode, FilePath save_path, af_Environment *env);
 static bool aFunInit_mark = false;
 
-bool aFunInit(char *log_dir, LogFactoryPrintConsole print_console, jmp_buf *buf, LogLevel level) {
+bool aFunInit(aFunInitInfo *info) {
     if (aFunInit_mark)
         return false;
 
-    aFunInit_mark = aFunCoreInit(log_dir, print_console, true, true, buf, level);
+    if (info == NULL) {
+        static aFunInitInfo info_default = {.base_dir="",
+                                            .pc=log_pc_all,
+                                            .buf=NULL,
+                                            .level=log_info};
+        info = &info_default;
+    }
+
+    aFunCoreInitInfo core_info = {.base_dir=info->base_dir,
+                                  .fe=true,
+                                  .se=true,
+                                  .pc=info->pc,
+                                  .buf=info->buf,
+                                  .level=info->level};
+
+    aFunInit_mark = aFunCoreInit(&core_info);
     if (aFunInit_mark)
         writeInfoLog(aFunCoreLogger, log_default, "aFun-runtime Init success");
     return aFunInit_mark;

+ 1 - 1
src/tool/CMakeLists.txt

@@ -34,7 +34,7 @@ foreach(tgt tool-shared tool-static)
     target_include_directories(${tgt} PUBLIC
                                $<BUILD_INTERFACE:${build_include_tool}>
                                $<INSTALL_INTERFACE:${install_include_tool}>)
-    target_link_libraries(${tgt} PUBLIC ${dlfcn_lib} ${pcre2_lib} ${fflags_lib})
+    target_link_libraries(${tgt} PUBLIC ${hgt-lib} ${dlfcn_lib} ${pcre2_lib} ${fflags_lib})
     set_target_properties(${tgt} PROPERTIES
                           PUBLIC_HEADER "${public_h_build}")
     if(_build_mem)

+ 5 - 5
src/tool/log.c

@@ -167,7 +167,7 @@ static int writeLog_(Logger *logger, LogLoggerPrintConsole pc, LogLevel level, c
     char *ti = getTime(&t, "%Y-%m-%d %H:%M:%S");
 
 #define FORMAT "%s/[%s] %ld %ld {%s %ld} (%s:%d at %s) : '%s'\n"
-#define FORMAT_SHORT "%s[%s] (%s:%d at %s) : %s\n"
+#define FORMAT_SHORT "%s(%s:%d) : %s\n"
     long tid = gettid();
 
     char tmp[2048] = {0};
@@ -189,22 +189,22 @@ static int writeLog_(Logger *logger, LogLoggerPrintConsole pc, LogLevel level, c
         switch (log_factory.print_console) {
             case log_pc_all:
                 if (level < log_warning) {
-                    printf_stdout(STD_BUF_SIZE, FORMAT_SHORT, LogLevelNameLong[level], logger->id, file, line, func, tmp);
+                    printf_stdout(STD_BUF_SIZE, FORMAT_SHORT, LogLevelNameLong[level], file, line, tmp);
                     fflush(stdout);
                 } else if (log_factory.print_console) {
-                    printf_stderr(STD_BUF_SIZE, FORMAT_SHORT, LogLevelNameLong[level], logger->id, file, line, func, tmp);
+                    printf_stderr(STD_BUF_SIZE, FORMAT_SHORT, LogLevelNameLong[level], file, line, tmp);
                     fflush(stderr);
                 }
                 break;
             case log_pc_w:
                 if (level >= log_warning) { // warning的内容一定会被打印
-                    printf_stderr(STD_BUF_SIZE, FORMAT_SHORT, LogLevelNameLong[level], logger->id, file, line, func, tmp);
+                    printf_stderr(STD_BUF_SIZE, FORMAT_SHORT, LogLevelNameLong[level], file, line, tmp);
                     fflush(stderr);
                 }
                 break;
             case log_pc_e:
                 if (level >= log_error) {  // warning的内容一定会被打印
-                    printf_stderr(STD_BUF_SIZE, FORMAT_SHORT, LogLevelNameLong[level], logger->id, file, line, func, tmp);
+                    printf_stderr(STD_BUF_SIZE, FORMAT_SHORT, LogLevelNameLong[level], file, line, tmp);
                     fflush(stderr);
                 }
                 break;

+ 1 - 1
test/src/byte_code.c

@@ -24,7 +24,7 @@ int main() {
 
     af_Code *get = NULL;
     if((re = readByteCode(&get, "test.aub")) != 1) {
-        fprintf(stderr, "Read test.aub error[%d] %s.\n", re);
+        fprintf(stderr, "Read test.aub error[%d].\n", re);
         return EXIT_FAILURE;
     }
 

+ 1 - 1
test/src/env_init.c

@@ -2,7 +2,7 @@
 #include "aFun.h"
 
 int main() {
-    aFunCoreInit("env_init-", log_pc_all, false, false, NULL, log_debug);
+    aFunCoreInit(NULL);
 
     af_Environment *env = makeEnvironment(grt_always);
     enableEnvironment(env);

+ 1 - 1
test/src/run_code.c

@@ -370,7 +370,7 @@ bool infixFunc(char *id, af_Object *obj) {
 }
 
 int main(int argc, char **argv) {
-    bool re = aFunInit("", log_pc_all, NULL, log_debug);
+    bool re = aFunInit(NULL);
 
     if (!re) {
         printf("re = %d\n", re);