Archived
1
0
Fork 0
This repository has been archived on 2024-04-26. You can view files and clone it, but cannot push or open issues or pull requests.
akari-bot/core/utils/i18n.py
2023-04-30 11:30:59 +08:00

108 lines
3.8 KiB
Python

import os
from collections.abc import MutableMapping
from string import Template
from typing import TypedDict
import ujson as json
from config import Config
from .text import remove_suffix
# Load all locale files into memory
# We might change this behavior in the future and read them on demand as
# locale files get too large
locale_cache = {}
# From https://stackoverflow.com/a/6027615
def flatten(d, parent_key='', sep='.'):
items = []
for k, v in d.items():
new_key = parent_key + sep + k if parent_key else k
if isinstance(v, MutableMapping):
items.extend(flatten(v, new_key, sep=sep).items())
else:
items.append((new_key, v))
return dict(items)
def load_locale_file():
err_prompt = []
locales_path = os.path.abspath('./locales')
locales = os.listdir(locales_path)
try:
for l in locales:
with open(f'{locales_path}/{l}', 'r', encoding='utf-8') as f:
locale_cache[remove_suffix(l, '.json')] = flatten(json.load(f))
except Exception as e:
err_prompt.append(str(e))
modules_path = os.path.abspath('./modules')
for m in os.listdir(modules_path):
if os.path.isdir(f'{modules_path}/{m}'):
if os.path.exists(f'{modules_path}/{m}/locales'):
locales_m = os.listdir(f'{modules_path}/{m}/locales')
for lm in locales_m:
ml = f'{modules_path}/{m}/locales/{lm}'
with open(ml, 'r', encoding='utf-8') as f:
try:
if remove_suffix(lm, '.json') in locale_cache:
locale_cache[remove_suffix(lm, '.json')].update(flatten(json.load(f)))
else:
locale_cache[remove_suffix(lm, '.json')] = flatten(json.load(f))
except Exception as e:
err_prompt.append(f'Failed to load {ml}: {e}')
return err_prompt
class LocaleFile(TypedDict):
key: str
string: str
class Locale:
def __init__(self, locale: str, fallback_lng=None):
"""创建一个本地化对象"""
if fallback_lng is None:
fallback_lng = ['zh_cn', 'zh_tw', 'en_us']
self.locale = locale
self.data: LocaleFile = locale_cache[locale]
self.fallback_lng = fallback_lng
def __getitem__(self, key: str):
return self.data[key]
def __contains__(self, key: str):
return key in self.data
def t(self, key: str, fallback_failed_prompt=True, *args, **kwargs) -> str:
'''获取本地化字符串'''
localized = self.get_string_with_fallback(key, fallback_failed_prompt)
return Template(localized).safe_substitute(*args, **kwargs)
def get_string_with_fallback(self, key: str, fallback_failed_prompt) -> str:
value = self.data.get(key, None)
if value is not None:
return value # 1. 如果本地化字符串存在,直接返回
fallback_lng = list(self.fallback_lng)
fallback_lng.insert(0, self.locale)
for lng in fallback_lng:
if lng in locale_cache:
string = locale_cache[lng].get(key, None)
if string is not None:
return string # 2. 如果在 fallback 语言中本地化字符串存在,直接返回
if fallback_failed_prompt:
return f'{{{key}}}' + self.t("i18n.prompt.fallback.failed", url=Config('bug_report_url'),
fallback_failed_prompt=False)
else:
return key
# 3. 如果在 fallback 语言中本地化字符串不存在,返回 key
def get_available_locales():
return list(locale_cache.keys())
__all__ = ['Locale', 'load_locale_file', 'get_available_locales']