update i18n support load from yaml files, add trans search tool maixtool i18n command

This commit is contained in:
Neucrack
2024-07-12 22:20:25 +08:00
parent efb2135d03
commit 1b64a6cd0f
15 changed files with 463 additions and 104 deletions

View File

@@ -29,7 +29,7 @@ endif()
###### Add required/dependent components ######
list(APPEND ADD_REQUIREMENTS pthread stdc++fs ini)
list(APPEND ADD_REQUIREMENTS pthread stdc++fs ini yaml)
###############################################
###### Add link search path for requirements/libs ######

View File

@@ -168,10 +168,10 @@ namespace maix::fs
/**
* Get file extension
* @param path path to get extension
* @return extension if success, empty string if failed
* @return prefix_path and extension list if success, empty string if failed
* @maixpy maix.fs.splitext
*/
std::string splitext(const std::string &path);
std::vector<std::string> splitext(const std::string &path);
/**
* List files in directory

View File

@@ -10,6 +10,8 @@
#include <map>
#include <string>
#include <vector>
#include "maix_err.hpp"
#include "maix_log.hpp"
using namespace std;
@@ -18,43 +20,58 @@ namespace maix::i18n
/**
* i18n locales list
* @maixpy maix.i18n.locales
*/
*/
static std::vector<std::string> locales = {
"en",
"zh",
"zh-tw",
"ja"
};
"ja"};
/**
* i18n language names list
* @maixpy maix.i18n.names
*/
*/
const static std::vector<std::string> names = {
"English",
"简体中文",
"繁體中文",
"日本語"
};
"日本語"};
/**
* Get system config of locale.
* @return language locale, e.g. en, zh, zh_CN, zh_TW, etc.
* @maixpy maix.i18n.get_locale
*/
*/
string get_locale();
/**
* Get system config of language name.
* @return language name, e.g. English, 简体中文, 繁體中文, etc.
* @maixpy maix.i18n.get_language_name
*/
*/
string get_language_name();
/**
* Load translations from yaml files.
* @param locales_dir translation yaml files directory.
* @return A dict contains all translations, e.g. {"zh":{"hello": "你好"}, "en":{"hello": "hello"}}, you should delete it after use in C++.
* @maixpy maix.i18n.load_trans_yaml
*/
const std::map<string, std::map<string, string>> *load_trans_yaml(const std::string &locales_dir);
/**
* Load translations from yaml files.
* @param locales_dir translation yaml files directory.
* @param dict dict to store key values. A dict contains all translations, e.g. {"zh":{"hello": "你好"}, "en":{"hello": "hello"}}, you should delete it after use in C++.
* @return err::ERR
* @maixcdk maix.i18n.load_trans_yaml
*/
err::Err load_trans_yaml(const std::string &locales_dir, std::map<string, std::map<string, string>> &dict);
/**
* Translate helper class.
* @maixpy maix.i18n.Trans
*/
*/
class Trans
{
public:
@@ -65,27 +82,81 @@ namespace maix::i18n
* @param locales_dict locales dict, e.g. {"zh": {"Confirm": "确认", "OK": "好的"}, "en": {"Confirm": "Confirm", "OK": "OK"}}
* @maixpy maix.i18n.Trans.__init__
* @maixcdk maix.i18n.Trans.Trans
*/
Trans(const std::map<string, const std::map<string, string>> &locales_dict)
:locales_dict(locales_dict)
*/
Trans(const std::map<string, const std::map<string, string>> &locales_dict = std::map<string, const std::map<string, string>>())
: locales_dict_const(locales_dict)
{
this->locale = ""; // DO NOT load from file system here will coredump
}
/**
* Load translation from yaml files generated by `maixtool i18n` command.
* @param locales_dir the translation files directory.
* @return err.Err type, no error will return err.Err.ERR_NONE.
* @maixpy maix.i18n.Trans.load
*/
err::Err load(const std::string &locales_dir)
{
return load_trans_yaml(locales_dir, locales_dict);
}
/**
* Update translation dict.
* @param dict the new translation dict.
* @return err.Err type, no error will return err.Err.ERR_NONE.
* @maixpy maix.i18n.Trans.update_dict
*/
err::Err update_dict(const std::map<std::string, const std::map<std::string, std::string>> &dict)
{
try
{
for (const auto &file_pair : dict)
{
const std::string &filename = file_pair.first;
for (const auto &kv_pair : file_pair.second)
{
locales_dict[filename][kv_pair.first] = kv_pair.second;
}
}
return err::Err();
}
catch (const std::exception &e)
{
log::error("copy dict value failed");
return err::Err::ERR_ARGS;
}
}
/**
* Translate string by key.
* @param key string key, e.g. "Confirm"
* @param locale locale name, if not assign, use default locale set by system settings or set_locale function.
* @return translated string, if find translation, return it, or return key, e.g. "确认", "Confirm", etc.
* @maixpy maix.i18n.Trans.tr
*/
*/
string tr(const string &key, const string locale = "")
{
if(this->locale.empty())
if (this->locale.empty())
this->locale = i18n::get_locale();
// if locale not in locales_dict, return key
const std::map<string, const std::map<string, string>>::const_iterator iter = locales_dict.find(locale.empty() ? this->locale : locale);
if (iter == locales_dict.end())
if (!locales_dict.empty())
{
const std::map<string, std::map<string, string>>::const_iterator iter = locales_dict.find(locale.empty() ? this->locale : locale);
if (iter == locales_dict.end())
{
return key;
}
// get key value from locales_dict[locale][key], default value is key
const std::map<string, string> dict = iter->second;
const std::map<string, string>::const_iterator iter2 = dict.find(key);
if (iter2 == dict.end())
{
return key;
}
return iter2->second;
}
const std::map<string, const std::map<string, string>>::const_iterator iter = locales_dict_const.find(locale.empty() ? this->locale : locale);
if (iter == locales_dict_const.end())
{
return key;
}
@@ -103,7 +174,7 @@ namespace maix::i18n
* Set locale temporarily, will not affect system settings.
* @param locale locale name, e.g. "zh", "en", etc. @see maix.i18n.locales
* @maixpy maix.i18n.Trans.set_locale
*/
*/
void set_locale(const string &locale)
{
this->locale = locale;
@@ -113,16 +184,17 @@ namespace maix::i18n
* Get current locale.
* @return locale name, e.g. "zh", "en", etc. @see maix.i18n.locales
* @maixpy maix.i18n.Trans.get_locale
*/
*/
string get_locale()
{
if(this->locale.empty())
if (this->locale.empty())
this->locale = i18n::get_locale();
return this->locale;
}
private:
std::map<string, const std::map<string, string>> locales_dict; // copy dict to here to avoid memory problem, e.g. For python
std::map<string, std::map<string, string>> locales_dict; // copy dict to here to avoid memory problem, e.g. For python
std::map<string, const std::map<string, string>> locales_dict_const;
string locale;
};
}
}

View File

@@ -48,27 +48,35 @@ namespace maix::fs
err::Err symlink(const std::string &src, const std::string &link, bool force)
{
// 检查源文件是否存在
if (!fs::exists(src)) {
if (!fs::exists(src))
{
return err::Err::ERR_NOT_FOUND;
}
// 删除已存在的软链接
if (fs::exists(link)) {
if(!force)
if (fs::exists(link))
{
if (!force)
{
return err::Err::ERR_ALREAY_EXIST;
}
try {
try
{
fs::remove(link);
} catch(...) {
}
catch (...)
{
return err::Err::ERR_IO;
}
}
// 创建软链接
try {
try
{
fs_sys::create_symlink(src, link);
} catch(...) {
}
catch (...)
{
return err::Err::ERR_IO;
}
return err::Err::ERR_NONE;
@@ -84,7 +92,7 @@ namespace maix::fs
{
// create a directory use fs_sys::create_directories
// if exist_ok is true, also return true if directory already exists
if(!exist_ok && fs_sys::exists(path))
if (!exist_ok && fs_sys::exists(path))
{
return err::ERR_ALREAY_EXIST;
}
@@ -103,7 +111,7 @@ namespace maix::fs
err::Err rmdir(const std::string &path, bool recursive)
{
// remove a directory use fs_sys::remove_all
if(!fs_sys::exists(path))
if (!fs_sys::exists(path))
{
return err::ERR_NOT_FOUND;
}
@@ -122,7 +130,7 @@ namespace maix::fs
err::Err remove(const std::string &path)
{
// remove a file use fs_sys::remove
if(!fs_sys::exists(path))
if (!fs_sys::exists(path))
{
return err::ERR_NOT_FOUND;
}
@@ -133,7 +141,7 @@ namespace maix::fs
err::Err rename(const std::string &src, const std::string &dst)
{
// rename a file or directory use fs_sys::rename
if(!fs_sys::exists(src))
if (!fs_sys::exists(src))
{
return err::ERR_NOT_FOUND;
}
@@ -149,7 +157,7 @@ namespace maix::fs
int getsize(const std::string &path)
{
// get file size use fs_sys::file_size
if(!fs_sys::exists(path))
if (!fs_sys::exists(path))
{
return -err::ERR_NOT_FOUND;
}
@@ -160,7 +168,7 @@ namespace maix::fs
{
fs_sys::path p(path);
std::string ret = p.parent_path().string();
if(ret.empty())
if (ret.empty())
{
ret = ".";
}
@@ -188,23 +196,38 @@ namespace maix::fs
return fs_sys::canonical(path).string();
}
std::string splitext(const std::string &path)
std::vector<std::string> splitext(const std::string &path)
{
fs_sys::path p(path);
return p.extension().string();
std::vector<std::string> result;
// 获取文件的后缀名
std::string extension = p.extension().string();
// 获取文件的前缀
std::string stem = p.stem().string();
std::string parent_path = p.parent_path().string();
// 拼接前缀路径和文件名
std::string prefix = parent_path.empty() ? stem : parent_path + fs_sys::path::preferred_separator + stem;
result.push_back(prefix);
result.push_back(extension);
return result;
}
std::vector<std::string> *listdir(const std::string &path, bool recursive, bool full_path)
{
// list directory use fs_sys::directory_iterator
if(!fs_sys::exists(path))
if (!fs_sys::exists(path))
{
return nullptr;
}
std::vector<std::string> *list = new std::vector<std::string>();
if (recursive)
{
if(full_path)
if (full_path)
for (auto &p : fs_sys::recursive_directory_iterator(path))
{
list->push_back(p.path().string());
@@ -217,7 +240,7 @@ namespace maix::fs
}
else
{
if(full_path)
if (full_path)
for (auto &p : fs_sys::directory_iterator(path))
{
list->push_back(p.path().string());
@@ -236,7 +259,7 @@ namespace maix::fs
err::Err error = err::ERR_NONE;
fs::File *file = new fs::File();
error = file->open(path, mode);
if(error != err::ERR_NONE)
if (error != err::ERR_NONE)
{
log::error("open file %s failed, error code: %d\n", path.c_str(), error);
delete file;
@@ -250,16 +273,15 @@ namespace maix::fs
return fs_sys::temp_directory_path().string();
}
err::Err File::open(const std::string &path, const std::string &mode)
{
// open file use std::fopen
if(_fp != nullptr)
if (_fp != nullptr)
{
return err::ERR_NOT_READY;
}
_fp = std::fopen(path.c_str(), mode.c_str());
if(_fp == nullptr)
if (_fp == nullptr)
{
log::error("open file %s failed\n", path.c_str());
return err::ERR_ARGS;
@@ -270,9 +292,9 @@ namespace maix::fs
void File::close()
{
// close file use std::fclose
if(_fp != nullptr)
if (_fp != nullptr)
{
std::fclose((FILE*)_fp);
std::fclose((FILE *)_fp);
_fp = nullptr;
}
}
@@ -280,24 +302,24 @@ namespace maix::fs
int File::read(void *buf, int size)
{
// read data from file use std::fread
if(_fp == nullptr)
if (_fp == nullptr)
{
return -err::ERR_NOT_READY;
}
return std::fread(buf, 1, size, (FILE*)_fp);
return std::fread(buf, 1, size, (FILE *)_fp);
}
std::vector<uint8_t> *File::read(int size)
{
// read data from file use std::fread
if(_fp == nullptr)
if (_fp == nullptr)
{
log::error("file not opened\n");
return nullptr;
}
std::vector<uint8_t> *buf = new std::vector<uint8_t>(size);
int read_size = std::fread(buf->data(), 1, size, (FILE*)_fp);
if(read_size < 0)
int read_size = std::fread(buf->data(), 1, size, (FILE *)_fp);
if (read_size < 0)
{
delete buf;
return nullptr;
@@ -309,12 +331,12 @@ namespace maix::fs
int File::readline(std::string &line)
{
// read line from file use std::fgets
if(_fp == nullptr)
if (_fp == nullptr)
{
return -err::ERR_NOT_OPEN;
}
char buf[1024] = {0};
if(std::fgets(buf, 1024, (FILE*)_fp) == nullptr)
if (std::fgets(buf, 1024, (FILE *)_fp) == nullptr)
{
return 0;
}
@@ -325,12 +347,12 @@ namespace maix::fs
std::string *File::readline()
{
// read line from file use std::fgets
if(_fp == nullptr)
if (_fp == nullptr)
{
throw err::Exception(err::ERR_NOT_OPEN, "file not opened");
}
char buf[1024] = {0};
if(std::fgets(buf, 1024, (FILE*)_fp) == nullptr)
if (std::fgets(buf, 1024, (FILE *)_fp) == nullptr)
{
return new std::string();
}
@@ -339,65 +361,64 @@ namespace maix::fs
}
/**
* End of file or not
* @return 0 if not reach end of file, else eof.
* @maixpy maix.fs.File.eof
*/
int File::eof()
{
return std::feof((FILE*)_fp);
}
* End of file or not
* @return 0 if not reach end of file, else eof.
* @maixpy maix.fs.File.eof
*/
int File::eof()
{
return std::feof((FILE *)_fp);
}
int File::write(const void *buf, int size)
{
// write data to file use std::fwrite
if(_fp == nullptr)
if (_fp == nullptr)
{
return -err::ERR_NOT_READY;
}
return std::fwrite(buf, 1, size, (FILE*)_fp);
return std::fwrite(buf, 1, size, (FILE *)_fp);
}
int File::write(const std::vector<uint8_t> &buf)
{
// write data to file use std::fwrite
if(_fp == nullptr)
if (_fp == nullptr)
{
return -err::ERR_NOT_READY;
}
return std::fwrite(buf.data(), 1, buf.size(), (FILE*)_fp);
return std::fwrite(buf.data(), 1, buf.size(), (FILE *)_fp);
}
int File::seek(int offset, int whence)
{
// seek file position use std::fseek
if(_fp == nullptr)
if (_fp == nullptr)
{
return -err::ERR_NOT_READY;
}
return std::fseek((FILE*)_fp, offset, whence);
return std::fseek((FILE *)_fp, offset, whence);
}
int File::tell()
{
// get file position use std::ftell
if(_fp == nullptr)
if (_fp == nullptr)
{
return -err::ERR_NOT_READY;
}
return std::ftell((FILE*)_fp);
return std::ftell((FILE *)_fp);
}
err::Err File::flush()
{
// flush file use std::fflush
if(_fp == nullptr)
if (_fp == nullptr)
{
return err::ERR_NOT_READY;
}
std::fflush((FILE*)_fp);
std::fflush((FILE *)_fp);
return err::ERR_NONE;
}
} // namespace maix::fs

View File

@@ -7,6 +7,8 @@
#include "maix_i18n.hpp"
#include "maix_app.hpp"
#include <yaml-cpp/yaml.h>
#include "maix_fs.hpp"
namespace maix::i18n
{
@@ -33,4 +35,69 @@ namespace maix::i18n
return "English";
}
const std::map<std::string, std::map<std::string, std::string>> *load_trans_yaml(const std::string &locales_dir)
{
auto *translations = new std::map<std::string, std::map<std::string, std::string>>();
// 遍历目录中的所有文件
std::vector<std::string> *files = fs::listdir(locales_dir, true, true);
for (std::string file : *files)
{
std::string filename = fs::basename(file);
std::vector<std::string> splitname = fs::splitext(file);
if (splitname[1] == ".yaml")
{
YAML::Node node = YAML::LoadFile(file);
std::map<std::string, std::string> content;
// 遍历 YAML 文件中的所有键值对
for (YAML::const_iterator it = node.begin(); it != node.end(); ++it)
{
content[it->first.as<std::string>()] = it->second.as<std::string>();
}
(*translations)[splitname[0]] = content;
}
}
delete files;
return translations;
}
err::Err load_trans_yaml(const std::string &locales_dir, std::map<string, std::map<string, string>> &dict)
{
if(!fs::exists(locales_dir))
{
log::error("dir [ %s ] not found", locales_dir.c_str());
return err::ERR_ARGS;
}
// 遍历目录中的所有文件
std::vector<std::string> *files = fs::listdir(locales_dir, true, true);
if(!files)
{
log::error("no trans yaml files");
return err::ERR_ARGS;
}
for (std::string file : *files)
{
std::string filename = fs::basename(file);
std::vector<std::string> splitname = fs::splitext(filename);
if (splitname[1] == ".yaml")
{
YAML::Node node = YAML::LoadFile(file);
std::map<std::string, std::string> content;
// 遍历 YAML 文件中的所有键值对
for (YAML::const_iterator it = node.begin(); it != node.end(); ++it)
{
content[it->first.as<std::string>()] = it->second.as<std::string>();
}
dict[splitname[0]] = content;
}
}
delete files;
return err::ERR_NONE;
}
} // namespace maix::i18n

12
examples/i18n/README.md Normal file
View File

@@ -0,0 +1,12 @@
MaixCDK i18n demo
=====
## Usage
1. coding like [main.cpp](./main/src/main.cpp), use `tr` function to call the string you want to translate.
2. Execute `maixtool i18n -d . -r` in project dir to generate translation files, they will be in `locales` dir, all are `yaml` file.
3. Translate files.
4. Compile and you will get binary files in dist dir, copy them to board and run.

9
examples/i18n/app.yaml Normal file
View File

@@ -0,0 +1,9 @@
id: i18n_demo
name: I18N demo
version: 1.0.0
#icon: assets/hello.png
author: Sipeed Ltd
desc: i18n demo
files:
- locales

View File

@@ -0,0 +1,2 @@
hello: hello
out: out

View File

@@ -0,0 +1,2 @@
hello: 你好
out: 出去

View File

@@ -4,37 +4,43 @@
using namespace maix;
const std::map<string, string> locale_zh_dict = {
{"out", "输出"},
{"hello", "你好"}
};
// const std::map<string, string> locale_zh_dict = {
// {"out", "输出"},
// {"hello", "你好"}
// };
const std::map<string, string> locale_ja_dict = {
// {"out", "出力"},
{"hello", "こんにちは"}
};
// const std::map<string, string> locale_ja_dict = {
// // {"out", "出力"},
// {"hello", "こんにちは"}
// };
const std::map<string, const std::map<string, string>> locales_dict = {
{"zh", locale_zh_dict},
{"ja", locale_ja_dict}
};
// const std::map<string, const std::map<string, string>> locales_dict = {
// {"zh", locale_zh_dict},
// {"ja", locale_ja_dict}
// };
i18n::Trans trans(locales_dict);
// i18n::Trans trans(locales_dict);
i18n::Trans trans;
int _main(int argc, char* argv[])
{
err::Err e = trans.load("locales");
err::check_raise(e, "load translation yamls failed");
log::info("system locale: %s\n", i18n::get_locale().c_str());
log::info("%s: %s\n", trans.tr("out").c_str(), trans.tr("hello").c_str());
log::info("%s: %s, %s\n", i18n::get_locale().c_str(), trans.tr("out").c_str(), trans.tr("hello").c_str());
trans.set_locale("zh");
log::info("%s: %s\n", trans.tr("out").c_str(), trans.tr("hello").c_str());
log::info("zh: %s, %s\n", trans.tr("out").c_str(), trans.tr("hello").c_str());
trans.set_locale("en");
log::info("%s: %s\n", trans.tr("out").c_str(), trans.tr("hello").c_str());
log::info("en: %s, %s\n", trans.tr("out").c_str(), trans.tr("hello").c_str());
trans.set_locale("ja");
log::info("%s: %s\n", trans.tr("out").c_str(), trans.tr("hello").c_str());
log::info("ja: %s, %s\n", trans.tr("out").c_str(), trans.tr("hello").c_str());
// after coding, you need to execute `maixtool i18n -d . -r` in project dir to generate translation files, and translate them
return 0;
}

View File

@@ -0,0 +1,49 @@
import os
import sys
import shutil
import yaml
def copy_assets(project_path, dist_path):
app_yaml_path = os.path.join(project_path, 'app.yaml')
# 检查 app.yaml 是否存在
if not os.path.exists(app_yaml_path):
print(f"{app_yaml_path} not a app, skip copy files")
return
# 读取 app.yaml 文件
with open(app_yaml_path, 'r') as file:
config = yaml.safe_load(file)
# 检查 assets 键是否存在
if 'files' not in config:
print("No 'files' key found in app.yaml, skip copy files")
return
assets = config.get('files', [])
if isinstance(assets, list):
for item in assets:
src = os.path.join(project_path, item)
dest = os.path.join(dist_path, item)
if os.path.exists(src):
shutil.copytree(src, dest, dirs_exist_ok=True)
print(f"Copied {src} to {dest}")
else:
raise Exception(f"{src} does not exist.")
elif isinstance(assets, dict):
for src_key, dest_value in assets.items():
src = os.path.join(project_path, src_key)
dest = os.path.join(dist_path, dest_value)
if os.path.exists(src):
shutil.copytree(src, dest, dirs_exist_ok=True)
print(f"Copied {src} to {dest}")
else:
raise Exception(f"{src} does not exist.")
else:
print("'files' key must be a list or a dictionary.")
if __name__ == "__main__":
project_path = sys.argv[1]
dist_path = sys.argv[2]
copy_assets(project_path, dist_path)

View File

@@ -25,12 +25,15 @@ if(${BUILD_TYPE} STREQUAL "Release")
endif()
endif()
set(cp_assets_cmd COMMAND python ${SDK_PATH}/tools/cmake/copy_assets.py ${PROJECT_PATH} ${PROJECT_DIST_DIR}/${PROJECT_ID}_${build_type})
add_custom_command(TARGET ${PROJECT_ID} POST_BUILD
${strip_cmd}
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/dl_lib
${cp_command}
${cp_dist_cmd}
${cp_dl_to_dist_cmd}
${cp_assets_cmd}
DEPENDS ${PROJECT_ID}
COMMENT "-- copy dynamic libs to build/dl_lib dir ...")

View File

@@ -0,0 +1,101 @@
import sys, os
import yaml
from collections import OrderedDict
def get_files(scan_dir, recursive, exts=[".c", ".cpp", ".h", ".hpp"]):
files = []
if recursive:
for root, dirs, fs in os.walk(scan_dir):
for f in fs:
if os.path.splitext(f)[-1] in exts:
files.append(os.path.join(root, f))
else:
for name in os.listdir(scan_dir):
if os.path.splitext(name)[1] in exts:
files.append(os.path.join(scan_dir, name))
return files
def get_i18n_strs(file, keywords):
'''
read content and find '_("xxx")', get all xxx and return
'''
strs = []
with open(file, "r") as f:
content = f.read()
for key in keywords:
idx = 0
while True:
flag = False
idx = content.find(f'{key}("', idx)
if idx == -1:
idx = content.find(f"{key}('", idx)
if idx == -1:
break
flag = True
if not (idx == 0 or content[idx - 1] in [".", ",", " ", ">"]): # maybe like str etc.
idx += 1
continue
idx += len(key) + 2
if flag:
end = content.find("')", idx)
else:
end = content.find('")', idx)
if end == -1:
break
strs.append(content[idx:end])
idx = end
return strs
def main(search_dir, keywords, exts, out, recursive, locales):
print("seach dir: ", dir)
print("keywords: ", keywords)
print("extentions:", exts)
print("out dir: ", out)
print("")
files = get_files(search_dir, recursive)
print(f"{len(files)} files found")
keys_list = []
for f in files:
strs = get_i18n_strs(f, keywords)
keys_list.extend(strs)
keys_list = list(set(keys_list))
keys_list.sort()
keys_dict = {}
for i in keys_list:
keys_dict[i] = i
os.makedirs(out, exist_ok=True)
for locale in locales:
print(f"gen locale [ {locale} ]")
path = os.path.join(out, f"{locale}.yaml")
old = {}
if os.path.exists(path):
with open(path, "r", encoding="utf-8") as f:
old = yaml.safe_load(f)
if old is None:
old = {}
# update by old
for k in keys_dict:
if k in old:
keys_dict[k] = old[k]
with open(path, "w", encoding="utf-8") as f:
yaml.dump(keys_dict, f, allow_unicode=True)
if __name__ == "__main__":
import argparse
paser = argparse.ArgumentParser()
paser.add_argument("-k", "--keywords", nargs="+", type=str, default=["_", "tr"], help="translate function keywords to search")
paser.add_argument("-r", action="store_true", help="recursive search dir")
paser.add_argument("-e", "--exts", nargs="+", type=str, default=[".c", ".cpp", ".h", ".hpp", ".py"], help="file extention to search")
paser.add_argument("-l", "--locales", nargs="+", type=str, default=["en", "zh"], help="locals of region, like en zh ja etc.")
paser.add_argument("-o", "--out", type=str, default="locales", help="translation files output directory")
paser.add_argument("-d", "--dir", type=str, help="where to search", required=True)
args = paser.parse_args()
main(args.dir, args.keywords, args.exts, args.out, args.r, args.locales)

View File

@@ -3,6 +3,7 @@ from .version import __version__
import os
from .app_deploy import serve
from .app_release import pack
from .i18n import main as i18n_main
def deploy(port, pkg_path):
if not pkg_path:
@@ -15,15 +16,29 @@ def deploy(port, pkg_path):
serve(pkg_path, port)
def main():
cmds = ["release", "deploy"]
parser = argparse.ArgumentParser(description='maixtool, tools for maix series development')
parser.add_argument('-v', '--version', action='version', version=__version__)
parser.add_argument('-p', "--port", default=8888, type=int, help="deploy server port")
parser.add_argument("--pkg", default="", type=str, help="app package path")
parser.add_argument("command", help="command to run", choices=cmds)
args = parser.parse_args()
if args.command == "release":
pack(os.getcwd())
elif args.command == "deploy":
deploy(args.port, args.pkg)
subparsers = parser.add_subparsers(help='command help', dest="cmd")
# release
parser_release = subparsers.add_parser("release", help="release APP")
# deploy
parser_deploy = subparsers.add_parser("deploy", help="release APP")
parser_deploy.add_argument('-p', "--port", default=8888, type=int, help="deploy server port")
parser_deploy.add_argument("--pkg", default="", type=str, help="app package path")
# i18n
parser_i18n = subparsers.add_parser("i18n", help="release APP")
parser_i18n.add_argument("-k", "--keywords", nargs="+", type=str, default=["_", "tr"], help="translate function keywords to search")
parser_i18n.add_argument("-r", action="store_true", help="recursive search dir")
parser_i18n.add_argument("-e", "--exts", nargs="+", type=str, default=[".c", ".cpp", ".h", ".hpp", ".py"], help="file extention to search")
parser_i18n.add_argument("-l", "--locales", nargs="+", type=str, default=["en", "zh"], help="locals of region, like en zh ja etc.")
parser_i18n.add_argument("-o", "--out", type=str, default="locales", help="translation files output directory")
parser_i18n.add_argument("-d", "--dir", type=str, help="where to search", required=True)
args = parser.parse_args()
if args.cmd == "release":
pack(os.getcwd())
elif args.cmd == "deploy":
deploy(args.port, args.pkg)
elif args.cmd == "i18n":
i18n_main(args.dir, args.keywords, args.exts, args.out, args.r, args.locales)

View File

@@ -1,4 +1,4 @@
__version__ = "1.3.3"
__version__ = "1.3.4"