Features implemented by this package are now possible without it due to implementing .tokenized method directly into Source.Python: https://github.com/Source-Python-Dev-Team/Source.Python/commit/520cc3b8d282651cba9304020eb828776bb4b0ac
Note that this package used .tokenize method.
Source.Python Advanced TranslationStrings (custom package)
Download: https://github.com/KirillMysnik/sp-advanced-ts-package/releases/latest
The package includes 2 classes:
- BaseLangStrings - basically LangStrings class but after initialization replaces all TranslationStrings instances with the given base:
Syntax: Select all
class BaseLangStrings(LangStrings):
"""
This is a LangStrings class but after initialization replaces
all TranslationStrings values with instances of the given
dict-inherited base class.
"""
def __init__(self, infile, encoding='utf_8', base=RecursiveTS):
super().__init__(infile, encoding)
for key, value in self.items():
if isinstance(value, TranslationStrings):
new_translation_strings = base()
for key_, value_ in value.items():
new_translation_strings[key_] = value_
self[key] = new_translation_strings
If base kwarg is implemented in SP's LangStrings, there's no need to keep this class in the package. - RecursiveTS - provides new .tokenize method and makes .get_string method recursive
Syntax: Select all
class RecursiveTS(TranslationStrings):
"""
This class provides recursive get_string method.
"""
def get_string(self, language=None, **tokens):
"""
Exposes packed tokens (self.tokens) and additional tokens (**tokens).
"""
# Deeply expose all TranslationStrings instances in self.tokens
for token_name, token in self.tokens.items():
if isinstance(token, TranslationStrings):
new_tokens = self.tokens.copy()
del new_tokens[token_name]
# Pass additional tokens - these will NOT be used to
# continue deep exposition but will be used to call
# super().get_string.
token = token.get_string(language, **tokens)
self.tokens[token_name] = token
# Then shallowly expose all TranslationsStrings instances in **tokens
for token_name, token in tokens.items():
if isinstance(token, TranslationStrings):
# Don't pass any additional tokens.
# The token should either be trivial (regular
# TranslationStrings instance) or rely on itself (self.tokens).
tokens[token_name] = token.get_string(language)
# Finally with all of the tokens exposed, call the original get_string
return super().get_string(language, **tokens)
def tokenize(self, **tokens):
"""Return new TranslationStrings object with updated tokens."""
rs = type(self)()
for key, value in self.items():
rs[key] = value
rs.tokens = self.tokens.copy()
rs.tokens.update(tokens)
return rs
Now, why we would need all this. Here's an example plugin that utilizes the package:
cstrike/resource/source-python/translations/advanced_ts_test.ini
Code: Select all
[popup title]
en="My Popup"
[popup base]
en="{text}"
[popup base disabled]
en="(DISABLED) {text}"
[popup slay_team]
en="Slay {team} players"
[team_alpha]
en="Alpha"
[team_bravo]
en="Bravo"
[team_charlie]
en="Charlie"
[chat base]
en="{colorful_sign}{text}"
[chat base with_tag]
en="{colorful_sign}{tag} {text}"
[tag]
en="{color_special}[{color_default}My Plugin{color_special}]{color_default}"
[your_team_is]
en="Your team is team {team}"
cstrike/addons/source-python/plugins/advanced_ts_test/advanced_ts_test.py
Import/constants
Syntax: Select all
from colors import Color
from events import Event
from menus import PagedMenu
from menus import PagedOption
from messages import SayText2
from players.entity import Player
from players.helpers import index_from_userid
from advanced_ts import BaseLangStrings, RecursiveTS
COLORFUL_SIGN = '\x01'
COLOR_DEFAULT = Color(255, 255, 255)
COLOR_SPECIAL = Color(255, 0, 0)
strings = BaseLangStrings('advanced_ts_test', base=RecursiveTS)
color_tokens = {
'color_default': COLOR_DEFAULT,
'color_special': COLOR_SPECIAL,
}
Now let's create a simple popup but we will take advantage of .tokenize. The thing is that we want to show 'your_team_is' translation but one of its tokens, 'team', is a translation itself. What is more, we should add an ability to add a translated "(DISABLED)" phrase to options we want to disable. Here it is (for tokens explanation look at the advanced_ts.ini above):
Syntax: Select all
popup = PagedMenu(title=strings['popup title'])
# Regular option
popup.append(PagedOption(
text=strings['popup base'].tokenize(
text=strings['popup slay_team'].tokenize(
team=strings['team_alpha']
)
)
))
# "(DISABLED)" option
popup.append(PagedOption(
text=strings['popup base disabled'].tokenize(
text=strings['popup slay_team'].tokenize(
team=strings['team_bravo'],
)
),
highlight=False,
selectable=False
))
Then let's think about more complex structures. For example, chat message. It may include a tag - in this case tag will need colors, as you can see in strings file. So we will need to tokenize it with color_tokens (see import/constants section). Then there's a message itself. Not only should it accept various tokens which in turn may turn out to be even more TranslationStrings instanes, but it also should receive color_tokens.
Let's see create tell() function which takes care of all of that. I will actually firstly show you Python 3.5 version which won't work with current SP builds, but it's easier to read.
Syntax: Select all
def tell(*players, message, with_tag=True, **tokens):
"""Send a SayText2 message to players"""
player_indexes = [player.index for player in players]
base = strings['chat base with_tag' if with_tag else 'chat base']
tokenized_message = base.tokenize(
tag=strings['tag'].tokenize(
**color_tokens # Tag only needs color tokens
),
text=message.tokenize(
**color_tokens, # Message needs color tokens
**tokens # But it also needs tokens passed to tell()
),
colorful_sign=COLORFUL_SIGN, # The base needs this symbol
)
# Note how we don't pass any tokens in .send()
SayText2(message=tokenized_message).send(*player_indexes)
Finally, let's use all of that
Syntax: Select all
@Event('player_spawn')
def on_player_spawn(game_event):
player = Player(index_from_userid(game_event.get_int('userid')))
# Let tell() pack all additional tokens ('team' in our case) by itself
tell(player, message=strings['your_team_is'], team=strings['team_alpha'])
# Or pack such tokens by ourselves - the previous approach is shorter
tell(player, message=strings['your_team_is'].tokenize(team=strings['team_bravo']))
# Also send a popup
popup.send(player.index)
Result:
The package can be found here
Sample plugin is all given above, the one thing is that here's working Python 3.4 version of tell():
Syntax: Select all
def tell(*players, message, with_tag=True, **tokens):
"""Send a SayText2 message to players"""
player_indexes = [player.index for player in players]
base = strings['chat base with_tag' if with_tag else 'chat base']
tokens.update(color_tokens)
tokenized_message = base.tokenize(
tag=strings['tag'].tokenize(
**color_tokens # Tag only needs color tokens
),
text=message.tokenize(
**tokens
),
colorful_sign=COLORFUL_SIGN, # The base needs this
)
SayText2(message=tokenized_message).send(*player_indexes)
I hope some ideas of this package can make their way into Source.Python. Thanks.