#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Определение языка
"""
# ######################################################################################################################
# Импорт необходимых инструментов
# ######################################################################################################################
# Интернационализация (I18N) и локализация (L10N) (см. https://www.loc.gov/standards/iso639-2/php/code_list.php)
# - brew install gettext (Если не установлен)
# - brew link gettext --force
# 1. gettext --help
# 2. locate pygettext.py
# 3. /usr/local/Cellar/python@3.9/3.9.16/Frameworks/Python.framework/Versions/3.9/share/doc/python3.9/examples/
# Tools/i18n/pygettext.py -d openav -o openav/modules/locales/base.pot openav
# 4. msgfmt --help
# 5. locate msgfmt.py
# 6. /usr/local/Cellar/python@3.9/3.9.16/Frameworks/Python.framework/Versions/3.9/share/doc/python3.9/examples/
# Tools/i18n/msgfmt.py openav/modules/locales/en/LC_MESSAGES/base.po openav/modules/locales/en/LC_MESSAGES/base
# Подавление Warning
import warnings
for warn in [UserWarning, FutureWarning]:
warnings.filterwarnings("ignore", category=warn)
from dataclasses import dataclass, field # Класс данных
import os # Взаимодействие с файловой системой
import sys # Доступ к некоторым переменным и функциям Python
import gettext # Формирование языковых пакетов
import inspect # Инспектор
import argparse # Парсинг аргументов и параметров командной строки
# Типы данных
from typing import List, Dict, Optional
from types import MethodType
# Персональные
from openav.modules.core.logging import Logging # Логирование
# ######################################################################################################################
# Константы
# ######################################################################################################################
LANG: str = "ru" # Язык
# ######################################################################################################################
# Интернационализация (I18N) и локализация (L10N)
# ######################################################################################################################
[документация]@dataclass
class Language(Logging):
"""Класс для интернационализации (I18N) и локализации (L10N)
Args:
path_to_logs (str): Смотреть :attr:`~openav.modules.core.logging.Logging.path_to_logs`
lang (str): Язык
"""
# ------------------------------------------------------------------------------------------------------------------
# Конструктор
# ------------------------------------------------------------------------------------------------------------------
lang: str
__lang: str = field(default=LANG, init=False, repr=False)
"""Язык
.. note::
private (приватный аргумент)
"""
def __post_init__(self):
super().__post_init__() # Выполнение конструктора из суперкласса
self.__i18n: List[Dict[str, MethodType]] = self.__get_locales() # Получение языковых пакетов
self._: MethodType = self.__set_locale(self.lang) # Установка языка
argparse._ = self.__i18n[1][self.lang] # Установка языка для парсинга аргументов и параметров командной строки
# ------------------------------------------------------------------------------------------------------------------
# Свойства
# ------------------------------------------------------------------------------------------------------------------
@property
def lang(self) -> str:
"""Получение/установка текущего языка
Args:
(str): Язык, доступные варианты:
* ``"ru"`` - Русский язык (``по умолчанию``)
* ``"en"`` - Английский язык
Returns:
str: Язык
"""
return self.__lang
@lang.setter
def lang(self, lang: str):
"""Установка текущего языка"""
try:
# Проверка аргументов
if type(lang) is not str or not lang or (lang in self.locales) is False:
raise TypeError
except TypeError:
pass
else:
self.__lang = lang
@property
def locales(self) -> List[str]:
"""Получение поддерживаемых языков
Returns:
List[str]: Список поддерживаемых языков
"""
return self.__get_languages() # Поддерживаемые языки
@property
def path_to_locales(self) -> str:
"""Получение директории с языковыми пакетами
Returns:
str: Директория с языковыми пакетами
"""
# Нормализация пути
return os.path.normpath(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "locales")))
# ------------------------------------------------------------------------------------------------------------------
# Внутренние методы (приватные)
# ------------------------------------------------------------------------------------------------------------------
def __get_languages(self) -> List[Optional[str]]:
"""Получение поддерживаемых языков
.. note::
private (приватный метод)
Returns:
List[Optional[str]]: Список поддерживаемых языков
"""
# Директория с языками найдена
if os.path.exists(self.path_to_locales):
# Формирование списка с подерживаемыми языками
return next(os.walk(self.path_to_locales))[1]
return []
def __get_locales(self) -> List[Dict[str, MethodType]]:
"""Получение языковых пакетов
.. note::
private (приватный метод)
Returns:
List[Dict[str, MethodType]]: Список словарей с языковыми пакетами
"""
# Языки
tr_argparse = {}
trs_base = {}
# Проход по всем языкам
for curr_lang in self.locales:
tr_argparse[curr_lang] = gettext.translation(
"argparse", # Домен
localedir=self.path_to_locales, # Директория с поддерживаемыми языками
languages=[curr_lang], # Язык
fallback=True, # Отключение ошибки
).gettext
trs_base[curr_lang] = gettext.translation(
"base", # Домен
localedir=self.path_to_locales, # Директория с поддерживаемыми языками
languages=[curr_lang], # Язык
fallback=True, # Отключение ошибки
).gettext
return [trs_base, tr_argparse]
def __set_locale(self, lang: str = LANG) -> MethodType:
"""Установка языка
.. note::
private (приватный метод)
Args:
lang (str): Язык
Returns:
MethodType: MethodType перевода строк на один из поддерживаемых языков если метод запущен через конструктор
"""
try:
# Проверка аргументов
if type(lang) is not str:
raise TypeError
except TypeError:
pass
else:
# Проход по всем поддерживаемым языкам
for curr_lang in self.locales:
# В аргументах командной строки не найден язык
if curr_lang not in sys.argv:
# В аргументах метода не найден язык
if lang != curr_lang:
continue
self.lang = curr_lang # Изменение языка
# Метод запущен в конструкторе
if inspect.stack()[1].function == "__init__" or inspect.stack()[1].function == "__post_init__":
return self.__i18n[0][self.lang]
else:
self._ = self.__i18n[0][self.lang]