# -*- coding: utf-8 -*- import re import random from redbot.core.utils.chat_formatting import box def reverse_map(m): return {v: k for k, v in m.items()} class TranslationLogicMixin: """This class holds all the translation logic for the cog.""" def _escape_regex(self, s): return re.escape(s) def _generic_translator(self, text, lang_map, char_separator): # Regex to find custom emojis emoji_regex = re.compile(r"") # Find all emojis and store them emojis = emoji_regex.findall(text) # Replace emojis with a unique placeholder placeholder = "||EMOJI||" text_with_placeholders = emoji_regex.sub(placeholder, text) # Translate the text with placeholders word_separator = ' ' if char_separator == '' else ' ' words = text_with_placeholders.split(' ') translated_words = [] emoji_counter = 0 for word in words: if placeholder in word: # If a word contains a placeholder, it might be part of the emoji code that got split. # We simply re-insert the emoji from our list. translated_words.append(emojis[emoji_counter]) emoji_counter += 1 else: translated_word = char_separator.join([lang_map.get(char.lower(), char) for char in word]) translated_words.append(translated_word) return word_separator.join(translated_words) def _generic_decoder(self, text, reverse_map, chunk_size): result = "" text_no_space = text.replace(' ','') for i in range(0, len(text_no_space), chunk_size): chunk = text_no_space[i:i+chunk_size] result += reverse_map.get(chunk, '?') return result def _greedy_decoder(self, text, reverse_map): syllables = sorted(reverse_map.keys(), key=len, reverse=True) regex = re.compile('|'.join(map(self._escape_regex, syllables))) matches = regex.findall(text.replace(' ', '')) return "".join([reverse_map.get(m, '?') for m in matches]) def _leet_decoder(self, text, reverse_map): decoded_text = text sorted_keys = sorted(reverse_map.keys(), key=len, reverse=True) for key in sorted_keys: decoded_text = decoded_text.replace(key, reverse_map[key]) return decoded_text def _morse_decoder(self, text, reverse_map): words = text.split(' ') decoded_words = [] for word in words: chars = word.split(' ') decoded_words.append("".join([reverse_map.get(c, '?') for c in chars])) return " ".join(decoded_words) def _pig_latin_translator(self, text): vowels = "aeiou" translated_words = [] for word in text.split(' '): if not word: continue match = re.match(r"^([^a-zA-Z0-9]*)(.*?)([^a-zA-Z0-9]*)$", word) leading_punct, clean_word, trailing_punct = match.groups() if not clean_word: translated_words.append(word) continue if clean_word and clean_word[0].lower() in vowels: translated_word = clean_word + "way" else: first_vowel_index = -1 for i, char in enumerate(clean_word): if char.lower() in vowels: first_vowel_index = i break if first_vowel_index == -1: translated_word = clean_word + "ay" else: translated_word = clean_word[first_vowel_index:] + clean_word[:first_vowel_index] + "ay" translated_words.append(leading_punct + translated_word + trailing_punct) return " ".join(translated_words) def _pig_latin_decoder(self, text): decoded_words = [] for word in text.split(' '): if not word: continue match = re.match(r"^([^a-zA-Z0-9]*)(.*?)([^a-zA-Z0-9]*)$", word) leading_punct, clean_word, trailing_punct = match.groups() if not clean_word: decoded_words.append(word) continue original_word = "" if clean_word.lower().endswith("way"): original_word = clean_word[:-3] elif clean_word.lower().endswith("ay"): base_word = clean_word[:-2] last_consonant_block_index = -1 for i in range(len(base_word) - 1, -1, -1): if base_word[i].lower() not in "aeiou": last_consonant_block_index = i else: break if last_consonant_block_index != -1: while last_consonant_block_index > 0 and base_word[last_consonant_block_index-1].lower() not in "aeiou": last_consonant_block_index -= 1 consonants = base_word[last_consonant_block_index:] stem = base_word[:last_consonant_block_index] original_word = consonants + stem else: original_word = base_word else: original_word = clean_word decoded_words.append(leading_punct + original_word + trailing_punct) return " ".join(decoded_words) def _uwu_translator(self, text): text = text.lower() text = text.replace('l', 'ww') text = text.replace('r', 'w') text = text.replace('na', 'nya').replace('ne', 'nye').replace('ni', 'nyi').replace('no', 'nyo').replace('nu', 'nyu') text = text.replace('ove', 'uv') words = text.split(' ') uwu_words = [] for word in words: if len(word) > 3 and random.random() < 0.3: uwu_words.append(f"{word[0]}-{word}") else: uwu_words.append(word) text = " ".join(uwu_words) if random.random() < 0.5: emoticons = [' uwu', ' owo', ' >w<', ' ^-^', ' ;;'] text += random.choice(emoticons) return text def _uwu_decoder(self, text): emoticons = [' uwu', ' owo', ' >w<', ' ^-^', ' ;;'] for emo in emoticons: if text.endswith(emo): text = text[:-len(emo)] text = re.sub(r'(\w)-(\w+)', r'\2', text) text = text.replace('uv', 'ove') text = text.replace('nyu', 'nu').replace('nyo', 'no').replace('nyi', 'ni').replace('nye', 'ne').replace('nya', 'na') text = text.replace('ww', 'l') text = text.replace('w', 'r') return text async def _find_language(self, query: str): """Finds languages by key or name, case-insensitively.""" query = query.lower() exact_key_match = [key for key in self.all_languages if key.lower() == query] if exact_key_match: return exact_key_match exact_name_match = [key for key, lang in self.all_languages.items() if lang['name'].lower() == query] if exact_name_match: return exact_name_match partial_matches = [ key for key, lang in self.all_languages.items() if query in key.lower() or query in lang['name'].lower() ] return partial_matches async def _auto_translate_to_common(self, text: str) -> list: """Helper to find all possible translations for a given text.""" possible_translations = [] for lang_key, lang_obj in self.all_languages.items(): if lang_key == 'common': continue try: decoded_text = lang_obj['from_func'](text) re_encoded_text = lang_obj['to_func'](decoded_text) if re_encoded_text == text: possible_translations.append(f"**As {lang_obj['name']}:**\n{box(decoded_text)}") except Exception: continue return possible_translations