summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoremkael <emkael@tlen.pl>2018-05-01 14:05:35 +0200
committeremkael <emkael@tlen.pl>2018-05-01 14:05:35 +0200
commitddf22911ae7e13f2070b164a060478f309ab09d7 (patch)
tree244fcb402cd5074249fd3fcb6fd173ee2dd45e82
parent780d9f86ad66d97662bbc6908bbb5625f4317557 (diff)
parent4fa864f8b75a3865c714a2b864784b1eb85c9c1b (diff)
Merge branch 'i18n' into devel
-rw-r--r--CONFIG.md19
-rw-r--r--jfr_playoff/generator.py92
-rw-r--r--jfr_playoff/i18n.py31
-rw-r--r--jfr_playoff/template.py369
4 files changed, 298 insertions, 213 deletions
diff --git a/CONFIG.md b/CONFIG.md
index d4fa87f..906081f 100644
--- a/CONFIG.md
+++ b/CONFIG.md
@@ -36,6 +36,25 @@ Ustawienia bazy danych nie są wymagane, program potrafi sobie poradzić bez nic
Ustawienia Gońca nie są wymagane, domyślnie jest on wyłączony, a przy włączeniu - domyślnie komunikuje się z `localhost` na porcie `8090`.
+Tłumaczenia tekstów
+-------------------
+
+Program obsługuje możliwość ustawienia własnych łańcuchów znaków wyświetlanych w różnych miejscach wynikowej strony. Teksty te opcjonalnie określa sekcja `"i18n"` pliku konfiguracyjnego.
+
+Domyślna postać wszystkich obsługiwanych łańcuchów to:
+
+```
+{
+ "SCORE": "wynik",
+ "FINAL_STANDINGS": "klasyfikacja końcowa",
+ "STANDINGS_PLACE": "miejsce",
+ "STANDINGS_TEAM": "drużyna",
+ "STANDINGS_CAPTIONS": "legenda",
+ "FOOTER_GENERATED": "strona wygenerowana",
+ "SWISS_DEFAULT_LABEL": "Turniej o&nbsp;%d.&nbsp;miejsce"
+}
+```
+
Zdalne pliki konfiguracyjne
---------------------------
diff --git a/jfr_playoff/generator.py b/jfr_playoff/generator.py
index ee1a496..6d107bd 100644
--- a/jfr_playoff/generator.py
+++ b/jfr_playoff/generator.py
@@ -1,6 +1,6 @@
from datetime import datetime
-import jfr_playoff.template as p_temp
+from jfr_playoff.template import PlayoffTemplate
from jfr_playoff.data import PlayoffData
from jfr_playoff.logger import PlayoffLogger
@@ -21,6 +21,8 @@ class PlayoffGenerator(object):
self.leaderboard_classes = settings.get('position_styles')
PlayoffLogger.get('generator').info(
'leaderboard classes settings: %s', self.leaderboard_classes)
+ self.p_temp = PlayoffTemplate(
+ settings.get('i18n') if settings.has_section('i18n') else {})
def generate_content(self):
match_grid = self.get_match_grid(
@@ -28,69 +30,84 @@ class PlayoffGenerator(object):
self.data.generate_phases(),
self.data.fill_match_info())
leaderboard_table = self.get_leaderboard_table()
- return p_temp.PAGE % (
- p_temp.PAGE_HEAD % (
- p_temp.PAGE_HEAD_REFRESH % (
- self.page['refresh'])
+ return self.p_temp.get(
+ 'PAGE',
+ self.p_temp.get(
+ 'PAGE_HEAD',
+ self.p_temp.get(
+ 'PAGE_HEAD_REFRESH',
+ self.page['refresh']) \
if self.page['refresh'] > 0 else '',
self.page['title']),
- p_temp.PAGE_BODY % (
+ self.p_temp.get(
+ 'PAGE_BODY',
self.page['logoh'],
match_grid,
self.get_swiss_links(),
leaderboard_table,
self.get_leaderboard_caption_table() if leaderboard_table else '',
- p_temp.PAGE_BODY_FOOTER.decode('utf8') % (
+ self.p_temp.get(
+ 'PAGE_BODY_FOOTER',
datetime.now().strftime('%Y-%m-%d o %H:%M:%S'))))
def get_match_table(self, match):
rows = ''
for team in match.teams:
- score_html = p_temp.MATCH_SCORE % (team.score)
+ score_html = self.p_temp.get('MATCH_SCORE', team.score)
team_label = ' / '.join([
self.data.get_shortname(name) for name in
team.name.split('<br />')])
- team_html = p_temp.MATCH_TEAM_LINK % (
- match.link, team.name, team_label) if match.link is not None \
- else p_temp.MATCH_TEAM_NON_LINK % (
- team.name, team_label)
- rows += p_temp.MATCH_TEAM_ROW % (
+ team_html = self.p_temp.get(
+ 'MATCH_TEAM_LINK',
+ match.link, team.name, team_label) \
+ if match.link is not None \
+ else self.p_temp.get(
+ 'MATCH_TEAM_NON_LINK',
+ team.name, team_label)
+ rows += self.p_temp.get(
+ 'MATCH_TEAM_ROW',
' '.join([
'winner' if team.name == match.winner else '',
'loser' if team.name == match.loser else ''
]).strip(),
team_html,
- p_temp.MATCH_LINK % (match.link, score_html) if match.link is not None else score_html)
- html = p_temp.MATCH_TABLE.decode('utf8') % (
+ self.p_temp.get(
+ 'MATCH_LINK',
+ match.link, score_html) \
+ if match.link is not None else score_html)
+ html = self.p_temp.get(
+ 'MATCH_TABLE',
int(self.page['width'] * 0.75),
int(self.page['width'] * 0.25),
rows)
if match.running > 0:
- running_html = p_temp.MATCH_RUNNING % (match.running)
- html += p_temp.MATCH_LINK % (match.link, running_html) if match.link is not None else running_html
+ running_html = self.p_temp.get('MATCH_RUNNING', match.running)
+ html += self.p_temp.get('MATCH_LINK', match.link, running_html) if match.link is not None else running_html
PlayoffLogger.get('generator').info(
'match table for #%d generated: %d bytes', match.id, len(html))
return html
def get_phase_header(self, phase, position):
- if phase.running:
- grid_header = p_temp.MATCH_GRID_PHASE_RUNNING
- else:
- grid_header = p_temp.MATCH_GRID_PHASE
- grid_header = grid_header % (phase.title)
+ grid_header = self.p_temp.get(
+ 'MATCH_GRID_PHASE_RUNNING' if phase.running \
+ else 'MATCH_GRID_PHASE',
+ phase.title)
if phase.link is not None:
- return p_temp.MATCH_GRID_PHASE_LINK % (
+ return self.p_temp.get(
+ 'MATCH_GRID_PHASE_LINK',
phase.link,
self.page['width'], position,
grid_header)
else:
- return p_temp.MATCH_GRID_PHASE_NON_LINK % (
+ return self.p_temp.get(
+ 'MATCH_GRID_PHASE_NON_LINK',
self.page['width'], position,
grid_header)
def get_match_box(self, match, position):
if match is not None:
- return p_temp.MATCH_BOX % (
+ return self.p_temp.get(
+ 'MATCH_BOX',
position[0], position[1],
match.id,
' '.join([
@@ -130,7 +147,8 @@ class PlayoffGenerator(object):
(grid_x, grid_y))
row_no += 1
col_no += 1
- return p_temp.MATCH_GRID % (
+ return self.p_temp.get(
+ 'MATCH_GRID',
canvas_size[0], canvas_size[1],
canvas_size[0], canvas_size[1],
' '.join(['data-%s="%s"' % (
@@ -150,9 +168,10 @@ class PlayoffGenerator(object):
rows = ''
for style in self.leaderboard_classes:
if 'caption' in style:
- rows += p_temp.LEADERBOARD_CAPTION_TABLE_ROW % (
+ rows += self.p_temp.get(
+ 'LEADERBOARD_CAPTION_TABLE_ROW',
style['class'], style['caption'])
- return p_temp.LEADERBOARD_CAPTION_TABLE % (rows) if rows else ''
+ return self.p_temp.get('LEADERBOARD_CAPTION_TABLE', rows) if rows else ''
def get_leaderboard_table(self):
leaderboard = self.data.fill_leaderboard()
@@ -161,11 +180,12 @@ class PlayoffGenerator(object):
position = 1
rows = ''
for team in leaderboard:
- rows += p_temp.LEADERBOARD_ROW % (
+ rows += self.p_temp.get(
+ 'LEADERBOARD_ROW',
self.get_leaderboard_row_class(position),
position, self.get_flag(team), team or '')
position += 1
- html = p_temp.LEADERBOARD.decode('utf8') % (rows)
+ html = self.p_temp.get('LEADERBOARD', rows)
PlayoffLogger.get('generator').info(
'leaderboard HTML generated: %d bytes', len(html))
return html
@@ -173,12 +193,14 @@ class PlayoffGenerator(object):
def get_swiss_links(self):
info = []
for event in self.data.get_swiss_info():
- event_label = p_temp.SWISS_DEFAULT_LABEL % (event['position'])
+ event_label = self.p_temp.get('SWISS_DEFAULT_LABEL', event['position'])
if 'label' in event and event['label'] is not None:
event_label = event['label']
- info.append((p_temp.SWISS_LINK if event['finished'] else p_temp.SWISS_RUNNING_LINK) % (
- event['link'], event_label
- ))
+ info.append((self.p_temp.get('SWISS_LINK') \
+ if event['finished'] \
+ else self.p_temp.get(
+ 'SWISS_RUNNING_LINK',
+ event['link'], event_label)))
html = '\n'.join(info)
PlayoffLogger.get('generator').info(
'swiss HTML generated: %d bytes', len(html))
@@ -186,4 +208,4 @@ class PlayoffGenerator(object):
def get_flag(self, team):
flag = self.data.get_team_image(team)
- return '' if flag is None else p_temp.LEADERBOARD_ROW_FLAG % (flag)
+ return '' if flag is None else self.p_temp.get('LEADERBOARD_ROW_FLAG', flag)
diff --git a/jfr_playoff/i18n.py b/jfr_playoff/i18n.py
new file mode 100644
index 0000000..e22a563
--- /dev/null
+++ b/jfr_playoff/i18n.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+
+import re
+
+PLAYOFF_I18N_DEFAULTS = {
+ 'SCORE': 'wynik',
+ 'FINAL_STANDINGS': 'klasyfikacja końcowa',
+ 'STANDINGS_PLACE': 'miejsce',
+ 'STANDINGS_TEAM': 'drużyna',
+ 'STANDINGS_CAPTIONS': 'legenda',
+ 'FOOTER_GENERATED': 'strona wygenerowana',
+ 'SWISS_DEFAULT_LABEL': 'Turniej o&nbsp;%d.&nbsp;miejsce'
+}
+
+class PlayoffI18N(object):
+
+ def __init__(self, settings):
+ self.settings = settings
+ self.string_match = re.compile(r'{{(.*)}}')
+
+ def localize(self, string):
+ return re.sub(
+ self.string_match,
+ lambda x: self.__get_translation(x.group(1)),
+ string)
+
+ def __get_translation(self, string):
+ for dictionary in [self.settings, PLAYOFF_I18N_DEFAULTS]:
+ if string in dictionary:
+ return dictionary[string].decode('utf8')
+ return '{{%s}}' % (string)
diff --git a/jfr_playoff/template.py b/jfr_playoff/template.py
index b99c7c7..70c179b 100644
--- a/jfr_playoff/template.py
+++ b/jfr_playoff/template.py
@@ -1,180 +1,193 @@
# -*- coding: utf-8 -*-
-MATCH_TABLE = '''
-<table border="0" cellspacing="0">
-<tr>
-<td class="s12" width="%d">&nbsp;</td>
-<td class="bdcc2" width="%d">&nbsp;wynik&nbsp;</td>
-</tr>
-%s
-</table>
-'''
-
-MATCH_LINK = '''
-<a href="%s" target="_top">
-%s
-</a>
-'''
-
-MATCH_SCORE = '''
-&nbsp;%.1f&nbsp;
-'''
-
-MATCH_TEAM_LINK = '''
-<a href="%s" onmouseover="Tip('%s')" onmouseout="UnTip()">%s</a>
-'''
-
-MATCH_TEAM_NON_LINK = '''
-<a onmouseover="Tip('%s')" onmouseout="UnTip()">%s</a>
-'''
-
-MATCH_TEAM_ROW = '''
-<tr class="%s">
-<td class="bd1">&nbsp;%s&nbsp;</td>
-<td class="bdc">
-%s
-</td>
-</tr>
-'''
-
-MATCH_RUNNING = '''
-<img src="images/A.gif" />
-<span style="font-size: 10pt">%d</span>
-<img src="images/A.gif" />
-'''
-
-MATCH_GRID = '''
-<div style="position: relative; width: %dpx; height: %dpx; margin: 10px">
-<canvas width="%d" height="%d" id="playoff_canvas" %s></canvas>
-%s
-<script src="sklady/playoff.js" type="text/javascript"></script>
-</div>
-'''
-
-MATCH_GRID_PHASE_LINK = '''
-<a href="%s" target="_top" style="display: inline-block; width: %dpx; text-align: center; position: absolute; top: 0; left: %dpx">
-%s
-</a>
-'''
-
-MATCH_GRID_PHASE_NON_LINK = '''
-<span class="phase_header" style="display: inline-block; width: %dpx; text-align: center; position: absolute; top: 0; left: %dpx">
-<p style="margin: 0">%s</p>
-</span>
-'''
-
-MATCH_GRID_PHASE = '''
-<font size="4">%s</font>
-'''
-
-MATCH_GRID_PHASE_RUNNING = '''
-<img src="images/A.gif" />
-<font size="4">%s</font>
-<img src="images/A.gif" />
-'''
-
-MATCH_BOX = '''
-<div style="text-align: center; position: absolute; left: %dpx; top: %dpx" data-id="%d" data-winner="%s" data-loser="%s" class="playoff_matchbox">
-%s
-</div>
-'''
-
-LEADERBOARD = '''
-<table border="0" cellspacing="0">
-<tr>
-<td class="bdnl12" colspan="2" align="center"><b>&nbsp;KLASYFIKACJA KOŃCOWA&nbsp;</b></td>
-</tr>
-<tr>
-<td class="e" colspan="2">&nbsp;</td>
-</tr>
-<tr>
-<td class="bdcc12">&nbsp;miejsce&nbsp;</td>
-<td class="bdcc2">&nbsp;drużyna&nbsp;</td>
-</tr>
-%s
-</table>
-'''
-
-LEADERBOARD_ROW = '''
-<tr class="%s">
-<td class="bdc1">%d</td>
-<td class="bd">
-&nbsp;%s&nbsp;&nbsp;%s&nbsp;
-</td>
-</tr>
-'''
-
-LEADERBOARD_ROW_FLAG = '''
-<img class="fl" src="images/%s" />
-'''
-
-LEADERBOARD_CAPTION_TABLE = '''
-<table class="caption_table" border="0" cellspacing="0">
-<tr><td class="e">&nbsp;</td></tr>
-<tr><td class="bdnl12" align="center"><b>&nbsp;LEGENDA&nbsp;</b></td></tr>
-%s
-</table>
-'''
-
-LEADERBOARD_CAPTION_TABLE_ROW = '''
-<tr class="%s">
-<td class="bd1">
-&nbsp;%s&nbsp;
-</td>
-</tr>
-'''
-
-PAGE_HEAD = '''
-<meta http-equiv="Pragma" content="no-cache" />
-<meta http-equiv="Cache-Control" content="no-cache" />
-<meta name="robots" content="noarchive" />
-<meta http-equiv="expires" content="0" />
-<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-<meta name="Generator" content="PlayOff" />
-%s
-<title>%s</title>
-<link rel="stylesheet" type="text/css" href="css/kolorki.css" />
-<script type="text/javascript" src="sklady/myAjax.js"></script>
-'''
-
-PAGE_HEAD_REFRESH = '''
-<meta http-equiv="Refresh" content="%d" />
-'''
-
-PAGE_BODY = '''
-<script type="text/javascript" src="sklady/wz_tooltip.js"></script>
-%s
-%s
-<p>
-%s
-</p>
-%s
-%s
-%s
-'''
-
-PAGE_BODY_FOOTER = '''
-<p class="f">&nbsp;Admin&nbsp;&copy;Jan Romański&#39;2005, PlayOff&nbsp;&copyMichał Klichowicz&#39;2017-2018, strona wygenerowana %s</p>
-'''
-
-PAGE = '''
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<head>
-%s
-</head>
-<body class="all">
-%s
-</body>
-</html>
-'''
-
-SWISS_LINK = '''
-[<a href="%s" class="zb" target="_top">&nbsp;%s&nbsp;</a>]<br /><br />
-'''
-
-SWISS_RUNNING_LINK = '''
-[<a href="%s" class="zb" target="_top">&nbsp;<img src="images/A.gif" />&nbsp;%s&nbsp;<img src="images/A.gif" />&nbsp;</a>]<br /><br />
-'''
-
-SWISS_DEFAULT_LABEL = 'Turniej o&nbsp;%d.&nbsp;miejsce'
+from jfr_playoff.i18n import PlayoffI18N
+
+class PlayoffTemplateStrings(object):
+
+ MATCH_TABLE = '''
+ <table border="0" cellspacing="0">
+ <tr>
+ <td class="s12" width="%d">&nbsp;</td>
+ <td class="bdcc2" width="%d">&nbsp;{{SCORE}}&nbsp;</td>
+ </tr>
+ %s
+ </table>
+ '''
+
+ MATCH_LINK = '''
+ <a href="%s" target="_top">
+ %s
+ </a>
+ '''
+
+ MATCH_SCORE = '''
+ &nbsp;%.1f&nbsp;
+ '''
+
+ MATCH_TEAM_LINK = '''
+ <a href="%s" onmouseover="Tip('%s')" onmouseout="UnTip()">%s</a>
+ '''
+
+ MATCH_TEAM_NON_LINK = '''
+ <a onmouseover="Tip('%s')" onmouseout="UnTip()">%s</a>
+ '''
+
+ MATCH_TEAM_ROW = '''
+ <tr class="%s">
+ <td class="bd1">&nbsp;%s&nbsp;</td>
+ <td class="bdc">
+ %s
+ </td>
+ </tr>
+ '''
+
+ MATCH_RUNNING = '''
+ <img src="images/A.gif" />
+ <span style="font-size: 10pt">%d</span>
+ <img src="images/A.gif" />
+ '''
+
+ MATCH_GRID = '''
+ <div style="position: relative; width: %dpx; height: %dpx; margin: 10px">
+ <canvas width="%d" height="%d" id="playoff_canvas" %s></canvas>
+ %s
+ <script src="sklady/playoff.js" type="text/javascript"></script>
+ </div>
+ '''
+
+ MATCH_GRID_PHASE_LINK = '''
+ <a href="%s" target="_top" style="display: inline-block; width: %dpx; text-align: center; position: absolute; top: 0; left: %dpx">
+ %s
+ </a>
+ '''
+
+ MATCH_GRID_PHASE_NON_LINK = '''
+ <span class="phase_header" style="display: inline-block; width: %dpx; text-align: center; position: absolute; top: 0; left: %dpx">
+ <p style="margin: 0">%s</p>
+ </span>
+ '''
+
+ MATCH_GRID_PHASE = '''
+ <font size="4">%s</font>
+ '''
+
+ MATCH_GRID_PHASE_RUNNING = '''
+ <img src="images/A.gif" />
+ <font size="4">%s</font>
+ <img src="images/A.gif" />
+ '''
+
+ MATCH_BOX = '''
+ <div style="text-align: center; position: absolute; left: %dpx; top: %dpx" data-id="%d" data-winner="%s" data-loser="%s" class="playoff_matchbox">
+ %s
+ </div>
+ '''
+
+ LEADERBOARD = '''
+ <table border="0" cellspacing="0">
+ <tr>
+ <td class="bdnl12" colspan="2" align="center" style="text-transform: uppercase"><b>&nbsp;{{FINAL_STANDINGS}}&nbsp;</b></td>
+ </tr>
+ <tr>
+ <td class="e" colspan="2">&nbsp;</td>
+ </tr>
+ <tr>
+ <td class="bdcc12">&nbsp;{{STANDINGS_PLACE}}&nbsp;</td>
+ <td class="bdcc2">&nbsp;{{STANDINGS_TEAM}}&nbsp;</td>
+ </tr>
+ %s
+ </table>
+ '''
+
+ LEADERBOARD_ROW = '''
+ <tr class="%s">
+ <td class="bdc1">%d</td>
+ <td class="bd">
+ &nbsp;%s&nbsp;&nbsp;%s&nbsp;
+ </td>
+ </tr>
+ '''
+
+ LEADERBOARD_ROW_FLAG = '''
+ <img class="fl" src="images/%s" />
+ '''
+
+ LEADERBOARD_CAPTION_TABLE = '''
+ <table class="caption_table" border="0" cellspacing="0">
+ <tr><td class="e">&nbsp;</td></tr>
+ <tr><td class="bdnl12" align="center" style="text-transform: uppercase"><b>&nbsp;{{STANDINGS_CAPTIONS}}&nbsp;</b></td></tr>
+ %s
+ </table>
+ '''
+
+ LEADERBOARD_CAPTION_TABLE_ROW = '''
+ <tr class="%s">
+ <td class="bd1">
+ &nbsp;%s&nbsp;
+ </td>
+ </tr>
+ '''
+
+ PAGE_HEAD = '''
+ <meta http-equiv="Pragma" content="no-cache" />
+ <meta http-equiv="Cache-Control" content="no-cache" />
+ <meta name="robots" content="noarchive" />
+ <meta http-equiv="expires" content="0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta name="Generator" content="PlayOff" />
+ %s
+ <title>%s</title>
+ <link rel="stylesheet" type="text/css" href="css/kolorki.css" />
+ <script type="text/javascript" src="sklady/myAjax.js"></script>
+ '''
+
+ PAGE_HEAD_REFRESH = '''
+ <meta http-equiv="Refresh" content="%d" />
+ '''
+
+ PAGE_BODY = '''
+ <script type="text/javascript" src="sklady/wz_tooltip.js"></script>
+ %s
+ %s
+ <p>
+ %s
+ </p>
+ %s
+ %s
+ %s
+ '''
+
+ PAGE_BODY_FOOTER = '''
+ <p class="f">&nbsp;Admin&nbsp;&copy;Jan Romański&#39;2005, PlayOff&nbsp;&copyMichał Klichowicz&#39;2017-2018, {{FOOTER_GENERATED}} %s</p>
+ '''
+
+ PAGE = '''
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+ <html>
+ <head>
+ %s
+ </head>
+ <body class="all">
+ %s
+ </body>
+ </html>
+ '''
+
+ SWISS_LINK = '''
+ [<a href="%s" class="zb" target="_top">&nbsp;%s&nbsp;</a>]<br /><br />
+ '''
+
+ SWISS_RUNNING_LINK = '''
+ [<a href="%s" class="zb" target="_top">&nbsp;<img src="images/A.gif" />&nbsp;%s&nbsp;<img src="images/A.gif" />&nbsp;</a>]<br /><br />
+ '''
+
+ SWISS_DEFAULT_LABEL = '{{SWISS_DEFAULT_LABEL}}'
+
+class PlayoffTemplate(object):
+
+ def __init__(self, settings):
+ self.i18n = PlayoffI18N(settings)
+
+ def get(self, string, *params):
+ return self.i18n.localize(
+ getattr(PlayoffTemplateStrings, string).decode('utf8')) % params