修改为东南天坐标系
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
__all__ = [
|
||||
# emoji.core
|
||||
'emojize',
|
||||
'demojize',
|
||||
'analyze',
|
||||
'config',
|
||||
'emoji_list',
|
||||
'distinct_emoji_list',
|
||||
'emoji_count',
|
||||
'replace_emoji',
|
||||
'is_emoji',
|
||||
'purely_emoji',
|
||||
'version',
|
||||
'Token',
|
||||
'EmojiMatch',
|
||||
'EmojiMatchZWJ',
|
||||
'EmojiMatchZWJNonRGI',
|
||||
# emoji.unicode_codes
|
||||
'EMOJI_DATA',
|
||||
'STATUS',
|
||||
'LANGUAGES',
|
||||
]
|
||||
|
||||
__version__ = '2.15.0'
|
||||
__author__ = 'Taehoon Kim, Kevin Wurster'
|
||||
__email__ = 'carpedm20@gmail.com'
|
||||
# and wursterk@gmail.com, tahir.jalilov@gmail.com
|
||||
__source__ = 'https://github.com/carpedm20/emoji/'
|
||||
__license__ = """
|
||||
New BSD License
|
||||
|
||||
Copyright (c) 2014-2025, Taehoon Kim, Kevin Wurster
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* The names of its contributors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
|
||||
from emoji.core import *
|
||||
from emoji.unicode_codes import *
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
445
backend_service/venv/lib/python3.13/site-packages/emoji/core.py
Normal file
445
backend_service/venv/lib/python3.13/site-packages/emoji/core.py
Normal file
@@ -0,0 +1,445 @@
|
||||
"""
|
||||
emoji.core
|
||||
~~~~~~~~~~
|
||||
|
||||
Core components for emoji.
|
||||
|
||||
"""
|
||||
|
||||
import re
|
||||
import unicodedata
|
||||
from typing import Any, Callable, Dict, Iterator, List, Literal, Match, Optional, Tuple, TypedDict, Union
|
||||
|
||||
from emoji import unicode_codes
|
||||
from emoji.tokenizer import (
|
||||
Token,
|
||||
EmojiMatch,
|
||||
EmojiMatchZWJ,
|
||||
EmojiMatchZWJNonRGI,
|
||||
tokenize,
|
||||
filter_tokens,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'emojize',
|
||||
'demojize',
|
||||
'analyze',
|
||||
'config',
|
||||
'emoji_list',
|
||||
'distinct_emoji_list',
|
||||
'emoji_count',
|
||||
'replace_emoji',
|
||||
'is_emoji',
|
||||
'purely_emoji',
|
||||
'version',
|
||||
'Token',
|
||||
'EmojiMatch',
|
||||
'EmojiMatchZWJ',
|
||||
'EmojiMatchZWJNonRGI',
|
||||
]
|
||||
|
||||
_DEFAULT_DELIMITER = ':'
|
||||
# In Arabic language, the unicode character "\u0655" should be kept so we add it to the pattern below
|
||||
_EMOJI_NAME_PATTERN = '\\w\\-&.’”“()!#*+,/«»\u0300\u0301\u0302\u0303\u0306\u0308\u030a\u0327\u064b\u064e\u064f\u0650\u0653\u0654\u3099\u30fb\u309a\u0655'
|
||||
|
||||
|
||||
class _EmojiListReturn(TypedDict):
|
||||
emoji: str
|
||||
match_start: int
|
||||
match_end: int
|
||||
|
||||
|
||||
class config:
|
||||
"""Module-wide configuration"""
|
||||
|
||||
demojize_keep_zwj = True
|
||||
"""Change the behavior of :func:`emoji.demojize()` regarding
|
||||
zero-width-joiners (ZWJ/``\\u200D``) in emoji that are not
|
||||
"recommended for general interchange" (non-RGI).
|
||||
It has no effect on RGI emoji.
|
||||
|
||||
For example this family emoji with different skin tones "👨👩🏿👧🏻👦🏾" contains four
|
||||
person emoji that are joined together by three ZWJ characters:
|
||||
``👨\\u200D👩🏿\\u200D👧🏻\\u200D👦🏾``
|
||||
|
||||
If ``True``, the zero-width-joiners will be kept and :func:`emoji.emojize()` can
|
||||
reverse the :func:`emoji.demojize()` operation:
|
||||
``emoji.emojize(emoji.demojize(s)) == s``
|
||||
|
||||
The example emoji would be converted to
|
||||
``:man:\\u200d:woman_dark_skin_tone:\\u200d:girl_light_skin_tone:\\u200d:boy_medium-dark_skin_tone:``
|
||||
|
||||
If ``False``, the zero-width-joiners will be removed and :func:`emoji.emojize()`
|
||||
can only reverse the individual emoji: ``emoji.emojize(emoji.demojize(s)) != s``
|
||||
|
||||
The example emoji would be converted to
|
||||
``:man::woman_dark_skin_tone::girl_light_skin_tone::boy_medium-dark_skin_tone:``
|
||||
"""
|
||||
|
||||
replace_emoji_keep_zwj = False
|
||||
"""Change the behavior of :func:`emoji.replace_emoji()` regarding
|
||||
zero-width-joiners (ZWJ/``\\u200D``) in emoji that are not
|
||||
"recommended for general interchange" (non-RGI).
|
||||
It has no effect on RGI emoji.
|
||||
|
||||
See :attr:`config.demojize_keep_zwj` for more information.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def load_language(language: Union[List[str], str, None] = None):
|
||||
"""Load one or multiple languages into memory.
|
||||
If no language is specified, all languages will be loaded.
|
||||
|
||||
This makes language data accessible in the :data:`EMOJI_DATA` dict.
|
||||
For example to access a French emoji name, first load French with
|
||||
|
||||
``emoji.config.load_language('fr')``
|
||||
|
||||
and then access it with
|
||||
|
||||
``emoji.EMOJI_DATA['🏄']['fr']``
|
||||
|
||||
Available languages are listed in :data:`LANGUAGES`"""
|
||||
|
||||
languages = (
|
||||
[language]
|
||||
if isinstance(language, str)
|
||||
else language
|
||||
if language
|
||||
else unicode_codes.LANGUAGES
|
||||
)
|
||||
|
||||
for lang in languages:
|
||||
unicode_codes.load_from_json(lang)
|
||||
|
||||
|
||||
def emojize(
|
||||
string: str,
|
||||
delimiters: Tuple[str, str] = (_DEFAULT_DELIMITER, _DEFAULT_DELIMITER),
|
||||
variant: Optional[Literal['text_type', 'emoji_type']] = None,
|
||||
language: str = 'en',
|
||||
version: Optional[float] = None,
|
||||
handle_version: Optional[Union[str, Callable[[str, Dict[str, str]], str]]] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Replace emoji names in a string with Unicode codes.
|
||||
>>> import emoji
|
||||
>>> print(emoji.emojize("Python is fun :thumbsup:", language='alias'))
|
||||
Python is fun 👍
|
||||
>>> print(emoji.emojize("Python is fun :thumbs_up:"))
|
||||
Python is fun 👍
|
||||
>>> print(emoji.emojize("Python is fun {thumbs_up}", delimiters = ("{", "}")))
|
||||
Python is fun 👍
|
||||
>>> print(emoji.emojize("Python is fun :red_heart:", variant="text_type"))
|
||||
Python is fun ❤
|
||||
>>> print(emoji.emojize("Python is fun :red_heart:", variant="emoji_type"))
|
||||
Python is fun ❤️ # red heart, not black heart
|
||||
|
||||
:param string: String contains emoji names.
|
||||
:param delimiters: (optional) Use delimiters other than _DEFAULT_DELIMITER. Each delimiter
|
||||
should contain at least one character that is not part of a-zA-Z0-9 and ``_-&.()!?#*+,``.
|
||||
See ``emoji.core._EMOJI_NAME_PATTERN`` for the regular expression of unsafe characters.
|
||||
:param variant: (optional) Choose variation selector between "base"(None), VS-15 ("text_type") and VS-16 ("emoji_type")
|
||||
:param language: Choose language of emoji name: language code 'es', 'de', etc. or 'alias'
|
||||
to use English aliases
|
||||
:param version: (optional) Max version. If set to an Emoji Version,
|
||||
all emoji above this version will be ignored.
|
||||
:param handle_version: (optional) Replace the emoji above ``version``
|
||||
instead of ignoring it. handle_version can be either a string or a
|
||||
callable; If it is a callable, it's passed the Unicode emoji and the
|
||||
data dict from :data:`EMOJI_DATA` and must return a replacement string
|
||||
to be used::
|
||||
|
||||
handle_version('\\U0001F6EB', {
|
||||
'en' : ':airplane_departure:',
|
||||
'status' : fully_qualified,
|
||||
'E' : 1,
|
||||
'alias' : [':flight_departure:'],
|
||||
'de': ':abflug:',
|
||||
'es': ':avión_despegando:',
|
||||
...
|
||||
})
|
||||
|
||||
:raises ValueError: if ``variant`` is neither None, 'text_type' or 'emoji_type'
|
||||
|
||||
"""
|
||||
|
||||
unicode_codes.load_from_json(language)
|
||||
|
||||
pattern = re.compile(
|
||||
'(%s[%s]+%s)'
|
||||
% (re.escape(delimiters[0]), _EMOJI_NAME_PATTERN, re.escape(delimiters[1]))
|
||||
)
|
||||
|
||||
def replace(match: Match[str]) -> str:
|
||||
name = match.group(1)[len(delimiters[0]) : -len(delimiters[1])]
|
||||
emj = unicode_codes.get_emoji_by_name(
|
||||
_DEFAULT_DELIMITER
|
||||
+ unicodedata.normalize('NFKC', name)
|
||||
+ _DEFAULT_DELIMITER,
|
||||
language,
|
||||
)
|
||||
|
||||
if emj is None:
|
||||
return match.group(1)
|
||||
|
||||
if version is not None and unicode_codes.EMOJI_DATA[emj]['E'] > version:
|
||||
if callable(handle_version):
|
||||
emj_data = unicode_codes.EMOJI_DATA[emj].copy()
|
||||
emj_data['match_start'] = match.start()
|
||||
emj_data['match_end'] = match.end()
|
||||
return handle_version(emj, emj_data)
|
||||
|
||||
elif handle_version is not None:
|
||||
return str(handle_version)
|
||||
else:
|
||||
return ''
|
||||
|
||||
if variant is None or 'variant' not in unicode_codes.EMOJI_DATA[emj]:
|
||||
return emj
|
||||
|
||||
if emj[-1] == '\ufe0e' or emj[-1] == '\ufe0f':
|
||||
# Remove an existing variant
|
||||
emj = emj[0:-1]
|
||||
if variant == 'text_type':
|
||||
return emj + '\ufe0e'
|
||||
elif variant == 'emoji_type':
|
||||
return emj + '\ufe0f'
|
||||
else:
|
||||
raise ValueError(
|
||||
"Parameter 'variant' must be either None, 'text_type' or 'emoji_type'"
|
||||
)
|
||||
|
||||
return pattern.sub(replace, string)
|
||||
|
||||
|
||||
def analyze(
|
||||
string: str, non_emoji: bool = False, join_emoji: bool = True
|
||||
) -> Iterator[Token]:
|
||||
"""
|
||||
Find unicode emoji in a string. Yield each emoji as a named tuple
|
||||
:class:`Token` ``(chars, EmojiMatch)`` or :class:`Token` ``(chars, EmojiMatchZWJNonRGI)``.
|
||||
If ``non_emoji`` is True, also yield all other characters as
|
||||
:class:`Token` ``(char, char)`` .
|
||||
|
||||
:param string: String to analyze
|
||||
:param non_emoji: If True also yield all non-emoji characters as Token(char, char)
|
||||
:param join_emoji: If True, multiple EmojiMatch are merged into a single
|
||||
EmojiMatchZWJNonRGI if they are separated only by a ZWJ.
|
||||
"""
|
||||
|
||||
return filter_tokens(
|
||||
tokenize(string, keep_zwj=True), emoji_only=not non_emoji, join_emoji=join_emoji
|
||||
)
|
||||
|
||||
|
||||
def demojize(
|
||||
string: str,
|
||||
delimiters: Tuple[str, str] = (_DEFAULT_DELIMITER, _DEFAULT_DELIMITER),
|
||||
language: str = 'en',
|
||||
version: Optional[float] = None,
|
||||
handle_version: Optional[Union[str, Callable[[str, Dict[str, str]], str]]] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Replace Unicode emoji in a string with emoji shortcodes. Useful for storage.
|
||||
>>> import emoji
|
||||
>>> print(emoji.emojize("Python is fun :thumbs_up:"))
|
||||
Python is fun 👍
|
||||
>>> print(emoji.demojize("Python is fun 👍"))
|
||||
Python is fun :thumbs_up:
|
||||
>>> print(emoji.demojize("icode is tricky 😯", delimiters=("__", "__")))
|
||||
Unicode is tricky __hushed_face__
|
||||
|
||||
:param string: String contains Unicode characters. MUST BE UNICODE.
|
||||
:param delimiters: (optional) User delimiters other than ``_DEFAULT_DELIMITER``
|
||||
:param language: Choose language of emoji name: language code 'es', 'de', etc. or 'alias'
|
||||
to use English aliases
|
||||
:param version: (optional) Max version. If set to an Emoji Version,
|
||||
all emoji above this version will be removed.
|
||||
:param handle_version: (optional) Replace the emoji above ``version``
|
||||
instead of removing it. handle_version can be either a string or a
|
||||
callable ``handle_version(emj: str, data: dict) -> str``; If it is
|
||||
a callable, it's passed the Unicode emoji and the data dict from
|
||||
:data:`EMOJI_DATA` and must return a replacement string to be used.
|
||||
The passed data is in the form of::
|
||||
|
||||
handle_version('\\U0001F6EB', {
|
||||
'en' : ':airplane_departure:',
|
||||
'status' : fully_qualified,
|
||||
'E' : 1,
|
||||
'alias' : [':flight_departure:'],
|
||||
'de': ':abflug:',
|
||||
'es': ':avión_despegando:',
|
||||
...
|
||||
})
|
||||
|
||||
"""
|
||||
|
||||
if language == 'alias':
|
||||
language = 'en'
|
||||
_use_aliases = True
|
||||
else:
|
||||
_use_aliases = False
|
||||
|
||||
unicode_codes.load_from_json(language)
|
||||
|
||||
def handle(emoji_match: EmojiMatch) -> str:
|
||||
assert emoji_match.data is not None
|
||||
if version is not None and emoji_match.data['E'] > version:
|
||||
if callable(handle_version):
|
||||
return handle_version(emoji_match.emoji, emoji_match.data_copy())
|
||||
elif handle_version is not None:
|
||||
return handle_version
|
||||
else:
|
||||
return ''
|
||||
elif language in emoji_match.data:
|
||||
if _use_aliases and 'alias' in emoji_match.data:
|
||||
return (
|
||||
delimiters[0] + emoji_match.data['alias'][0][1:-1] + delimiters[1]
|
||||
)
|
||||
else:
|
||||
return delimiters[0] + emoji_match.data[language][1:-1] + delimiters[1]
|
||||
else:
|
||||
# The emoji exists, but it is not translated, so we keep the emoji
|
||||
return emoji_match.emoji
|
||||
|
||||
matches = tokenize(string, keep_zwj=config.demojize_keep_zwj)
|
||||
return ''.join(
|
||||
str(handle(token.value)) if isinstance(token.value, EmojiMatch) else token.value
|
||||
for token in matches
|
||||
)
|
||||
|
||||
|
||||
def replace_emoji(
|
||||
string: str,
|
||||
replace: Union[str, Callable[[str, Dict[str, str]], str]] = '',
|
||||
version: float = -1,
|
||||
) -> str:
|
||||
"""
|
||||
Replace Unicode emoji in a customizable string.
|
||||
|
||||
:param string: String contains Unicode characters. MUST BE UNICODE.
|
||||
:param replace: (optional) replace can be either a string or a callable;
|
||||
If it is a callable, it's passed the Unicode emoji and the data dict from
|
||||
:data:`EMOJI_DATA` and must return a replacement string to be used.
|
||||
replace(str, dict) -> str
|
||||
:param version: (optional) Max version. If set to an Emoji Version,
|
||||
only emoji above this version will be replaced.
|
||||
"""
|
||||
|
||||
def handle(emoji_match: EmojiMatch) -> str:
|
||||
if version > -1:
|
||||
assert emoji_match.data is not None
|
||||
if emoji_match.data['E'] > version:
|
||||
if callable(replace):
|
||||
return replace(emoji_match.emoji, emoji_match.data_copy())
|
||||
else:
|
||||
return str(replace)
|
||||
elif callable(replace):
|
||||
return replace(emoji_match.emoji, emoji_match.data_copy())
|
||||
elif replace is not None: # type: ignore
|
||||
return replace
|
||||
return emoji_match.emoji
|
||||
|
||||
matches = tokenize(string, keep_zwj=config.replace_emoji_keep_zwj)
|
||||
if config.replace_emoji_keep_zwj:
|
||||
matches = filter_tokens(matches, emoji_only=False, join_emoji=True)
|
||||
return ''.join(
|
||||
str(handle(m.value)) if isinstance(m.value, EmojiMatch) else m.value
|
||||
for m in matches
|
||||
)
|
||||
|
||||
|
||||
def emoji_list(string: str) -> List[_EmojiListReturn]:
|
||||
"""
|
||||
Returns the location and emoji in list of dict format.
|
||||
>>> emoji.emoji_list("Hi, I am fine. 😁")
|
||||
[{'match_start': 15, 'match_end': 16, 'emoji': '😁'}]
|
||||
"""
|
||||
|
||||
return [
|
||||
{
|
||||
'match_start': m.value.start,
|
||||
'match_end': m.value.end,
|
||||
'emoji': m.value.emoji,
|
||||
}
|
||||
for m in tokenize(string, keep_zwj=False)
|
||||
if isinstance(m.value, EmojiMatch)
|
||||
]
|
||||
|
||||
|
||||
def distinct_emoji_list(string: str) -> List[str]:
|
||||
"""Returns distinct list of emojis from the string."""
|
||||
distinct_list = list({e['emoji'] for e in emoji_list(string)})
|
||||
return distinct_list
|
||||
|
||||
|
||||
def emoji_count(string: str, unique: bool = False) -> int:
|
||||
"""
|
||||
Returns the count of emojis in a string.
|
||||
|
||||
:param unique: (optional) True if count only unique emojis
|
||||
"""
|
||||
if unique:
|
||||
return len(distinct_emoji_list(string))
|
||||
return len(emoji_list(string))
|
||||
|
||||
|
||||
def is_emoji(string: str) -> bool:
|
||||
"""
|
||||
Returns True if the string is a single emoji, and it is "recommended for
|
||||
general interchange" by Unicode.org.
|
||||
"""
|
||||
return string in unicode_codes.EMOJI_DATA
|
||||
|
||||
|
||||
def purely_emoji(string: str) -> bool:
|
||||
"""
|
||||
Returns True if the string contains only emojis.
|
||||
This might not imply that `is_emoji` for all the characters, for example,
|
||||
if the string contains variation selectors.
|
||||
"""
|
||||
return all(isinstance(m.value, EmojiMatch) for m in analyze(string, non_emoji=True))
|
||||
|
||||
|
||||
def version(string: str) -> float:
|
||||
"""
|
||||
Returns the Emoji Version of the emoji.
|
||||
|
||||
See https://www.unicode.org/reports/tr51/#Versioning for more information.
|
||||
>>> emoji.version("😁")
|
||||
0.6
|
||||
>>> emoji.version(":butterfly:")
|
||||
3
|
||||
|
||||
:param string: An emoji or a text containing an emoji
|
||||
:raises ValueError: if ``string`` does not contain an emoji
|
||||
"""
|
||||
# Try dictionary lookup
|
||||
if string in unicode_codes.EMOJI_DATA:
|
||||
return unicode_codes.EMOJI_DATA[string]['E']
|
||||
|
||||
# Try name lookup
|
||||
emj_code = unicode_codes.get_emoji_by_name(string, 'en')
|
||||
if emj_code and emj_code in unicode_codes.EMOJI_DATA:
|
||||
return unicode_codes.EMOJI_DATA[emj_code]['E']
|
||||
|
||||
# Try to find first emoji in string
|
||||
version: List[float] = []
|
||||
|
||||
def f(e: str, emoji_data: Dict[str, Any]) -> str:
|
||||
version.append(emoji_data['E'])
|
||||
return ''
|
||||
|
||||
replace_emoji(string, replace=f, version=-1)
|
||||
if version:
|
||||
return version[0]
|
||||
emojize(string, language='alias', version=-1, handle_version=f)
|
||||
if version:
|
||||
return version[0]
|
||||
for lang_code in unicode_codes.LANGUAGES:
|
||||
emojize(string, language=lang_code, version=-1, handle_version=f)
|
||||
if version:
|
||||
return version[0]
|
||||
|
||||
raise ValueError('No emoji found in string')
|
||||
@@ -0,0 +1,376 @@
|
||||
"""
|
||||
emoji.tokenizer
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Components for detecting and tokenizing emoji in strings.
|
||||
|
||||
"""
|
||||
|
||||
from typing import List, NamedTuple, Dict, Union, Iterator, Any
|
||||
from emoji import unicode_codes
|
||||
|
||||
|
||||
__all__ = [
|
||||
'EmojiMatch',
|
||||
'EmojiMatchZWJ',
|
||||
'EmojiMatchZWJNonRGI',
|
||||
'Token',
|
||||
'tokenize',
|
||||
'filter_tokens',
|
||||
]
|
||||
|
||||
_ZWJ = '\u200d'
|
||||
_SEARCH_TREE: Dict[str, Any] = {}
|
||||
|
||||
|
||||
class EmojiMatch:
|
||||
"""
|
||||
Represents a match of a "recommended for general interchange" (RGI)
|
||||
emoji in a string.
|
||||
"""
|
||||
|
||||
__slots__ = ('emoji', 'start', 'end', 'data')
|
||||
|
||||
def __init__(
|
||||
self, emoji: str, start: int, end: int, data: Union[Dict[str, Any], None]
|
||||
):
|
||||
self.emoji = emoji
|
||||
"""The emoji substring"""
|
||||
|
||||
self.start = start
|
||||
"""The start index of the match in the string"""
|
||||
|
||||
self.end = end
|
||||
"""The end index of the match in the string"""
|
||||
|
||||
self.data = data
|
||||
"""The entry from :data:`EMOJI_DATA` for this emoji or ``None`` if the emoji is non-RGI"""
|
||||
|
||||
def data_copy(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Returns a copy of the data from :data:`EMOJI_DATA` for this match
|
||||
with the additional keys ``match_start`` and ``match_end``.
|
||||
"""
|
||||
if self.data:
|
||||
emj_data = self.data.copy()
|
||||
emj_data['match_start'] = self.start
|
||||
emj_data['match_end'] = self.end
|
||||
return emj_data
|
||||
else:
|
||||
return {'match_start': self.start, 'match_end': self.end}
|
||||
|
||||
def is_zwj(self) -> bool:
|
||||
"""
|
||||
Checks if this is a ZWJ-emoji.
|
||||
|
||||
:returns: True if this is a ZWJ-emoji, False otherwise
|
||||
"""
|
||||
|
||||
return _ZWJ in self.emoji
|
||||
|
||||
def split(self) -> Union['EmojiMatchZWJ', 'EmojiMatch']:
|
||||
"""
|
||||
Splits a ZWJ-emoji into its constituents.
|
||||
|
||||
:returns: An :class:`EmojiMatchZWJ` containing the "sub-emoji" if this is a ZWJ-emoji, otherwise self
|
||||
"""
|
||||
|
||||
if self.is_zwj():
|
||||
return EmojiMatchZWJ(self)
|
||||
else:
|
||||
return self
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'{self.__class__.__name__}({self.emoji}, {self.start}:{self.end})'
|
||||
|
||||
|
||||
class EmojiMatchZWJ(EmojiMatch):
|
||||
"""
|
||||
Represents a match of multiple emoji in a string that were joined by
|
||||
zero-width-joiners (ZWJ/``\\u200D``)."""
|
||||
|
||||
__slots__ = ('emojis',)
|
||||
|
||||
def __init__(self, match: EmojiMatch):
|
||||
super().__init__(match.emoji, match.start, match.end, match.data)
|
||||
|
||||
self.emojis: List[EmojiMatch] = []
|
||||
"""List of sub emoji as EmojiMatch objects"""
|
||||
|
||||
i = match.start
|
||||
for e in match.emoji.split(_ZWJ):
|
||||
m = EmojiMatch(e, i, i + len(e), unicode_codes.EMOJI_DATA.get(e, None))
|
||||
self.emojis.append(m)
|
||||
i += len(e) + 1
|
||||
|
||||
def join(self) -> str:
|
||||
"""
|
||||
Joins a ZWJ-emoji into a string
|
||||
"""
|
||||
|
||||
return _ZWJ.join(e.emoji for e in self.emojis)
|
||||
|
||||
def is_zwj(self) -> bool:
|
||||
return True
|
||||
|
||||
def split(self) -> 'EmojiMatchZWJ':
|
||||
return self
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'{self.__class__.__name__}({self.join()}, {self.start}:{self.end})'
|
||||
|
||||
|
||||
class EmojiMatchZWJNonRGI(EmojiMatchZWJ):
|
||||
"""
|
||||
Represents a match of multiple emoji in a string that were joined by
|
||||
zero-width-joiners (ZWJ/``\\u200D``). This class is only used for emoji
|
||||
that are not "recommended for general interchange" (non-RGI) by Unicode.org.
|
||||
The data property of this class is always None.
|
||||
"""
|
||||
|
||||
def __init__(self, first_emoji_match: EmojiMatch, second_emoji_match: EmojiMatch):
|
||||
self.emojis = [first_emoji_match, second_emoji_match]
|
||||
"""List of sub emoji as EmojiMatch objects"""
|
||||
|
||||
self._update()
|
||||
|
||||
def _update(self):
|
||||
self.emoji = _ZWJ.join(e.emoji for e in self.emojis)
|
||||
self.start = self.emojis[0].start
|
||||
self.end = self.emojis[-1].end
|
||||
self.data = None
|
||||
|
||||
def _add(self, next_emoji_match: EmojiMatch):
|
||||
self.emojis.append(next_emoji_match)
|
||||
self._update()
|
||||
|
||||
|
||||
class Token(NamedTuple):
|
||||
"""
|
||||
A named tuple containing the matched string and its :class:`EmojiMatch` object if it is an emoji
|
||||
or a single character that is not a unicode emoji.
|
||||
"""
|
||||
|
||||
chars: str
|
||||
value: Union[str, EmojiMatch]
|
||||
|
||||
|
||||
def tokenize(string: str, keep_zwj: bool) -> Iterator[Token]:
|
||||
"""
|
||||
Finds unicode emoji in a string. Yields all normal characters as a named
|
||||
tuple :class:`Token` ``(char, char)`` and all emoji as :class:`Token` ``(chars, EmojiMatch)``.
|
||||
|
||||
:param string: String contains unicode characters. MUST BE UNICODE.
|
||||
:param keep_zwj: Should ZWJ-characters (``\\u200D``) that join non-RGI emoji be
|
||||
skipped or should be yielded as normal characters
|
||||
:return: An iterable of tuples :class:`Token` ``(char, char)`` or :class:`Token` ``(chars, EmojiMatch)``
|
||||
"""
|
||||
|
||||
tree = get_search_tree()
|
||||
EMOJI_DATA = unicode_codes.EMOJI_DATA
|
||||
# result: [ Token(oldsubstring0, EmojiMatch), Token(char1, char1), ... ]
|
||||
result: List[Token] = []
|
||||
i = 0
|
||||
length = len(string)
|
||||
ignore: List[
|
||||
int
|
||||
] = [] # index of chars in string that are skipped, i.e. the ZWJ-char in non-RGI-ZWJ-sequences
|
||||
while i < length:
|
||||
consumed = False
|
||||
char = string[i]
|
||||
if i in ignore:
|
||||
i += 1
|
||||
if char == _ZWJ and keep_zwj:
|
||||
result.append(Token(char, char))
|
||||
continue
|
||||
|
||||
elif char in tree:
|
||||
j = i + 1
|
||||
sub_tree = tree[char]
|
||||
while j < length and string[j] in sub_tree:
|
||||
if j in ignore:
|
||||
break
|
||||
sub_tree = sub_tree[string[j]]
|
||||
j += 1
|
||||
if 'data' in sub_tree:
|
||||
emj_data = sub_tree['data']
|
||||
code_points = string[i:j]
|
||||
|
||||
# We cannot yield the result here, we need to defer
|
||||
# the call until we are sure that the emoji is finished
|
||||
# i.e. we're not inside an ongoing ZWJ-sequence
|
||||
match_obj = EmojiMatch(code_points, i, j, emj_data)
|
||||
|
||||
i = j - 1
|
||||
consumed = True
|
||||
result.append(Token(code_points, match_obj))
|
||||
|
||||
elif (
|
||||
char == _ZWJ
|
||||
and result
|
||||
and result[-1].chars in EMOJI_DATA
|
||||
and i > 0
|
||||
and string[i - 1] in tree
|
||||
):
|
||||
# the current char is ZWJ and the last match was an emoji
|
||||
ignore.append(i)
|
||||
if (
|
||||
EMOJI_DATA[result[-1].chars]['status']
|
||||
== unicode_codes.STATUS['component']
|
||||
):
|
||||
# last match was a component, it could be ZWJ+EMOJI+COMPONENT
|
||||
# or ZWJ+COMPONENT
|
||||
i = i - sum(len(t.chars) for t in result[-2:])
|
||||
if string[i] == _ZWJ:
|
||||
# It's ZWJ+COMPONENT, move one back
|
||||
i += 1
|
||||
del result[-1]
|
||||
else:
|
||||
# It's ZWJ+EMOJI+COMPONENT, move two back
|
||||
del result[-2:]
|
||||
else:
|
||||
# last match result[-1] was a normal emoji, move cursor
|
||||
# before the emoji
|
||||
i = i - len(result[-1].chars)
|
||||
del result[-1]
|
||||
continue
|
||||
|
||||
elif result:
|
||||
yield from result
|
||||
result = []
|
||||
|
||||
if not consumed and char != '\ufe0e' and char != '\ufe0f':
|
||||
result.append(Token(char, char))
|
||||
i += 1
|
||||
|
||||
yield from result
|
||||
|
||||
|
||||
def filter_tokens(
|
||||
matches: Iterator[Token], emoji_only: bool, join_emoji: bool
|
||||
) -> Iterator[Token]:
|
||||
"""
|
||||
Filters the output of `tokenize()`
|
||||
|
||||
:param matches: An iterable of tuples of the form ``(match_str, result)``
|
||||
where ``result`` is either an EmojiMatch or a string.
|
||||
:param emoji_only: If True, only EmojiMatch are returned in the output.
|
||||
If False all characters are returned
|
||||
:param join_emoji: If True, multiple EmojiMatch are merged into
|
||||
a single :class:`EmojiMatchZWJNonRGI` if they are separated only by a ZWJ.
|
||||
|
||||
:return: An iterable of tuples :class:`Token` ``(char, char)``,
|
||||
:class:`Token` ``(chars, EmojiMatch)`` or :class:`Token` ``(chars, EmojiMatchZWJNonRGI)``
|
||||
"""
|
||||
|
||||
if not join_emoji and not emoji_only:
|
||||
yield from matches
|
||||
return
|
||||
|
||||
if not join_emoji:
|
||||
for token in matches:
|
||||
if token.chars != _ZWJ:
|
||||
yield token
|
||||
return
|
||||
|
||||
# Combine multiple EmojiMatch that are separated by ZWJs into
|
||||
# a single EmojiMatchZWJNonRGI
|
||||
previous_is_emoji = False
|
||||
previous_is_zwj = False
|
||||
pre_previous_is_emoji = False
|
||||
accumulator: List[Token] = []
|
||||
for token in matches:
|
||||
pre_previous_is_emoji = previous_is_emoji
|
||||
if previous_is_emoji and token.value == _ZWJ:
|
||||
previous_is_zwj = True
|
||||
elif isinstance(token.value, EmojiMatch):
|
||||
if pre_previous_is_emoji and previous_is_zwj:
|
||||
if isinstance(accumulator[-1].value, EmojiMatchZWJNonRGI):
|
||||
accumulator[-1].value._add(token.value) # pyright: ignore [reportPrivateUsage]
|
||||
accumulator[-1] = Token(
|
||||
accumulator[-1].chars + _ZWJ + token.chars,
|
||||
accumulator[-1].value,
|
||||
)
|
||||
else:
|
||||
prev = accumulator.pop()
|
||||
assert isinstance(prev.value, EmojiMatch)
|
||||
accumulator.append(
|
||||
Token(
|
||||
prev.chars + _ZWJ + token.chars,
|
||||
EmojiMatchZWJNonRGI(prev.value, token.value),
|
||||
)
|
||||
)
|
||||
else:
|
||||
accumulator.append(token)
|
||||
previous_is_emoji = True
|
||||
previous_is_zwj = False
|
||||
else:
|
||||
# Other character, not an emoji
|
||||
previous_is_emoji = False
|
||||
previous_is_zwj = False
|
||||
yield from accumulator
|
||||
if not emoji_only:
|
||||
yield token
|
||||
accumulator = []
|
||||
yield from accumulator
|
||||
|
||||
|
||||
def get_search_tree() -> Dict[str, Any]:
|
||||
"""
|
||||
Generate a search tree for demojize().
|
||||
Example of a search tree::
|
||||
|
||||
EMOJI_DATA =
|
||||
{'a': {'en': ':Apple:'},
|
||||
'b': {'en': ':Bus:'},
|
||||
'ba': {'en': ':Bat:'},
|
||||
'band': {'en': ':Beatles:'},
|
||||
'bandit': {'en': ':Outlaw:'},
|
||||
'bank': {'en': ':BankOfEngland:'},
|
||||
'bb': {'en': ':BB-gun:'},
|
||||
'c': {'en': ':Car:'}}
|
||||
|
||||
_SEARCH_TREE =
|
||||
{'a': {'data': {'en': ':Apple:'}},
|
||||
'b': {'a': {'data': {'en': ':Bat:'},
|
||||
'n': {'d': {'data': {'en': ':Beatles:'},
|
||||
'i': {'t': {'data': {'en': ':Outlaw:'}}}},
|
||||
'k': {'data': {'en': ':BankOfEngland:'}}}},
|
||||
'b': {'data': {'en': ':BB-gun:'}},
|
||||
'data': {'en': ':Bus:'}},
|
||||
'c': {'data': {'en': ':Car:'}}}
|
||||
|
||||
_SEARCH_TREE
|
||||
/ | ⧵
|
||||
/ | ⧵
|
||||
a b c
|
||||
| / | ⧵ |
|
||||
| / | ⧵ |
|
||||
:Apple: ba :Bus: bb :Car:
|
||||
/ ⧵ |
|
||||
/ ⧵ |
|
||||
:Bat: ban :BB-gun:
|
||||
/ ⧵
|
||||
/ ⧵
|
||||
band bank
|
||||
/ ⧵ |
|
||||
/ ⧵ |
|
||||
bandi :Beatles: :BankOfEngland:
|
||||
|
|
||||
bandit
|
||||
|
|
||||
:Outlaw:
|
||||
|
||||
|
||||
"""
|
||||
if not _SEARCH_TREE:
|
||||
for emj in unicode_codes.EMOJI_DATA:
|
||||
sub_tree = _SEARCH_TREE
|
||||
lastidx = len(emj) - 1
|
||||
for i, char in enumerate(emj):
|
||||
if char not in sub_tree:
|
||||
sub_tree[char] = {}
|
||||
sub_tree = sub_tree[char]
|
||||
if i == lastidx:
|
||||
sub_tree['data'] = unicode_codes.EMOJI_DATA[emj]
|
||||
return _SEARCH_TREE
|
||||
@@ -0,0 +1,111 @@
|
||||
import sys
|
||||
import importlib.resources
|
||||
import json
|
||||
from functools import lru_cache
|
||||
from warnings import warn
|
||||
|
||||
from typing import IO, Any, Dict, Optional, Set
|
||||
|
||||
from emoji.unicode_codes.data_dict import STATUS, LANGUAGES
|
||||
|
||||
__all__ = [
|
||||
'get_emoji_by_name',
|
||||
'load_from_json',
|
||||
'EMOJI_DATA',
|
||||
'STATUS',
|
||||
'LANGUAGES',
|
||||
]
|
||||
|
||||
_DEFAULT_KEYS = ('en', 'alias', 'E', 'status') # The keys in emoji.json
|
||||
|
||||
_loaded_keys: Set[str] = set(
|
||||
_DEFAULT_KEYS
|
||||
) # Keep track of keys already loaded from json files to avoid loading them twice
|
||||
|
||||
|
||||
@lru_cache(maxsize=4000)
|
||||
def get_emoji_by_name(name: str, language: str) -> Optional[str]:
|
||||
"""
|
||||
Find emoji by short-name in a specific language.
|
||||
Returns None if not found
|
||||
|
||||
:param name: emoji short code e.g. ":banana:"
|
||||
:param language: language-code e.g. 'es', 'de', etc. or 'alias'
|
||||
"""
|
||||
|
||||
fully_qualified = STATUS['fully_qualified']
|
||||
|
||||
if language == 'alias':
|
||||
for emj, data in EMOJI_DATA.items():
|
||||
if name in data.get('alias', []) and data['status'] <= fully_qualified:
|
||||
return emj
|
||||
language = 'en'
|
||||
|
||||
for emj, data in EMOJI_DATA.items():
|
||||
if data.get(language) == name and data['status'] <= fully_qualified:
|
||||
return emj
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class EmojiDataDict(Dict[str, Any]):
|
||||
"""Replaces built-in-dict in the values of the EMOJI_DATA dict.
|
||||
Auto loads language data when accessing language data via
|
||||
key-access without prior loading of the language:
|
||||
e.g. EMOJI_DATA['👌']['fr'] will auto load French language and not throw
|
||||
a KeyError.
|
||||
Shows a deprecation warning explainging that `emoji.config.load_language()`
|
||||
should be used."""
|
||||
|
||||
def __missing__(self, key: str) -> str:
|
||||
"""Auto load language `key`, raises KeyError if language is no supported."""
|
||||
if key in LANGUAGES and key not in _loaded_keys:
|
||||
load_from_json(key)
|
||||
if key in self:
|
||||
warn(
|
||||
f"""Use emoji.config.load_language('{key}') before accesing EMOJI_DATA[emj]['{key}'].
|
||||
Accessing EMOJI_DATA[emj]['{key}'] without loading the language is deprecated.""",
|
||||
DeprecationWarning,
|
||||
stacklevel=3,
|
||||
)
|
||||
return self[key] # type: ignore
|
||||
|
||||
raise KeyError(key)
|
||||
|
||||
|
||||
EMOJI_DATA: Dict[str, Dict[str, Any]]
|
||||
|
||||
|
||||
def _open_file(name: str) -> IO[bytes]:
|
||||
if sys.version_info >= (3, 9):
|
||||
return importlib.resources.files('emoji.unicode_codes').joinpath(name).open('rb')
|
||||
else:
|
||||
return importlib.resources.open_binary('emoji.unicode_codes', name)
|
||||
|
||||
|
||||
def _load_default_from_json():
|
||||
global EMOJI_DATA
|
||||
global _loaded_keys
|
||||
|
||||
with _open_file('emoji.json') as f:
|
||||
EMOJI_DATA = dict(json.load(f, object_pairs_hook=EmojiDataDict)) # type: ignore
|
||||
_loaded_keys = set(_DEFAULT_KEYS)
|
||||
|
||||
|
||||
def load_from_json(key: str):
|
||||
"""Load values from the file 'emoji_{key}.json' into EMOJI_DATA"""
|
||||
|
||||
if key in _loaded_keys:
|
||||
return
|
||||
|
||||
if key not in LANGUAGES:
|
||||
raise NotImplementedError('Language not supported', key)
|
||||
|
||||
with _open_file(f'emoji_{key}.json') as f:
|
||||
for emj, value in json.load(f).items():
|
||||
EMOJI_DATA[emj][key] = value # type: ignore
|
||||
|
||||
_loaded_keys.add(key)
|
||||
|
||||
|
||||
_load_default_from_json()
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,276 @@
|
||||
"""Data containing all current emoji
|
||||
Extracted from https://unicode.org/Public/emoji/latest/emoji-test.txt
|
||||
and https://www.unicode.org/Public/UCD/latest/ucd/emoji/emoji-variation-sequences.txt
|
||||
See utils/generate_emoji.py
|
||||
|
||||
+----------------+-------------+------------------+-------------------+
|
||||
| Emoji Version | Date | Unicode Version | Data File Comment |
|
||||
+----------------+-------------+------------------+-------------------+
|
||||
| N/A | 2010-10-11 | Unicode 6.0 | E0.6 |
|
||||
| N/A | 2014-06-16 | Unicode 7.0 | E0.7 |
|
||||
| Emoji 1.0 | 2015-06-09 | Unicode 8.0 | E1.0 |
|
||||
| Emoji 2.0 | 2015-11-12 | Unicode 8.0 | E2.0 |
|
||||
| Emoji 3.0 | 2016-06-03 | Unicode 9.0 | E3.0 |
|
||||
| Emoji 4.0 | 2016-11-22 | Unicode 9.0 | E4.0 |
|
||||
| Emoji 5.0 | 2017-06-20 | Unicode 10.0 | E5.0 |
|
||||
| Emoji 11.0 | 2018-05-21 | Unicode 11.0 | E11.0 |
|
||||
| Emoji 12.0 | 2019-03-05 | Unicode 12.0 | E12.0 |
|
||||
| Emoji 12.1 | 2019-10-21 | Unicode 12.1 | E12.1 |
|
||||
| Emoji 13.0 | 2020-03-10 | Unicode 13.0 | E13.0 |
|
||||
| Emoji 13.1 | 2020-09-15 | Unicode 13.0 | E13.1 |
|
||||
| Emoji 14.0 | 2021-09-14 | Unicode 14.0 | E14.0 |
|
||||
| Emoji 15.0 | 2022-09-13 | Unicode 15.0 | E15.0 |
|
||||
| Emoji 15.1 | 2023-09-12 | Unicode 15.1 | E15.1 |
|
||||
| Emoji 16.0 | 2024-09-10 | Unicode 16.0 | E16.0 |
|
||||
|
||||
http://www.unicode.org/reports/tr51/#Versioning
|
||||
|
||||
"""
|
||||
|
||||
__all__ = ['STATUS', 'LANGUAGES']
|
||||
|
||||
from typing import Any, Dict, List
|
||||
|
||||
|
||||
component = 1
|
||||
fully_qualified = 2
|
||||
minimally_qualified = 3
|
||||
unqualified = 4
|
||||
|
||||
STATUS: Dict[str, int] = {
|
||||
'component': component,
|
||||
'fully_qualified': fully_qualified,
|
||||
'minimally_qualified': minimally_qualified,
|
||||
'unqualified': unqualified,
|
||||
}
|
||||
|
||||
LANGUAGES: List[str] = [
|
||||
'en',
|
||||
'es',
|
||||
'ja',
|
||||
'ko',
|
||||
'pt',
|
||||
'it',
|
||||
'fr',
|
||||
'de',
|
||||
'fa',
|
||||
'id',
|
||||
'zh',
|
||||
'ru',
|
||||
'tr',
|
||||
'ar',
|
||||
]
|
||||
|
||||
|
||||
# The following is only an example of how the EMOJI_DATA dict is structured.
|
||||
# The real data is loaded from the json files at runtime, see unicode_codes/__init__.py
|
||||
EMOJI_DATA: Dict[str, Dict[str, Any]] = {
|
||||
'\U0001f947': { # 🥇
|
||||
'en': ':1st_place_medal:',
|
||||
'status': fully_qualified,
|
||||
'E': 3,
|
||||
'de': ':goldmedaille:',
|
||||
'es': ':medalla_de_oro:',
|
||||
'fr': ':médaille_d’or:',
|
||||
'ja': ':金メダル:',
|
||||
'ko': ':금메달:',
|
||||
'pt': ':medalha_de_ouro:',
|
||||
'it': ':medaglia_d’oro:',
|
||||
'fa': ':مدال_طلا:',
|
||||
'id': ':medali_emas:',
|
||||
'zh': ':金牌:',
|
||||
'ru': ':золотая_медаль:',
|
||||
'tr': ':birincilik_madalyası:',
|
||||
'ar': ':ميدالية_مركز_أول:',
|
||||
},
|
||||
'\U0001f948': { # 🥈
|
||||
'en': ':2nd_place_medal:',
|
||||
'status': fully_qualified,
|
||||
'E': 3,
|
||||
'de': ':silbermedaille:',
|
||||
'es': ':medalla_de_plata:',
|
||||
'fr': ':médaille_d’argent:',
|
||||
'ja': ':銀メダル:',
|
||||
'ko': ':은메달:',
|
||||
'pt': ':medalha_de_prata:',
|
||||
'it': ':medaglia_d’argento:',
|
||||
'fa': ':مدال_نقره:',
|
||||
'id': ':medali_perak:',
|
||||
'zh': ':银牌:',
|
||||
'ru': ':серебряная_медаль:',
|
||||
'tr': ':ikincilik_madalyası:',
|
||||
'ar': ':ميدالية_مركز_ثان:',
|
||||
},
|
||||
'\U0001f949': { # 🥉
|
||||
'en': ':3rd_place_medal:',
|
||||
'status': fully_qualified,
|
||||
'E': 3,
|
||||
'de': ':bronzemedaille:',
|
||||
'es': ':medalla_de_bronce:',
|
||||
'fr': ':médaille_de_bronze:',
|
||||
'ja': ':銅メダル:',
|
||||
'ko': ':동메달:',
|
||||
'pt': ':medalha_de_bronze:',
|
||||
'it': ':medaglia_di_bronzo:',
|
||||
'fa': ':مدال_برنز:',
|
||||
'id': ':medali_perunggu:',
|
||||
'zh': ':铜牌:',
|
||||
'ru': ':бронзовая_медаль:',
|
||||
'tr': ':üçüncülük_madalyası:',
|
||||
'ar': ':ميدالية_مركز_ثالث:',
|
||||
},
|
||||
'\U0001f18e': { # 🆎
|
||||
'en': ':AB_button_(blood_type):',
|
||||
'status': fully_qualified,
|
||||
'E': 0.6,
|
||||
'alias': [':ab:', ':ab_button_blood_type:'],
|
||||
'de': ':großbuchstaben_ab_in_rotem_quadrat:',
|
||||
'es': ':grupo_sanguíneo_ab:',
|
||||
'fr': ':groupe_sanguin_ab:',
|
||||
'ja': ':血液型ab型:',
|
||||
'ko': ':에이비형:',
|
||||
'pt': ':botão_ab_(tipo_sanguíneo):',
|
||||
'it': ':gruppo_sanguigno_ab:',
|
||||
'fa': ':دکمه_آ_ب_(گروه_خونی):',
|
||||
'id': ':tombol_ab_(golongan_darah):',
|
||||
'zh': ':AB型血:',
|
||||
'ru': ':IV_группа_крови:',
|
||||
'tr': ':ab_düğmesi_(kan_grubu):',
|
||||
'ar': ':زر_ab_(فئة_الدم):',
|
||||
},
|
||||
'\U0001f3e7': { # 🏧
|
||||
'en': ':ATM_sign:',
|
||||
'status': fully_qualified,
|
||||
'E': 0.6,
|
||||
'alias': [':atm:', ':atm_sign:'],
|
||||
'de': ':symbol_geldautomat:',
|
||||
'es': ':señal_de_cajero_automático:',
|
||||
'fr': ':distributeur_de_billets:',
|
||||
'ja': ':atm:',
|
||||
'ko': ':에이티엠:',
|
||||
'pt': ':símbolo_de_caixa_automático:',
|
||||
'it': ':simbolo_dello_sportello_bancomat:',
|
||||
'fa': ':نشان_عابربانک:',
|
||||
'id': ':tanda_atm:',
|
||||
'zh': ':取款机:',
|
||||
'ru': ':значок_банкомата:',
|
||||
'tr': ':atm_işareti:',
|
||||
'ar': ':علامة_ماكينة_صرف_آلي:',
|
||||
},
|
||||
'\U0001f170\U0000fe0f': { # 🅰️
|
||||
'en': ':A_button_(blood_type):',
|
||||
'status': fully_qualified,
|
||||
'E': 0.6,
|
||||
'alias': [':a:', ':a_button_blood_type:'],
|
||||
'variant': True,
|
||||
'de': ':großbuchstabe_a_in_rotem_quadrat:',
|
||||
'es': ':grupo_sanguíneo_a:',
|
||||
'fr': ':groupe_sanguin_a:',
|
||||
'ja': ':血液型a型:',
|
||||
'ko': ':에이형:',
|
||||
'pt': ':botão_a_(tipo_sanguíneo):',
|
||||
'it': ':gruppo_sanguigno_a:',
|
||||
'fa': ':دکمه_آ_(گروه_خونی):',
|
||||
'id': ':tombol_a_(golongan_darah):',
|
||||
'zh': ':A型血:',
|
||||
'ru': ':ii_группа_крови:',
|
||||
'tr': ':a_düğmesi_(kan_grubu):',
|
||||
'ar': ':زر_a:',
|
||||
},
|
||||
'\U0001f170': { # 🅰
|
||||
'en': ':A_button_(blood_type):',
|
||||
'status': unqualified,
|
||||
'E': 0.6,
|
||||
'alias': [':a:', ':a_button_blood_type:'],
|
||||
'variant': True,
|
||||
'de': ':großbuchstabe_a_in_rotem_quadrat:',
|
||||
'es': ':grupo_sanguíneo_a:',
|
||||
'fr': ':groupe_sanguin_a:',
|
||||
'ja': ':血液型a型:',
|
||||
'ko': ':에이형:',
|
||||
'pt': ':botão_a_(tipo_sanguíneo):',
|
||||
'it': ':gruppo_sanguigno_a:',
|
||||
'fa': ':دکمه_آ_(گروه_خونی):',
|
||||
'id': ':tombol_a_(golongan_darah):',
|
||||
'zh': ':A型血:',
|
||||
'ru': ':II_группа_крови:',
|
||||
'tr': ':a_düğmesi_(kan_grubu):',
|
||||
'ar': ':زر_a:',
|
||||
},
|
||||
'\U0001f1e6\U0001f1eb': { # 🇦🇫
|
||||
'en': ':Afghanistan:',
|
||||
'status': fully_qualified,
|
||||
'E': 2,
|
||||
'alias': [':flag_for_Afghanistan:', ':afghanistan:'],
|
||||
'de': ':flagge_afghanistan:',
|
||||
'es': ':bandera_afganistán:',
|
||||
'fr': ':drapeau_afghanistan:',
|
||||
'ja': ':旗_アフガニスタン:',
|
||||
'ko': ':깃발_아프가니스탄:',
|
||||
'pt': ':bandeira_afeganistão:',
|
||||
'it': ':bandiera_afghanistan:',
|
||||
'fa': ':پرچم_افغانستان:',
|
||||
'id': ':bendera_afganistan:',
|
||||
'zh': ':阿富汗:',
|
||||
'ru': ':флаг_Афганистан:',
|
||||
'tr': ':bayrak_afganistan:',
|
||||
'ar': ':علم_أفغانستان:',
|
||||
},
|
||||
'\U0001f1e6\U0001f1f1': { # 🇦🇱
|
||||
'en': ':Albania:',
|
||||
'status': fully_qualified,
|
||||
'E': 2,
|
||||
'alias': [':flag_for_Albania:', ':albania:'],
|
||||
'de': ':flagge_albanien:',
|
||||
'es': ':bandera_albania:',
|
||||
'fr': ':drapeau_albanie:',
|
||||
'ja': ':旗_アルバニア:',
|
||||
'ko': ':깃발_알바니아:',
|
||||
'pt': ':bandeira_albânia:',
|
||||
'it': ':bandiera_albania:',
|
||||
'fa': ':پرچم_آلبانی:',
|
||||
'id': ':bendera_albania:',
|
||||
'zh': ':阿尔巴尼亚:',
|
||||
'ru': ':флаг_Албания:',
|
||||
'tr': ':bayrak_arnavutluk:',
|
||||
'ar': ':علم_ألبانيا:',
|
||||
},
|
||||
'\U0001f1e9\U0001f1ff': { # 🇩🇿
|
||||
'en': ':Algeria:',
|
||||
'status': fully_qualified,
|
||||
'E': 2,
|
||||
'alias': [':flag_for_Algeria:', ':algeria:'],
|
||||
'de': ':flagge_algerien:',
|
||||
'es': ':bandera_argelia:',
|
||||
'fr': ':drapeau_algérie:',
|
||||
'ja': ':旗_アルジェリア:',
|
||||
'ko': ':깃발_알제리:',
|
||||
'pt': ':bandeira_argélia:',
|
||||
'it': ':bandiera_algeria:',
|
||||
'fa': ':پرچم_الجزایر:',
|
||||
'id': ':bendera_aljazair:',
|
||||
'zh': ':阿尔及利亚:',
|
||||
'ru': ':флаг_Алжир:',
|
||||
'tr': ':bayrak_cezayir:',
|
||||
'ar': ':علم_الجزائر:',
|
||||
},
|
||||
'\U0001f1e6\U0001f1f8': { # 🇦🇸
|
||||
'en': ':American_Samoa:',
|
||||
'status': fully_qualified,
|
||||
'E': 2,
|
||||
'alias': [':flag_for_American_Samoa:', ':american_samoa:'],
|
||||
'de': ':flagge_amerikanisch-samoa:',
|
||||
'es': ':bandera_samoa_americana:',
|
||||
'fr': ':drapeau_samoa_américaines:',
|
||||
'ja': ':旗_米領サモア:',
|
||||
'ko': ':깃발_아메리칸_사모아:',
|
||||
'pt': ':bandeira_samoa_americana:',
|
||||
'it': ':bandiera_samoa_americane:',
|
||||
'fa': ':پرچم_ساموآی_امریکا:',
|
||||
'id': ':bendera_samoa_amerika:',
|
||||
'zh': ':美属萨摩亚:',
|
||||
'ru': ':флаг_Американское_Самоа:',
|
||||
'tr': ':bayrak_amerikan_samoası:',
|
||||
'ar': ':علم_ساموا_الأمريكية:',
|
||||
},
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user