|
| 1 | +from enum import Enum |
| 2 | + |
| 3 | +""" |
| 4 | +An enumeration representing two loading modes. |
| 5 | +
|
| 6 | +- ``REALTIME``: Static data files are read for each conversion. |
| 7 | +- ``ONETIME``: Static data files are only read once when the object is instantiated for the first time. |
| 8 | +""" |
| 9 | +load_mode = Enum('LoadMode', ('REALTIME', 'ONETIME')) |
| 10 | + |
| 11 | + |
| 12 | +from _errors import ExpressionError |
| 13 | +from _errors import LangDictSearchError |
| 14 | +from _lang_dict_parser import LangTypeLangDict |
| 15 | +from easierfile import File |
| 16 | + |
| 17 | + |
| 18 | +class _LangUniversal(): |
| 19 | + def __init__(self, super_self, lang_dicts): |
| 20 | + self.__m_dict = lang_dicts |
| 21 | + self.__m_super_self = super_self |
| 22 | + |
| 23 | + def __getattr__(self, universal_word_form_call): |
| 24 | + """ |
| 25 | + Chained calls enable the universal word to support object-oriented expression without string quotes in the code. |
| 26 | + """ |
| 27 | + universal_word_form_call_chain = [universal_word_form_call] |
| 28 | + super_self = self.__m_super_self |
| 29 | + lang_dicts = self.__m_dict |
| 30 | + |
| 31 | + class ChainedClass: |
| 32 | + def __getattr__(self, universal_word_form_call): |
| 33 | + universal_word_form_call_chain.append(universal_word_form_call) |
| 34 | + return ChainedClass() |
| 35 | + |
| 36 | + def __repr__(self): |
| 37 | + """ |
| 38 | + Final settlement of Chained Calls. |
| 39 | + """ |
| 40 | + if universal_word_form_call_chain[-1] in lang_dicts.keys(): |
| 41 | + return super_self.str('.'.join(universal_word_form_call_chain[0:-1]), None, universal_word_form_call_chain[-1]) |
| 42 | + else: |
| 43 | + return super_self.str('.'.join(universal_word_form_call_chain)) |
| 44 | + |
| 45 | + return ChainedClass() |
| 46 | + |
| 47 | + |
| 48 | +class Lang: |
| 49 | + def __init__(self, include=(), load_mode=load_mode.REALTIME): |
| 50 | + self.__m_dict = {} |
| 51 | + self._m_load_mode = load_mode |
| 52 | + |
| 53 | + # None represents the universal word. |
| 54 | + self.__m_default_source_lang = None |
| 55 | + self.__m_default_target_lang = None |
| 56 | + |
| 57 | + if len(include) != 0: |
| 58 | + for expression in include: |
| 59 | + """ |
| 60 | + Parses expressions imported into language dictionaries via param<include>. |
| 61 | +
|
| 62 | + There are two forms of this expression, one is full and the other is short. |
| 63 | +
|
| 64 | + For example: |
| 65 | +
|
| 66 | + 1. ``full``: './en_us.lang as en_us' |
| 67 | + 2. ``short``: './en_us.lang' |
| 68 | +
|
| 69 | + Note that when using the short form, the name of the associated language will be the name of the file. |
| 70 | + """ |
| 71 | + if expression.find("as") != -1: |
| 72 | + if expression.count("as") == 1: |
| 73 | + """ |
| 74 | + A list of strings representing the distinct terms of the expression after it is parsed. |
| 75 | + |
| 76 | + - ``expression_parsing[0]``: The file path of the language dictionary. |
| 77 | + - ``expression_parsing[1]``: The associated language of the language dictionary. |
| 78 | + """ |
| 79 | + expression_parsing = [expression_unit.strip() for expression_unit in expression] |
| 80 | + dictionary_file = File(expression_parsing[0]) |
| 81 | + dictionary_associated_lang = expression_parsing[1] |
| 82 | + else: |
| 83 | + raise ExpressionError(expression) |
| 84 | + else: |
| 85 | + dictionary_file = File(expression) |
| 86 | + dictionary_associated_lang = dictionary_file.info["name"] |
| 87 | + |
| 88 | + # Store language dictionary file information for different languages into member:private<self.__m_dict>. |
| 89 | + if not dictionary_file.status["exist"]: |
| 90 | + raise FileNotFoundError("Language dictionary not found: " + dictionary_file.info["path"]) |
| 91 | + else: |
| 92 | + if dictionary_associated_lang in self.__m_dict.keys(): |
| 93 | + self.__m_dict[dictionary_associated_lang].append(dictionary_file.info["path"]) |
| 94 | + else: |
| 95 | + self.__m_dict[dictionary_associated_lang] = [dictionary_file.info["path"]] |
| 96 | + |
| 97 | + # One-time loading mode. |
| 98 | + if self._m_load_mode == load_mode.ONETIME: |
| 99 | + self.__load_lang_dict() |
| 100 | + |
| 101 | + def __load_lang_dict(self): |
| 102 | + new_dict = self.__m_dict.copy() |
| 103 | + for dictionary_associated_lang, dictionary in self.__m_dict.items(): |
| 104 | + if isinstance(dictionary, list): # Indicates that dictionary is a list of lang dict file path. |
| 105 | + new_dict[dictionary_associated_lang] = {} |
| 106 | + for dictionary_file_path in dictionary: |
| 107 | + dict_file = File(dictionary_file_path) |
| 108 | + if dict_file.info["ext"] == "lang": # Parse the lang dict of type .lang |
| 109 | + new_dict[dictionary_associated_lang].update(LangTypeLangDict.loads(dict_file.content)) |
| 110 | + self.__m_dict = new_dict |
| 111 | + |
| 112 | + def default_lang(self, source_lang=None, target_lang=None): |
| 113 | + if source_lang is not None: |
| 114 | + self.__m_default_source_lang = source_lang |
| 115 | + if target_lang is not None: |
| 116 | + self.__m_default_target_lang = target_lang |
| 117 | + |
| 118 | + def str(self, word, source_lang=None, target_lang=None): |
| 119 | + # Whether are default langs setting. |
| 120 | + if source_lang is None: |
| 121 | + source_lang = self.__m_default_source_lang |
| 122 | + if target_lang is None: |
| 123 | + target_lang = self.__m_default_target_lang |
| 124 | + |
| 125 | + # Real-time loading mode. |
| 126 | + if self._m_load_mode == load_mode.REALTIME: |
| 127 | + self.__load_lang_dict() |
| 128 | + |
| 129 | + # Initialize lang dict search transition value. |
| 130 | + source_word = word |
| 131 | + universal_word = None |
| 132 | + target_word = None |
| 133 | + |
| 134 | + # Search for the universal word corresponding to the native word. |
| 135 | + if source_lang is None: |
| 136 | + is_matched = False |
| 137 | + for dictionary in self.__m_dict.values(): |
| 138 | + if source_word in dictionary.keys(): |
| 139 | + universal_word = source_word |
| 140 | + is_matched = True |
| 141 | + if not is_matched: |
| 142 | + raise LangDictSearchError( |
| 143 | + LangDictSearchError.UNIVERSAL_WORD_EXIST_ERROR, source_word, source_lang, target_lang) |
| 144 | + else: |
| 145 | + is_matched = False |
| 146 | + for dictionary_universal_word, dictionary_native_word in self.__m_dict[source_lang].items(): |
| 147 | + if dictionary_native_word == source_word: |
| 148 | + universal_word = dictionary_universal_word |
| 149 | + is_matched = True |
| 150 | + if not is_matched: |
| 151 | + raise LangDictSearchError( |
| 152 | + LangDictSearchError.SOURCE_LANG_DICT_MATCH_ERROR, source_word, source_lang, target_lang) |
| 153 | + |
| 154 | + # Search for the native word corresponding to the universal word. |
| 155 | + if target_lang is None: |
| 156 | + target_word = universal_word |
| 157 | + else: |
| 158 | + is_matched = False |
| 159 | + for dictionary_universal_word, dictionary_native_word in self.__m_dict[target_lang].items(): |
| 160 | + if dictionary_universal_word == universal_word: |
| 161 | + target_word = dictionary_native_word |
| 162 | + is_matched =True |
| 163 | + if not is_matched: |
| 164 | + raise LangDictSearchError( |
| 165 | + LangDictSearchError.TARGET_LANG_DICT_MATCH_ERROR, source_word, source_lang, target_lang) |
| 166 | + |
| 167 | + return target_word |
| 168 | + |
| 169 | + @property |
| 170 | + def universal(self): |
| 171 | + return _LangUniversal(self, self.__m_dict) |
0 commit comments