Comparing sensitive data, confidential files or internal emails?

Most legal and privacy policies prohibit uploading sensitive data online. Diffchecker Desktop ensures your confidential information never leaves your computer. Work offline and compare documents securely.

Untitled Diff

Created Diff never expires
2 removals
327 lines
15 additions
340 lines
# ../translations/strings.py
# ../translations/strings.py


"""Provides translation functionality."""
"""Provides translation functionality."""


# =============================================================================
# =============================================================================
# >> IMPORTS
# >> IMPORTS
# =============================================================================
# =============================================================================
# Python Imports
# Python Imports
# Binascii
# Binascii
from binascii import unhexlify
from binascii import unhexlify
# Codecs
# Codecs
from codecs import unicode_escape_decode
from codecs import unicode_escape_decode
# Re
# Re
from re import compile as re_compile
from re import compile as re_compile
from re import VERBOSE
from re import VERBOSE


# Site-Package Imports
# Site-Package Imports
# Configobj
# Configobj
from configobj import Section
from configobj import Section


# Source.Python Imports
# Source.Python Imports
# Core
# Core
from core import GameConfigObj
from core import GameConfigObj
# Paths
# Paths
from paths import TRANSLATION_PATH
from paths import TRANSLATION_PATH
from paths import GAME_PATH
from paths import GAME_PATH
# Translations
# Translations
from translations.manager import language_manager
from translations.manager import language_manager




# =============================================================================
# =============================================================================
# >> ALL DECLARATION
# >> ALL DECLARATION
# =============================================================================
# =============================================================================
__all__ = ('LangStrings',
__all__ = ('LangStrings',
'TranslationStrings',
'TranslationStrings',
)
)




# =============================================================================
# =============================================================================
# >> GLOBAL VARIABLES
# >> GLOBAL VARIABLES
# =============================================================================
# =============================================================================
# Get an re.compile instance to correct all double escaped strings
# Get an re.compile instance to correct all double escaped strings
_double_escaped_pattern = re_compile(
_double_escaped_pattern = re_compile(
r"""(\\(?:(?P<octal>[0-7]{1,3})|x(?P<hexadecimal>[0-9|a-f|A-F]{2})|
r"""(\\(?:(?P<octal>[0-7]{1,3})|x(?P<hexadecimal>[0-9|a-f|A-F]{2})|
(?P<notation>a|b|e|f|n|r|s|t|v)))""", VERBOSE)
(?P<notation>a|b|e|f|n|r|s|t|v)))""", VERBOSE)




# =============================================================================
# =============================================================================
# >> CLASSES
# >> CLASSES
# =============================================================================
# =============================================================================
class LangStrings(dict):
class LangStrings(dict):
"""Dictionary class used to store all strings for a plugin."""
"""Dictionary class used to store all strings for a plugin."""


def __init__(self, infile, encoding='utf_8'):
def __init__(self, infile, encoding='utf_8'):
"""Add all strings and fix double escaped strings."""
"""Add all strings and fix double escaped strings."""
# Initialize the dictionary
# Initialize the dictionary
super().__init__()
super().__init__()


# Get the path to the given file
# Get the path to the given file
self._mainfile = TRANSLATION_PATH / infile + '.ini'
self._mainfile = TRANSLATION_PATH / infile + '.ini'
self._encoding = encoding
self._encoding = encoding


# Does the file exist?
# Does the file exist?
if not self._mainfile.isfile():
if not self._mainfile.isfile():


# Raise an error
# Raise an error
raise FileNotFoundError(
raise FileNotFoundError(
'No file found at {0}'.format(self._mainfile))
'No file found at {0}'.format(self._mainfile))


# Get the path to the server specific file
# Get the path to the server specific file
self._serverfile = self._mainfile.parent / '{0}_server.ini'.format(
self._serverfile = self._mainfile.parent / '{0}_server.ini'.format(
self._mainfile.namebase)
self._mainfile.namebase)


# Get the strings from the main file
# Get the strings from the main file
main_strings = GameConfigObj(self._mainfile, encoding=encoding)
main_strings = GameConfigObj(self._mainfile, encoding=encoding)


# Does the server specific file exist?
# Does the server specific file exist?
if not self._serverfile.isfile() and not infile.startswith('_core/'):
if not self._serverfile.isfile() and not infile.startswith('_core/'):


# Create the server specific file
# Create the server specific file
self._create_server_file()
self._create_server_file()


# Otherwise
# Otherwise
else:
else:


# Get any strings from the server specific file
# Get any strings from the server specific file
server_strings = GameConfigObj(self._serverfile, encoding=encoding)
server_strings = GameConfigObj(self._serverfile, encoding=encoding)


# Merge the two ConfigObj instances together
# Merge the two ConfigObj instances together
main_strings.merge(server_strings)
main_strings.merge(server_strings)


# Loop through all strings
# Loop through all strings
for key in main_strings:
for key in main_strings:


# Is the current string not a Section?
# Is the current string not a Section?
if not isinstance(main_strings[key], Section):
if not isinstance(main_strings[key], Section):


# No need to go further
# No need to go further
continue
continue


# Get a TranslationStrings instance for the current string
# Get a TranslationStrings instance for the current string
translation_strings = TranslationStrings()
translation_strings = TranslationStrings()


# Loop through all languages for the current string
# Loop through all languages for the current string
for lang in main_strings[key]:
for lang in main_strings[key]:


# Get the shortname of the current language
# Get the shortname of the current language
language = language_manager.get_language(lang)
language = language_manager.get_language(lang)


# Was the language found?
# Was the language found?
if language is None:
if language is None:


# Do not add this translation
# Do not add this translation
# Possibly raise an error silently here
# Possibly raise an error silently here
continue
continue


# Get the language's string and fix any escaped strings
# Get the language's string and fix any escaped strings
translation_strings[
translation_strings[
language] = self._replace_escaped_sequences(
language] = self._replace_escaped_sequences(
main_strings[key][lang])
main_strings[key][lang])


# Add the TranslationStrings instance for the current string
# Add the TranslationStrings instance for the current string
self[key] = translation_strings
self[key] = translation_strings


# Is there any default language specified into the main file?
# Is there any default language specified into the main file?
if 'DEFAULT_LANGUAGE' in main_strings:
if 'DEFAULT_LANGUAGE' in main_strings:


# Get the default language
# Get the default language
default_language = main_strings['DEFAULT_LANGUAGE']
default_language = main_strings['DEFAULT_LANGUAGE']


# Make sure it is not a Section
# Make sure it is not a Section
if not isinstance(default_language, Section):
if not isinstance(default_language, Section):


# Get the given language code
# Get the given language code
language_code = language_manager.get_language(default_language)
language_code = language_manager.get_language(default_language)


# Is the language valid?
# Is the language valid?
if language_code is not None:
if language_code is not None:


# Set the default language
# Set the default language
self.default_language = language_code
self.default_language = language_code


# Delete the key from the main file as we are done with it
# Delete the key from the main file as we are done with it
del main_strings['DEFAULT_LANGUAGE']
del main_strings['DEFAULT_LANGUAGE']


else:
default_language = language_manager.default

self.default_language = default_language

def __setattr__(self, attribute, value):
def __setattr__(self, attribute, value):
"""Register the default language."""
"""Register the default language."""
# Is the given attribute the default language?
# Is the given attribute the default language?
if attribute == 'default_language':
if attribute == 'default_language':


# Get the given language code
# Get the given language code
language_code = language_manager.get_language(value)
language_code = language_manager.get_language(value)


# Is the given language code valid?
# Is the given language code valid?
if language_code is not None:
if language_code is not None:


# Loop through all strings
# Loop through all strings
for key in self:
for key in self:


# Set the default language to use for that string
# Set the default language to use for that string
self[key]._default_language = language_code
self[key]._default_language = language_code


# Override the given value
# Override the given value
value = language_code
value = language_code


# Set the attribute
# Set the attribute
super().__setattr__(attribute, value)
super().__setattr__(attribute, value)


def _create_server_file(self):
def _create_server_file(self):
"""Create a server specific langstrings file."""
"""Create a server specific langstrings file."""
# Get the server specific file's ConfigObj instance
# Get the server specific file's ConfigObj instance
server_file = GameConfigObj(self._serverfile, encoding=self._encoding)
server_file = GameConfigObj(self._serverfile, encoding=self._encoding)


# Set the initial comments to explain what the file is for
# Set the initial comments to explain what the file is for
server_file.initial_comment = _translation_strings[
server_file.initial_comment = _translation_strings[
'Initial Comment'].get_string(
'Initial Comment'].get_string(
language_manager.default,
language_manager.default,
filename=self._mainfile.replace(GAME_PATH, '')).splitlines()
filename=self._mainfile.replace(GAME_PATH, '')).splitlines()


# Write the server specific file
# Write the server specific file
server_file.write()
server_file.write()


@staticmethod
@staticmethod
def _replace_escaped_sequences(given_string):
def _replace_escaped_sequences(given_string):
"""Fix all double escaped strings."""
"""Fix all double escaped strings."""
# Loop through all matches
# Loop through all matches
for escaped_match in set(
for escaped_match in set(
_double_escaped_pattern.finditer(given_string)):
_double_escaped_pattern.finditer(given_string)):


# Get the match as a string
# Get the match as a string
matching_string = escaped_match.group()
matching_string = escaped_match.group()


# Get a dictionnary of all groups
# Get a dictionnary of all groups
matching_groups = escaped_match.groupdict()
matching_groups = escaped_match.groupdict()


# Are we matching any octal sequences?
# Are we matching any octal sequences?
if matching_groups['octal']:
if matching_groups['octal']:


# Replace it
# Replace it
given_string = given_string.replace(
given_string = given_string.replace(
matching_string, chr(int(matching_groups['octal'])))
matching_string, chr(int(matching_groups['octal'])))


# Otherwise, are we matching any hexadecimal sequences?
# Otherwise, are we matching any hexadecimal sequences?
elif matching_groups['hexadecimal']:
elif matching_groups['hexadecimal']:


# Replace it
# Replace it
given_string = given_string.replace(
given_string = given_string.replace(
matching_string, str(unhexlify(
matching_string, str(unhexlify(
matching_groups['hexadecimal']), encoding='ascii'))
matching_groups['hexadecimal']), encoding='ascii'))


# Otherwise, that means we are matching a notation
# Otherwise, that means we are matching a notation
else:
else:


# Replace it
# Replace it
given_string = given_string.replace(
given_string = given_string.replace(
matching_string, unicode_escape_decode(matching_string)[0])
matching_string, unicode_escape_decode(matching_string)[0])


# Return the replaced string
# Return the replaced string
return given_string
return given_string


def get_strings(self, key, **tokens):
def get_strings(self, key, **tokens):
"""Return a TranslationStrings object with updated tokens."""
"""Return a TranslationStrings object with updated tokens."""
strings = self[key]
strings = self[key]
strings.tokens.update(tokens)
strings.tokens.update(tokens)
return strings
return strings


def make_strings(self, string, **tokens):
s = TranslationStrings()
s[self.default_language] = string
s.tokens.update(tokens)
return s



class TranslationStrings(dict):
class TranslationStrings(dict):
"""Stores and get language strings for a particular string."""
"""Stores and get language strings for a particular string."""


def __init__(self):
def __init__(self):
"""Store an empty dictionary as the tokens."""
"""Store an empty dictionary as the tokens."""
super().__init__()
super().__init__()
self.tokens = {}
self.tokens = {}


def get_string(self, language=None, **tokens):
def get_string(self, language=None, **tokens):
"""Return the language string for the given language/tokens."""
"""Return the language string for the given language/tokens."""
requested_language = language

# Was no language passed?
# Was no language passed?
if language is None:
if language is None:


# Set the language to the server's default
# Set the language to the server's default
language = language_manager.default
language = language_manager.default


# Get the language shortname to be used
# Get the language shortname to be used
language = self.get_language(language)
language = self.get_language(language)


# Was a valid language found?
# Was a valid language found?
if language is None:
if language is None:


# Return an empty string
# Return an empty string
# Possibly raise an error silently here
# Possibly raise an error silently here
return ''
return ''


# Expose all TranslationStrings instances in self.tokens
# Expose all TranslationStrings instances in self.tokens
exposed_tokens = {}
exposed_tokens = {}


# Pass additional kwargs - these will be used to format the string
# Pass additional kwargs - these will be used to format the string
self._update_exposed_tokens(
self._update_exposed_tokens(
exposed_tokens, language, self.tokens, **tokens)
exposed_tokens, requested_language, self.tokens, **tokens)


# Don't pass any additional kwargs, each token should either
# Don't pass any additional kwargs, each token should either
# be trivial or rely on itself (self.tokens)
# be trivial or rely on itself (self.tokens)
self._update_exposed_tokens(exposed_tokens, language, tokens)
self._update_exposed_tokens(exposed_tokens, requested_language, tokens)


# Return the formatted message
# Return the formatted message
return self[language].format(**exposed_tokens)
return self[language].format(**exposed_tokens)


@staticmethod
@staticmethod
def _update_exposed_tokens(exposed_tokens, language, tokens, **kwargs):
def _update_exposed_tokens(exposed_tokens, language, tokens, **kwargs):
for token_name, token in tokens.items():
for token_name, token in tokens.items():
if isinstance(token, TranslationStrings):
if isinstance(token, TranslationStrings):
token = token.get_string(language, **kwargs)
token = token.get_string(language, **kwargs)


exposed_tokens[token_name] = token
exposed_tokens[token_name] = token


def get_language(self, language):
def get_language(self, language):
"""Return the language to be used."""
"""Return the language to be used."""
# Get the given language's shortname
# Get the given language's shortname
language = language_manager.get_language(language)
language = language_manager.get_language(language)


# Was a language found?
# Was a language found?
if language is not None and language in self:
if language is not None and language in self:


# Return the language
# Return the language
return language
return language


# Is the server's default language in the dictionary?
# Is the server's default language in the dictionary?
if language_manager.default in self:
if language_manager.default in self:


# Return the server's default language
# Return the server's default language
return language_manager.default
return language_manager.default


# Is there any default language defined?
# Is there any default language defined?
if hasattr(self, '_default_language'):
if hasattr(self, '_default_language'):


# Is the default language available for that string?
# Is the default language available for that string?
if self._default_language in self:
if self._default_language in self:


# Return the default language to use
# Return the default language to use
return self._default_language
return self._default_language


# Is the server's fallback language in the dictionary?
# Is the server's fallback language in the dictionary?
if language_manager.fallback in self:
if language_manager.fallback in self:


# Return the server's fallback language
# Return the server's fallback language
return language_manager.fallback
return language_manager.fallback


# Return None as the language, as no language has been found
# Return None as the language, as no language has been found
return None
return None


def tokenized(self, **tokens):
def tokenized(self, **tokens):
"""Create a new TranslationStrings instance and store tokens in it.
"""Create a new TranslationStrings instance and store tokens in it.


:param dict tokens: Tokens to store in the instance.
:param dict tokens: Tokens to store in the instance.
:return: New TranslationStrings instance with tokens stored in it.
:return: New TranslationStrings instance with tokens stored in it.
:rtype: TranslationStrings
:rtype: TranslationStrings
"""
"""
result = TranslationStrings()
result = TranslationStrings()
result.tokens.update(tokens)
result.tokens.update(tokens)


result.update(self)
result.update(self)


return result
return result


# Get the translations language strings
# Get the translations language strings
_translation_strings = LangStrings('_core/translations_strings')
_translation_strings = LangStrings('_core/translations_strings')