Difficult-Rocket/Difficult_Rocket/utils/translate.py

280 lines
9.7 KiB
Python
Raw Normal View History

2021-09-08 23:38:34 +08:00
# -------------------------------
# Difficult Rocket
2023-01-20 14:08:12 +08:00
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
2021-09-08 23:38:34 +08:00
# All rights reserved
# -------------------------------
2021-09-02 22:47:10 +08:00
"""
2022-11-26 21:48:55 +08:00
writen by shenjackyuanjie <3695888@qq.com>
2021-09-02 22:47:10 +08:00
mail: 3695888@qq.com
github: @shenjackyuanjie
gitee: @shenjackyuanjie
"""
2023-04-05 16:00:38 +08:00
import os
2022-07-01 13:59:08 +08:00
import inspect
2023-06-22 01:41:11 +08:00
from pathlib import Path
2023-01-03 16:24:06 +08:00
from dataclasses import dataclass
2023-03-02 13:15:29 +08:00
from typing import Union, Tuple, Any, List, Dict, Hashable, Optional
from Difficult_Rocket import DR_status
2022-04-26 22:05:58 +08:00
from Difficult_Rocket.utils import tools
from Difficult_Rocket.runtime import DR_runtime
2023-12-03 16:54:07 +08:00
from Difficult_Rocket.exception.language import LanguageNotFound, TranslateKeyNotFound
2022-11-26 22:45:30 +08:00
2023-01-03 16:24:06 +08:00
@dataclass
class TranslateConfig:
2023-01-16 18:34:17 +08:00
raise_error: bool = False # 引用错误时抛出错误
2023-02-03 20:31:44 +08:00
crack_normal: bool = True # 出现错误引用后 将引用到的正确内容替换为引用路径
2023-01-16 18:34:17 +08:00
insert_crack: bool = True # 加入引用的错误内容
is_final: bool = False # 是否为最终内容
keep_get: bool = True # 引用错误后是否继续引用
always_copy: bool = False # 是否一直新建 Translate (为 True 会降低性能)
2023-01-31 22:07:18 +08:00
source: Optional[Union["Tr", "Translates"]] = None # 翻译来源 (用于默认翻译)
2023-01-03 16:24:06 +08:00
2023-12-03 16:54:07 +08:00
def set(self, item: str, value: Union[bool, "Tr", "Translates"]) -> "TranslateConfig":
assert (
getattr(self, item, None) is not None
2023-12-03 16:54:07 +08:00
), f"Config {item} is not in TranslateConfig"
assert isinstance(value, bool)
2023-01-03 16:24:06 +08:00
setattr(self, item, value)
return self
2023-12-03 16:54:07 +08:00
def __copy__(self) -> "TranslateConfig":
return TranslateConfig(
raise_error=self.raise_error,
crack_normal=self.crack_normal,
insert_crack=self.insert_crack,
is_final=self.is_final,
keep_get=self.keep_get,
always_copy=self.always_copy,
source=self.source,
)
def copy(self) -> "TranslateConfig":
return self.__copy__()
2023-01-03 16:24:06 +08:00
2023-01-16 18:34:17 +08:00
key_type = Union[str, int, Hashable]
2022-11-26 23:35:49 +08:00
class Translates:
2023-12-03 16:54:07 +08:00
def __init__(
self,
value: Union[Dict[str, Any], list, tuple, str],
config: Optional[TranslateConfig] = None,
get_list: Optional[List[Tuple[bool, str]]] = None,
2023-12-03 16:54:07 +08:00
):
"""一个用于翻译的东西
2022-11-27 13:45:59 +08:00
:param value: 翻译键节点
:param config: 配置
:param get_list: 获取列表
2022-11-27 13:45:59 +08:00
"""
2023-04-06 15:31:21 +08:00
self._value: Union[Dict[str, Any], list, tuple] = value
self._config = config or TranslateConfig()
self._get_list = get_list or []
2023-01-03 16:24:06 +08:00
2023-12-03 16:54:07 +08:00
def set_conf_(
self,
option: Union[str, TranslateConfig],
value: Optional[Union[bool, List[str]]] = None,
2023-12-03 16:54:07 +08:00
) -> "Translates":
2023-01-19 16:34:06 +08:00
assert isinstance(option, (TranslateConfig, str))
2023-01-03 16:24:06 +08:00
if isinstance(option, TranslateConfig):
2023-04-06 15:31:21 +08:00
self._config = option
2023-01-03 16:24:06 +08:00
return self
2023-04-06 15:31:21 +08:00
self._config.set(option, value)
2023-01-16 18:34:17 +08:00
return self
def _raise_no_value(self, e: Exception, item: key_type):
2023-04-06 15:31:21 +08:00
if self._config.raise_error:
2023-12-03 16:54:07 +08:00
raise TranslateKeyNotFound(
self._value, [x[1] for x in self._get_list]
) from None
2023-06-16 23:36:24 +08:00
elif DR_status.report_translate_not_found:
2023-01-16 18:34:17 +08:00
frame = inspect.currentframe()
if frame is not None:
2023-02-03 00:05:32 +08:00
frame = frame.f_back.f_back
2023-12-03 16:54:07 +08:00
code_list = [
self.__getitem__.__code__,
self.__getattr__.__code__,
self.__copy__.__code__,
self.copy.__code__,
Tr.lang.__code__,
Tr.__getitem__.__code__,
Tr.__call__.__code__,
] # 调用堆栈上的不需要的东西
2023-02-03 00:05:32 +08:00
while True:
if frame.f_code not in code_list: # 直到调用堆栈不是不需要的东西
break
frame = frame.f_back # 继续向上寻找
2023-12-03 16:54:07 +08:00
frame = f"call at {frame.f_code.co_filename}:{frame.f_lineno}"
2023-01-16 18:34:17 +08:00
else:
2023-12-03 16:54:07 +08:00
frame = "but No Frame environment"
2023-01-16 18:34:17 +08:00
raise_info = f"{self.name} Cause a error when getting {item} {frame}"
print(raise_info)
2022-11-26 22:45:30 +08:00
2023-12-03 16:54:07 +08:00
def __getitem__(
self, item: Union[key_type, List[key_type], Tuple[key_type]]
2023-12-03 16:54:07 +08:00
) -> "Translates":
2022-11-26 22:45:30 +08:00
try:
2023-01-31 22:07:18 +08:00
if isinstance(item, (str, int, Hashable)):
2023-04-06 15:31:21 +08:00
cache_value = self._value[item]
2023-01-31 22:07:18 +08:00
else:
2023-04-06 15:31:21 +08:00
cache_value = self._value
2023-01-31 22:07:18 +08:00
for a_item in item:
cache_value = cache_value[a_item]
2023-12-03 16:54:07 +08:00
if isinstance(
cache_value,
(
int,
str,
),
2023-12-03 16:54:07 +08:00
):
2023-04-06 15:31:21 +08:00
self._config.is_final = True
self._get_list.append((True, item))
if self._config.always_copy:
2023-12-03 16:54:07 +08:00
return Translates(
value=cache_value, config=self._config, get_list=self._get_list
)
2023-04-06 15:31:21 +08:00
self._value = cache_value
2023-01-16 18:34:17 +08:00
except (KeyError, TypeError, AttributeError) as e:
2023-04-06 15:31:21 +08:00
self._get_list.append((False, item))
2023-01-16 18:34:17 +08:00
self._raise_no_value(e, item)
2023-04-06 15:31:21 +08:00
if not self._config.keep_get:
self._config.is_final = True
2023-02-03 20:31:44 +08:00
return self
2023-01-16 18:34:17 +08:00
2023-04-22 20:52:10 +08:00
def __call__(self, *args, **kwargs) -> str:
2023-04-22 21:30:04 +08:00
return str(self)
2023-01-31 22:07:18 +08:00
2023-01-19 16:34:06 +08:00
def copy(self):
return self.__copy__()
2023-01-16 18:34:17 +08:00
2023-12-03 16:54:07 +08:00
def __copy__(self) -> "Translates":
2023-04-06 15:31:21 +08:00
return Translates(value=self._value, config=self._config, get_list=self._get_list)
2022-11-26 21:48:55 +08:00
2023-01-19 16:34:06 +08:00
def __getattr__(self, item: key_type) -> "Translates":
2023-12-03 16:54:07 +08:00
if (self._config.is_final or any(x[0] for x in self._get_list)) and hasattr(
self._value, item
2023-12-03 16:54:07 +08:00
):
2023-04-06 15:31:21 +08:00
return getattr(self._value, item)
2023-01-05 21:36:47 +08:00
# 实际上我这里完全不需要处理正常需求,因为 __getattribute__ 已经帮我处理过了
2022-11-26 21:48:55 +08:00
return self.__getitem__(item)
def __str__(self):
2023-04-06 15:31:21 +08:00
if not any(not x[0] for x in self._get_list):
2023-06-22 13:34:14 +08:00
return str(self._value)
2023-04-06 15:31:21 +08:00
if self._config.crack_normal:
return f'{".".join(f"{gets[1]}({gets[0]})" for gets in self._get_list)}'
elif self._config.insert_crack:
return f'{self._value}.{".".join(gets[1] for gets in self._get_list if not gets[0])}'
2023-06-22 13:34:14 +08:00
return str(self._value)
2022-11-26 21:48:55 +08:00
2021-09-02 22:47:10 +08:00
2022-07-01 13:59:08 +08:00
class Tr:
"""
2023-01-16 18:34:17 +08:00
我不装了我就抄了tr(实际上没啥关系)
2022-08-16 13:25:44 +08:00
GOOD
2022-07-01 13:59:08 +08:00
"""
2023-01-03 16:24:06 +08:00
2023-12-03 16:54:07 +08:00
def __init__(
self,
language: str = None,
config: Optional[TranslateConfig] = None,
lang_path: Optional[Path] = None,
2023-12-03 16:54:07 +08:00
):
2022-12-08 09:53:22 +08:00
"""
诶嘿我抄的MCDR
:param language: Tr 所使用的的语言
:param config: 配置
2023-06-22 13:50:17 +08:00
:param lang_path: 语言文件夹路径
2022-12-08 09:53:22 +08:00
"""
2023-02-03 00:05:32 +08:00
self.language_name = language if language is not None else DR_runtime.language
2023-12-03 16:54:07 +08:00
self.language_path = lang_path if lang_path is not None else Path("assets/lang")
self.translates: Dict[str, Union[str, Dict]] = tools.load_file(
self.language_path / f"{self.language_name}.toml"
)
self.default_translate: Dict = tools.load_file(
f"{self.language_path}/{DR_status.default_language}.toml"
)
self.default_config = (
config.set("source", self)
if config is not None
else TranslateConfig(source=self)
)
self.translates_cache = Translates(
value=self.translates, config=self.default_config.copy()
)
2023-01-31 22:07:18 +08:00
2023-04-05 16:00:38 +08:00
@property
def _language(self) -> str:
return self.language_name
@_language.setter
def _language(self, value: str):
self.init_translate(value)
def init_translate(self, lang: Optional[str] = None) -> bool:
"""
初始化语言文件
:param lang: 要初始化的语言
:return:
"""
# 首先判定是否存在对应的语言文件
if lang == self.language_name:
return False
2023-12-03 16:54:07 +08:00
if lang == " " or lang == "":
raise LanguageNotFound("Can not be empty")
2023-04-05 16:00:38 +08:00
lang = lang or self.language_name
2023-12-03 16:54:07 +08:00
if not os.path.exists(f"{self.language_path}/{lang}.toml"):
print(
f"lang: {os.path.exists(f'{self.language_path}/{lang}.toml')} language = {lang} {self.language_name=}"
)
2023-04-05 16:00:38 +08:00
raise LanguageNotFound(lang)
2023-12-03 16:54:07 +08:00
self.translates: Dict[str, Union[str, Dict]] = tools.load_file(
f"{self.language_path}/{lang}.toml"
)
self.default_translate: Dict = tools.load_file(
f"{self.language_path}/{DR_runtime.default_language}.toml"
)
self.translates_cache = Translates(
value=self.translates, config=self.default_config.copy()
)
2023-04-05 16:00:38 +08:00
self.language_name = lang
DR_runtime.language = self.language_name
return True
2023-02-06 13:16:35 +08:00
def update_lang(self) -> bool:
2023-02-06 13:16:35 +08:00
if DR_runtime.language != self.language_name:
self.language_name = DR_runtime.language
self.init_translate()
return True
return False
2023-01-31 22:07:18 +08:00
def default(self, items: Union[str, List[str]]) -> Translates:
if isinstance(items, list):
cache_translate = self.default_translate
for item in items:
cache_translate = cache_translate[item]
return cache_translate
else:
return self.default_translate[items]
2023-02-03 20:31:44 +08:00
def lang(self, *items) -> Translates:
2023-02-03 00:05:32 +08:00
cache = self.translates_cache.copy()
for item in items:
cache = cache[item]
return cache
2023-01-03 16:24:06 +08:00
2023-02-03 00:05:32 +08:00
def __getitem__(self, item: Union[str, int]) -> Translates:
return self.translates_cache.copy()[item]
2023-01-05 21:36:47 +08:00
2023-02-03 00:05:32 +08:00
def __call__(self, *args, **kwargs) -> Translates:
return self.translates_cache.copy()
2022-07-04 10:36:19 +08:00
2022-07-01 13:59:08 +08:00
tr = Tr()