Coverage for aipyapp/i18n.py: 65%

1#!/usr/bin/env python

2# -*- coding: utf-8 -*-

3

4 import locale

5 import csv

6 from importlib import resources

7 import os

8 import ctypes

9 import platform

10

11 from loguru import logger

12

13 def get_system_language() -> str:

14 """

15 获取当前运行环境的语言代码 (例如 'en', 'zh')。

16 支持 Windows, macOS, Linux。

17 返回小写的语言代码，如果无法确定则返回 'en'。

18 """

19 language_code = 'en' # 默认英语

20

21 try:

22 if platform.system() == "Windows":

23 # Windows: 使用 GetUserDefaultUILanguage 或 GetSystemDefaultUILanguage

24 # https://learn.microsoft.com/en-us/windows/win32/intl/language-identifiers

25 windll = ctypes.windll.kernel32

26 # GetUserDefaultUILanguage 返回当前用户的UI语言ID

27 lang_id = windll.GetUserDefaultUILanguage()

28 # 将语言ID转换为标准语言代码 (例如 1033 -> en, 2052 -> zh)

29 # 主要语言ID在低10位

30 primary_lang_id = lang_id & 0x3FF

31 if primary_lang_id == 0x04: # zh - Chinese

32 language_code = 'zh'

33 elif primary_lang_id == 0x09: # en - English

34 language_code = 'en'

35 # 可以根据需要添加更多语言ID映射

36 # 参考: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c

37

38 elif platform.system() == "Darwin": # macOS

39 # macOS: 优先使用 locale.getlocale()

40 language, encoding = locale.getlocale()

41 if language:

42 language_code = language.split('_')[0].lower()

43 else:

44 # 备选方案: 读取环境变量

45 lang_env = os.environ.get('LANG')

46 if lang_env:

47 language_code = lang_env.split('_')[0].lower()

48

49 else: # Linux/Unix

50 # Linux/Unix: 优先使用 locale.getlocale()

51 language, encoding = locale.getlocale()

52 if language:

53 language_code = language.split('_')[0].lower()

54 else:

55 # 备选方案: 读取环境变量 LANG 或 LANGUAGE

56 lang_env = os.environ.get('LANG') or os.environ.get('LANGUAGE')

57 if lang_env:

58 language_code = lang_env.split('_')[0].lower()

59

60 # 规范化常见的中文代码

61 if language_code.startswith('zh'):

62 language_code = 'zh'

63

64 except Exception:

65 # 如果发生任何错误，回退到默认值 'en'

66 pass # 使用默认的 'en'

67

68 return language_code

69

70 class Translator:

71 def __init__(self, lang=None):

72 self.lang = lang

73 self.messages = {}

74 self.log = logger.bind(src='i18n')

75

76 def get_lang(self):

77 return self.lang

78

79 def set_lang(self, lang=None):

80 """Set the current language."""

81 if not lang:

82 lang = get_system_language()

83 self.log.info(f"No language specified, using system language: {lang}")

84

85 if lang != self.lang:

86 if self.lang: self.log.info(f"Switching language from {self.lang} to: {lang}")

87 else: self.log.info(f"Setting language to: {lang}")

88 self.lang = lang

89 self.load_messages()

90

91 def load_messages(self):

92 try:

93 with resources.open_text('aipyapp.res', 'locales.csv') as f:

94 reader = csv.DictReader(f)

95 for row in reader:

96 self.messages[row['en']] = None if self.lang=='en' else row.get(self.lang)

97 except Exception as e:

98 self.log.error(f"Error loading translations: {e}")

99

100 def translate(self, key, *args):

101 if not self.lang:

102 self.set_lang()

103

104 if self.lang == 'en':

105 msg = key

106 else:

107 msg = self.messages.get(key)

108 if not msg:

109 self.log.error(f"Translation not found for key: {key}")

110 msg = key

111 return msg.format(*args) if args else msg

112

113 translator = Translator()

114 T = translator.translate

115 get_lang = translator.get_lang

116 set_lang = translator.set_lang

117