Skip to content

Commit

Permalink
✨ lang model generate
Browse files Browse the repository at this point in the history
  • Loading branch information
RF-Tar-Railt committed May 19, 2024
1 parent e5f9076 commit e10b140
Show file tree
Hide file tree
Showing 11 changed files with 262 additions and 88 deletions.
17 changes: 9 additions & 8 deletions src/tarina/i18n/.lang.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@
"title": "Lang",
"description": "Scope 'lang' of lang item",
"type": "object",
"additionalProperties": false,
"properties": {
"locale_error": {
"title": "locale_error",
"description": "value of lang item type 'locale_error'",
"type": "string"
},
"scope_error": {
"title": "scope_error",
"description": "value of lang item type 'scope_error'",
Expand All @@ -20,20 +26,15 @@
"description": "value of lang item type 'type_error'",
"type": "string"
},
"name_error": {
"title": "name_error",
"description": "value of lang item type 'name_error'",
"miss_require_scope": {
"title": "miss_require_scope",
"description": "value of lang item type 'miss_require_scope'",
"type": "string"
},
"miss_require_type": {
"title": "miss_require_type",
"description": "value of lang item type 'miss_require_type'",
"type": "string"
},
"miss_require_name": {
"title": "miss_require_name",
"description": "value of lang item type 'miss_require_name'",
"type": "string"
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/tarina/i18n/.template.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
{
"scope": "lang",
"types": [
"locale_error",
"scope_error",
"type_error",
"name_error",
"miss_require_type",
"miss_require_name"
"miss_require_scope",
"miss_require_type"
]
}
]
Expand Down
8 changes: 8 additions & 0 deletions src/tarina/i18n/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from pathlib import Path

from tarina.lang import lang


lang.load(Path(__file__).parent)

from .model import Lang as Lang
10 changes: 5 additions & 5 deletions src/tarina/i18n/en-US.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"$schema": "./.lang.schema.json",
"lang": {
"scope_error": "'{target}' is not a valid language scope",
"type_error": "'{target}' is not a valid type in '{scope}'",
"name_error": "'{target}' is not a valid name in '{scope}:{type}'",
"miss_require_type": "lang file '{scope}' missed require type '{target}'",
"miss_require_name": "'{type}' missed require name {target} in '{scope}'"
"locale_error": "'{target}' is not a valid language locale",
"scope_error": "'{target}' is not a valid scope in '{locale}'",
"type_error": "'{target}' is not a valid type in '{locale}:{scope}'",
"miss_require_scope": "lang file '{locale}' missed require scope '{target}'",
"miss_require_type": "'{scope}' missed require type {target} in lang file '{locale}'"
}
}
14 changes: 14 additions & 0 deletions src/tarina/i18n/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from tarina.lang.model import LangModel, LangItem


class Lang_:
locale_error: LangItem = LangItem("lang", "locale_error")
scope_error: LangItem = LangItem("lang", "scope_error")
type_error: LangItem = LangItem("lang", "type_error")
miss_require_scope: LangItem = LangItem("lang", "miss_require_scope")
miss_require_type: LangItem = LangItem("lang", "miss_require_type")


class Lang(LangModel):
lang = Lang_

10 changes: 5 additions & 5 deletions src/tarina/i18n/zh-CN.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"$schema": "./.lang.schema.json",
"lang": {
"scope_error": "'{target}' 不是合法的语种",
"type_error": "'{target}' 在 '{scope}' 中不是合法的类型",
"name_error": "'{target}' 在 '{scope}:{type}' 不是合法的名称",
"miss_require_type": "语言文件 '{scope}' 缺少需求的类型 '{target}'",
"miss_require_name": "'{scope}' 的 '{type}' 缺少需求的内容 {target}"
"locale_error": "'{target}' 不是合法的语种",
"scope_error": "'{target}' 在 '{locale}' 中不是合法的域名",
"type_error": "'{target}' 在 '{locale}:{scope}' 不是合法的类型",
"miss_require_scope": "语言文件 '{locale}' 缺少需求的域名 '{target}'",
"miss_require_type": "语言文件 '{locale}' 的 '{scope}' 缺少需求的内容 {target}"
}
}
109 changes: 56 additions & 53 deletions src/tarina/lang/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,14 @@ def _get_config(root: Path) -> _LangDict:


def _get_scopes(root: Path) -> list[str]:
return [i.stem for i in root.iterdir() if i.is_file() and not i.name.startswith(".")]
return [i.stem for i in root.iterdir() if i.is_file() and i.suffix == ".json" and not i.name.startswith(".")]


def _get_lang(root: Path, _type: str) -> dict[str, dict[str, str]]:
with (root / f"{_type}.json").open("r", encoding="utf-8") as f:
return json.load(f)
data: dict[str, dict[str, str]] = json.load(f)
data.pop("$schema", None)
return data


def merge(source: dict, target: dict, ignore: list[str] | None = None) -> dict:
Expand All @@ -87,105 +89,106 @@ def merge(source: dict, target: dict, ignore: list[str] | None = None) -> dict:
class _LangConfig:
def __init__(self):
__config = _get_config(root_dir)
self.__scope: str = __config["default"]
self.__locale: str = __config["default"]
self.__frozen: list[str] = __config["frozen"]
self.__require: list[str] = __config["require"]
self.__langs = {t.replace("_", "-"): _get_lang(root_dir, t) for t in _get_scopes(root_dir)}
self.__scopes = set(self.__langs.keys())
self.__locales = set(self.__langs.keys())
self.select_local()

@property
def scopes(self):
return self.__scopes
def locales(self):
return self.__locales

@property
def current(self):
return self.__scope
return self.__locale

def select_local(self):
"""
依据系统语言尝试自动选择语言
"""
if (lc := get_locale()) and lc.replace("_", "-") in self.__langs:
self.__scope = lc.replace("_", "-")
self.__locale = lc.replace("_", "-")
return self

def select(self, item: str) -> Self:
item = item.replace("_", "-")
if item not in self.__langs:
raise ValueError(self.require("lang", "scope_error").format(target=item))
self.__scope = item
def select(self, locale: str) -> Self:
locale = locale.replace("_", "-")
if locale not in self.__langs:
raise ValueError(self.require("lang", "locale_error").format(target=locale))
self.__locale = locale
return self

def save(self, root: Path | None = None):
_root = root or root_dir
config = _get_config(_root)
config["default"] = self.__scope
config["default"] = self.__locale
with (_root / ".config.json").open("w+", encoding="utf-8") as f:
json.dump(config, f, ensure_ascii=False, indent=2)

def load_data(self, scope: str, data: dict[str, dict[str, str]]):
if scope in self.__langs:
self.__langs[scope] = merge(data, self.__langs[scope], self.__frozen)
def load_data(self, locale: str, data: dict[str, dict[str, str]]):
if locale in self.__langs:
self.__langs[locale] = merge(data, self.__langs[locale], self.__frozen)
else:
self.__scopes.add(scope)
self.__langs[scope] = data
self.__locales.add(locale)
self.__langs[locale] = data
for key in self.__require:
parts = key.split(".", 1)
t = parts[0]
n = parts[1] if len(parts) > 1 else None
if t not in self.__langs[scope]:
raise KeyError(self.require("lang", "miss_require_type", scope).format(scope=scope, target=t))
if n and n not in self.__langs[scope][t]:
raise KeyError(self.require("lang", "miss_require_name", scope).format(scope=scope, type=t, target=n))
s = parts[0]
t = parts[1] if len(parts) > 1 else None
if s not in self.__langs[locale]:
raise KeyError(self.require("lang", "miss_require_scope", locale).format(locale=locale, target=s))
if t and t not in self.__langs[locale][s]:
raise KeyError(self.require("lang", "miss_require_type", locale).format(locale=locale, scope=s, target=t))

def load_file(self, filepath: Path):
return self.load_data(filepath.stem, _get_lang(filepath.parent, filepath.stem))

def load_config(self, config: _LangDict):
self.__scope = config.get("default", self.__scope)
self.__locale = config.get("default", self.__locale)
self.__frozen.extend(config.get("frozen", []))
self.__require.extend(config.get("require", []))
self.select_local()

def load(self, root: Path):
def load(self, root: Path) -> Self:
self.load_config(_get_config(root))
for i in root.iterdir():
if not i.is_file() or i.name.startswith("."):
continue
self.load_file(i)
return self

def require(self, _type: str, _name: str, scope: str | None = None) -> str:
scope = scope or self.__scope
if scope not in self.__langs:
raise ValueError(self.__langs[self.__scope]["lang"]["scope_error"].format(target=scope))
if _type in self.__langs[scope]:
_types = self.__langs[scope][_type]
elif _type in self.__langs[self.__scope]:
_types = self.__langs[self.__scope][_type]
elif _type in self.__langs[(default := _get_config(root_dir)["default"])]:
_types = self.__langs[default][_type]
def require(self, scope: str, type: str, locale: str | None = None) -> str:
locale = locale or self.__locale
if locale not in self.__langs:
raise ValueError(self.__langs[self.__locale]["lang"]["locale_error"].format(target=locale))
if scope in self.__langs[locale]:
_types = self.__langs[locale][scope]
elif scope in self.__langs[self.__locale]:
_types = self.__langs[self.__locale][scope]
elif scope in self.__langs[(default := _get_config(root_dir)["default"])]:
_types = self.__langs[default][scope]
else:
raise ValueError(self.__langs[scope]["lang"]["type_error"].format(target=_type, scope=scope))
if _name in _types:
return _types[_name]
elif _name in self.__langs[self.__scope][_type]:
return self.__langs[self.__scope][_type][_name]
elif _name in self.__langs[(default := _get_config(root_dir)["default"])][_type]:
return self.__langs[default][_type][_name]
raise ValueError(self.__langs[locale]["lang"]["scope_error"].format(target=scope, locale=locale))
if type in _types:
return _types[type]
elif type in self.__langs[self.__locale][scope]:
return self.__langs[self.__locale][scope][type]
elif type in self.__langs[(default := _get_config(root_dir)["default"])][scope]:
return self.__langs[default][scope][type]
else:
raise ValueError(self.__langs[scope]["lang"]["name_error"].format(target=_name, scope=scope, type=_type))
raise ValueError(self.__langs[locale]["lang"]["type_error"].format(target=type, locale=locale, scope=scope))

def set(self, _type: str, _name: str, content: str, scope: str | None = None):
scope = scope or self.__scope
if scope not in self.__langs:
raise ValueError(self.__langs[self.__scope]["lang"]["scope_error"].format(target=scope))
if _type in self.__frozen:
raise ValueError(self.__langs[scope]["lang"]["type_error"].format(target=_type, scope=scope))
self.__langs[scope].setdefault(_type, {})[_name] = content
def set(self, scope: str, type: str, content: str, locale: str | None = None):
locale = locale or self.__locale
if locale not in self.__langs:
raise ValueError(self.__langs[self.__locale]["lang"]["locale_error"].format(target=locale))
if scope in self.__frozen:
raise ValueError(self.__langs[locale]["lang"]["scope_error"].format(target=scope, locale=locale))
self.__langs[locale].setdefault(scope, {})[type] = content

def __repr__(self):
return f"<LangConfig: {self.__scope}>"
return f"<LangConfig: {self.__locale}>"


lang: _LangConfig = _LangConfig()
Expand Down
Loading

0 comments on commit e10b140

Please sign in to comment.