在Fastapi中提供I18N支持的简单方法
#网络开发人员 #教程 #python #fastapi

处理I18N时,最常见的方法是添加中间件以检测输入请求中的特定语言环境。幸运的是,Fastapi支持中间件,使其非常简单。

  • i18n_middleware.py
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.requests import Request


class I18nMiddleware(BaseHTTPMiddleware):
    WHITE_LIST = ['en', 'ja', 'zh-TW']

    async def dispatch(  # type: ignore
            self, request: Request, call_next: RequestResponseEndpoint):
        # 1. headers 2. path 3. query string
        locale = request.headers.get('locale', None) or \
                 request.path_params.get('locale', None) or \
                 request.query_params.get('locale', None) or \
                 'zh-TW'

        if locale not in self.WHITE_LIST:
            locale = 'zh-TW'
        request.state.locale = locale

        return await call_next(request)

您可以在main.py中添加中间件:

  • main.py
app = FastAPI()
app.add_middleware(I18nMiddleware)

然后您可以在路由器中使用它:

@router.get('/my-resource')
def get_my_resource(request: Request): 
    print(request.state.locale)

但是,没有像 laravel __ ror I18n.t这样的全球助手,因此我们需要创建自己的。

这个想法非常简单:将翻译脚本放在Python中,然后使用importlib编写自己的进口商。您可以做到这一点:

  1. 写您的翻译脚本
locale = {
  'greeting': 'Hi, {user_name}',
  'title': 'hello world',
}
  1. 将您的翻译脚本放在以下结构中
app/lang
├── en
│   └── messages.py
├── ja
│   └── messages.py
└── zh-TW
    └── messages.py
  1. 写一个班级来处理翻译
import importlib
from typing import Any, Dict


class Translator:
    _instances: Dict[str, 'Translator'] = {}

    def __new__(cls, lang: str) -> 'Translator':
        if lang not in cls._instances:
            cls._instances[lang] = super(Translator, cls).__new__(cls)
        return cls._instances[lang]

    def __init__(self, lang: str):
        self.lang = lang

    def t(self, key: str, **kwargs: Dict[str, Any]) -> str:
        file_key, *translation_keys = key.split('.')

        locale_module = importlib.import_module(f'app.lang.{self.lang}.{file_key}')

        translation = locale_module.locale
        for translation_key in translation_keys:
            translation = translation.get(translation_key, None)
            if translation is None:
                return f'Key {key} not found in {self.lang} locale'
        if kwargs.keys():
            translation = translation.format(**kwargs)
        return translation

然后您可以在路由器中使用它

@router.get('/my-resource')
def get_my_resource(request: Request): 
    translator = Translator(request.state.locale)
    # 'hello world'
    print(translator.t('messages.title'))
    # 'Hi, Jon Doe'
    print(translator.t('messages.greeting'), user_name='Jon Doe')

也很容易。