babel integration

This commit is contained in:
Miguel Grinberg 2013-01-31 21:48:20 -08:00
parent 0bba066527
commit cac572cb0e
23 changed files with 350 additions and 56 deletions

1
.gitignore vendored
View File

@ -3,4 +3,5 @@
app.db
search.db
flask
*.mo

View File

@ -4,6 +4,7 @@ from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.login import LoginManager
from flask.ext.openid import OpenID
from flask.ext.mail import Mail
from flask.ext.babel import Babel, lazy_gettext
from config import basedir, ADMINS, MAIL_SERVER, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD
from momentjs import momentjs
@ -13,8 +14,10 @@ db = SQLAlchemy(app)
lm = LoginManager()
lm.init_app(app)
lm.login_view = 'login'
lm.login_message = lazy_gettext('Please log in to access this page.')
oid = OpenID(app, os.path.join(basedir, 'tmp'))
mail = Mail(app)
babel = Babel(app)
if not app.debug:
import logging

View File

@ -1,5 +1,6 @@
from flask.ext.wtf import Form, TextField, BooleanField, TextAreaField
from flask.ext.wtf import Required, Length
from flask.ext.babel import gettext
from app.models import User
class LoginForm(Form):
@ -19,9 +20,12 @@ class EditForm(Form):
return False
if self.nickname.data == self.original_nickname:
return True
if self.nickname.data != User.make_valid_nickname(self.nickname.data):
self.nickname.errors.append(gettext('This nickname has invalid characters. Please use letters, numbers, dots and underscores only.'))
return False
user = User.query.filter_by(nickname = self.nickname.data).first()
if user != None:
self.nickname.errors.append('This nickname is already in use. Please choose another one.')
self.nickname.errors.append(gettext('This nickname is already in use. Please choose another one.'))
return False
return True

View File

@ -2,6 +2,7 @@ from hashlib import md5
from app import db
from app import app
import flask.ext.whooshalchemy as whooshalchemy
import re
ROLE_USER = 0
ROLE_ADMIN = 1
@ -26,6 +27,10 @@ class User(db.Model):
backref = db.backref('followers', lazy = 'dynamic'),
lazy = 'dynamic')
@staticmethod
def make_valid_nickname(nickname):
return re.sub('[^a-zA-Z0-9_\.]', '', nickname)
@staticmethod
def make_unique_nickname(nickname):
if User.query.filter_by(nickname = nickname).first() == None:

4
app/static/js/moment-es.min.js vendored Executable file
View File

@ -0,0 +1,4 @@
// moment.js language configuration
// language : spanish (es)
// author : Julio Napurí : https://github.com/julionc
(function(){function e(e){e.lang("es",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:"ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),weekdays:"domingo_lunes_martes_mi\u00e9rcoles_jueves_viernes_s\u00e1bado".split("_"),weekdaysShort:"dom._lun._mar._mi\u00e9._jue._vie._s\u00e1b.".split("_"),weekdaysMin:"Do_Lu_Ma_Mi_Ju_Vi_S\u00e1".split("_"),longDateFormat:{LT:"H:mm",L:"DD/MM/YYYY",LL:"D \\de MMMM \\de YYYY",LLL:"D \\de MMMM \\de YYYY LT",LLLL:"dddd, D \\de MMMM \\de YYYY LT"},calendar:{sameDay:function(){return"[hoy a la"+(this.hours()!==1?"s":"")+"] LT"},nextDay:function(){return"[ma\u00f1ana a la"+(this.hours()!==1?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(this.hours()!==1?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(this.hours()!==1?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(this.hours()!==1?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un d\u00eda",dd:"%d d\u00edas",M:"un mes",MM:"%d meses",y:"un a\u00f1o",yy:"%d a\u00f1os"},ordinal:function(e){return"\u00ba"},week:{dow:1,doy:4}})}typeof define=="function"&&define.amd&&define(["moment"],e),typeof window!="undefined"&&window.moment&&e(window.moment)})();

View File

@ -2,6 +2,6 @@
{% extends "base.html" %}
{% block content %}
<h1>File Not Found</h1>
<p><a href="{{url_for('index')}}">Back</a></p>
<h1>{{ _('File Not Found') }}</h1>
<p><a href="{{url_for('index')}}">{{ _('Back') }}</a></p>
{% endblock %}

View File

@ -2,7 +2,7 @@
{% extends "base.html" %}
{% block content %}
<h1>An unexpected error has occurred</h1>
<p>The administrator has been notified. Sorry for the inconvenience!</p>
<p><a href="{{url_for('index')}}">Back</a></p>
<h1>{{ _('An unexpected error has occurred') }}</h1>
<p>{{ _('The administrator has been notified. Sorry for the inconvenience!') }}</p>
<p><a href="{{url_for('index')}}">{{ _('Back') }}</a></p>
{% endblock %}

View File

@ -11,6 +11,9 @@
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<script src="/static/js/moment.min.js"></script>
{% if g.locale != 'en' %}
<script src="/static/js/moment-{{g.locale}}.min.js"></script>
{% endif %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
@ -24,15 +27,15 @@
</a>
<a class="brand" href="/">microblog</a>
<ul class="nav">
<li><a href="{{ url_for('index') }}">Home</a></li>
<li><a href="{{ url_for('index') }}">{{ _('Home') }}</a></li>
{% if g.user.is_authenticated() %}
<li><a href="{{ url_for('user', nickname = g.user.nickname) }}">Your Profile</a></li>
<li><a href="{{ url_for('logout') }}">Logout</a></li>
<li><a href="{{ url_for('user', nickname = g.user.nickname) }}">{{ _('Your Profile') }}</a></li>
<li><a href="{{ url_for('logout') }}">{{ _('Logout') }}</a></li>
{% endif %}
</ul>
<div class="nav-collapse collapse">
{% if g.user.is_authenticated() %}
<form class="navbar-search pull-right" action="{{url_for('search')}}" method="post" name="search">{{g.search_form.hidden_tag()}}{{g.search_form.search(size=20,placeholder="Search",class="search-query")}}</form>
<form class="navbar-search pull-right" action="{{url_for('search')}}" method="post" name="search">{{g.search_form.hidden_tag()}}{{g.search_form.search(size=20,placeholder=_('Search'),class="search-query")}}</form>
{% endif %}
</div>
</div>
@ -45,3 +48,4 @@
</div>
</body>
</html>

View File

@ -2,13 +2,13 @@
{% extends "base.html" %}
{% block content %}
<h1>Edit Your Profile</h1>
<h1>{{ _('Edit Your Profile') }}</h1>
{% include 'flash.html' %}
<div class="well">
<form class="form-horizontal" action="" method="post" name="edit">
{{form.hidden_tag()}}
<div class="control-group{% if form.errors.post %} error{% endif %}">
<label class="control-label" for="nickname">Your nickname:</label>
<label class="control-label" for="nickname">{{ _('Your nickname:') }}</label>
<div class="controls">
{{ form.nickname(maxlength = 64, class = "span4") }}
{% for error in form.errors.nickname %}
@ -17,7 +17,7 @@
</div>
</div>
<div class="control-group{% if form.errors.post %} error{% endif %}">
<label class="control-label" for="about_me">About yourself:</label>
<label class="control-label" for="about_me">{{ _('About yourself:') }}</label>
<div class="controls">
{{ form.about_me(cols = 64, rows = 4, class = "span4") }}
{% for error in form.errors.about_me %}
@ -27,7 +27,7 @@
</div>
<div class="control-group">
<div class="controls">
<input class="btn btn-primary" type="submit" value="Save Changes">
<input class="btn btn-primary" type="submit" value="{{ _('Save Changes') }}">
</div>
</div>
</form>

View File

@ -1,5 +1,5 @@
<p>Dear {{user.nickname}},</p>
<p><a href="{{url_for("user", nickname = follower.nickname, _external = True)}}">{{follower.nickname}}</a> is now a follower.</p>
<p>{{ _('%(nickname)s is now a follower.', nickname = '<a href="' + url_for("user", nickname = follower.nickname, _external = True) + '">' + follower.nickname + '</a>') }}</p>
<table>
<tr valign="top">
<td><img src="{{follower.avatar(50)}}"></td>
@ -9,5 +9,5 @@
</td>
</tr>
</table>
<p>Regards,</p>
<p>The <code>microblog</code> admin</p>
<p>{{ _('Regards,') }}</p>
<p>{{ _('The <code>microblog</code> admin') }}</p>

View File

@ -1,9 +1,9 @@
Dear {{user.nickname}},
{{ _('Dear %(nickname)s,', nickname = user.nickname) }}
{{follower.nickname}} is now a follower. Click on the following link to visit {{follower.nickname}}'s profile page:
{{ _('%(nickname)s is now a follower. Click on the following link to visit %(nickname)s\'s profile page:', nickname = follower.nickname) }}
{{url_for("user", nickname = follower.nickname, _external = True)}}
Regards,
{{ _('Regards,') }}
The microblog admin
{{ _('The microblog admin') }}

View File

@ -2,13 +2,13 @@
{% extends "base.html" %}
{% block content %}
<h1>Hi, {{g.user.nickname}}!</h1>
<h1>{{ _('Hi, %(nickname)s!', nickname = g.user.nickname) }}</h1>
{% include 'flash.html' %}
<div class="well">
<form class="form-horizontal" action="" method="post" name="post">
{{form.hidden_tag()}}
<div class="control-group{% if form.errors.post %} error{% endif %}">
<label class="control-label" for="post">Say something:</label>
<label class="control-label" for="post">{{ _('Say something:') }}</label>
<div class="controls">
{{ form.post(size = 30, maxlength = 140) }}
{% for error in form.errors.post %}
@ -18,7 +18,7 @@
</div>
<div class="control-group">
<div class="controls">
<input class="btn btn-primary" type="submit" value="Post!">
<input class="btn btn-primary" type="submit" value="{{ _('Post!') }}">
</div>
</div>
</form>
@ -28,14 +28,14 @@
{% endfor %}
<ul class="pager">
{% if posts.has_prev %}
<li class="previous"><a href="{{ url_for('index', page = posts.prev_num) }}">Newer posts</a></li>
<li class="previous"><a href="{{ url_for('index', page = posts.prev_num) }}">{{ _('Newer posts') }}</a></li>
{% else %}
<li class="previous disabled"><a href="#">Newer posts</a></li>
<li class="previous disabled"><a href="#">{{ _('Newer posts') }}</a></li>
{% endif %}
{% if posts.has_next %}
<li class="next"><a href="{{ url_for('index', page = posts.next_num) }}">Older posts</a></li>
<li class="next"><a href="{{ url_for('index', page = posts.next_num) }}">{{ _('Older posts') }}</a></li>
{% else %}
<li class="next disabled"><a href="#">Older posts</a></li>
<li class="next disabled"><a href="#">{{ _('Older posts') }}</a></li>
{% endif %}
</ul>
{% endblock %}
{% endblock %}

View File

@ -17,17 +17,17 @@ function set_openid(openid, pr)
</script>
{% include 'flash.html' %}
<div class="well">
<h3>Please Sign In</h3>
<h3>{{ _('Please Sign In') }}</h3>
<form class="form" action="" method="post" name="login">
{{form.hidden_tag()}}
<div class="help-block">Click on your OpenID provider below:</div>
<div class="help-block">{{ _('Click on your OpenID provider below:') }}</div>
<div class="control-group">
{% for pr in providers %}
<a href="javascript:set_openid('{{pr.url}}', '{{pr.name}}');"><img src="/static/img/{{pr.name.lower()}}.png" class="img-polaroid" style="margin:2px;" /></a>
{% endfor %}
</div>
<div class="control-group{% if form.errors.openid %} error{% endif %}">
<label class="control-label" for="openid">Or enter your OpenID here:</label>
<label class="control-label" for="openid">{{ _('Or enter your OpenID here:') }}</label>
<div class="controls">
{{ form.openid(size = 80, class = "span4") }}
{% for error in form.errors.openid %}
@ -38,13 +38,13 @@ function set_openid(openid, pr)
<div class="control-group">
<div class="controls">
<label class="checkbox" for="remember_me">
{{ form.remember_me }} Remember Me
{{ form.remember_me }} {{ _('Remember Me') }}
</label>
</div>
</div>
<div class="control-group">
<div class="controls">
<input class="btn btn-primary" type="submit" value="Sign In">
<input class="btn btn-primary" type="submit" value="{{ _('Sign In') }}">
</div>
</div>
</form>

View File

@ -2,7 +2,9 @@
<tr>
<td width="70px"><a href="{{url_for('user', nickname = post.author.nickname)}}"><img src="{{post.author.avatar(70)}}" /></a></td>
<td>
<p><a href="{{url_for('user', nickname = post.author.nickname)}}">{{post.author.nickname}}</a> said {{momentjs(post.timestamp).fromNow()}}:</p>
{% autoescape false %}
<p>{{ _('%(nickname)s said %(when)s:', nickname = '<a href="%s">%s</a>' % (url_for('user', nickname = post.author.nickname), post.author.nickname), when = momentjs(post.timestamp).fromNow()) }}</p>
{% endautoescape %}
<p><strong>{{post.body}}</strong></p>
</td>
</tr>

View File

@ -2,7 +2,7 @@
{% extends "base.html" %}
{% block content %}
<h1>Search results for "{{query}}":</h1>
<h1>{{ _('Search results for "%(query)s":', query = query) }}</h1>
{% include 'flash.html' %}
{% for post in results %}
{% include 'post.html' %}

View File

@ -10,15 +10,15 @@
<h1>{{user.nickname}}</h1>
{% if user.about_me %}<p>{{user.about_me}}</p>{% endif %}
{% if user.last_seen %}
<p><em>Last seen: {{momentjs(user.last_seen).calendar()}}</em></p>
<p><em>{{ _('Last seen:') }} {{ momentjs(user.last_seen).calendar() }}</em></p>
{% endif %}
<p>Followers: {{user.followers.count() - 1}} | Following: {{user.followed.count() - 1}} |
<p>{{ _('Followers:') }} {{user.followers.count() - 1}} | {{ _('Following:') }} {{user.followed.count() - 1}} |
{% if user.id == g.user.id %}
<a href="{{url_for('edit')}}">Edit your profile</a>
<a href="{{url_for('edit')}}">{{ _('Edit your profile') }}</a>
{% elif not g.user.is_following(user) %}
<a href="{{url_for('follow', nickname = user.nickname)}}">Follow</a>
<a href="{{url_for('follow', nickname = user.nickname)}}">{{ _('Follow') }}</a>
{% else %}
<a href="{{url_for('unfollow', nickname = user.nickname)}}">Unfollow</a>
<a href="{{url_for('unfollow', nickname = user.nickname)}}">{{ _('Unfollow') }}</a>
{% endif %}
</p>
</div>
@ -27,14 +27,14 @@
{% endfor %}
<ul class="pager">
{% if posts.has_prev %}
<li class="previous"><a href="{{ url_for('user', nickname = user.nickname, page = posts.prev_num) }}">Newer posts</a></li>
<li class="previous"><a href="{{ url_for('user', nickname = user.nickname, page = posts.prev_num) }}">{{ _('Newer posts') }}</a></li>
{% else %}
<li class="previous disabled"><a href="#">Newer posts</a></li>
<li class="previous disabled"><a href="#">{{ _('Newer posts') }}</a></li>
{% endif %}
{% if posts.has_next %}
<li class="next"><a href="{{ url_for('user', nickname = user.nickname, page = posts.next_num) }}">Older posts</a></li>
<li class="next"><a href="{{ url_for('user', nickname = user.nickname, page = posts.next_num) }}">{{ _('Older posts') }}</a></li>
{% else %}
<li class="next disabled"><a href="#">Older posts</a></li>
<li class="next disabled"><a href="#">{{ _('Older posts') }}</a></li>
{% endif %}
</ul>
{% endblock %}

View File

@ -0,0 +1,218 @@
# Spanish translations for PROJECT.
# Copyright (C) 2013 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2013-01-31 23:19-0800\n"
"PO-Revision-Date: 2013-01-31 23:19-0800\n"
"Last-Translator: Miguel Grinberg <miguel.grinberg@gmail.com>\n"
"Language-Team: es <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"
"X-Generator: Poedit 1.5.4\n"
#: app/__init__.py:17
msgid "Please log in to access this page."
msgstr "Por favor regístrate para acceder a esta página."
#: app/forms.py:24
msgid ""
"This nickname has invalid characters. Please use letters, numbers, dots and "
"underscores only."
msgstr ""
"Este nombre de usuario tiene caracteres inválidos. Por favor usa letras, "
"números, puntos y underscores."
#: app/forms.py:28
msgid "This nickname is already in use. Please choose another one."
msgstr "Este nombre de usuario ya esta usado. Por favor elije otro."
#: app/views.py:49
msgid "Your post is now live!"
msgstr "¡Tu artículo ha sido publicado!"
#: app/views.py:74
msgid "Invalid login. Please try again."
msgstr "Credenciales inválidas. Por favor intenta de nuevo."
#: app/views.py:107
#, python-format
msgid "User %(nickname)s not found."
msgstr "El usuario %(nickname)s no existe."
#: app/views.py:123
msgid "Your changes have been saved."
msgstr "Tus cambios han sido guardados."
#: app/views.py:139
msgid "You can't follow yourself!"
msgstr "¡No te puedes seguir a tí mismo!"
#: app/views.py:143
#, python-format
msgid "Cannot follow %(nickname)s."
msgstr "No se pudo seguir a %(nickname)s."
#: app/views.py:147
#, python-format
msgid "You are now following %(nickname)s!"
msgstr "¡Ya estás siguiendo a %(nickname)s!"
#: app/views.py:159
msgid "You can't unfollow yourself!"
msgstr "¡No te puedes dejar de seguir a tí mismo!"
#: app/views.py:163
#, python-format
msgid "Cannot unfollow %(nickname)s."
msgstr "No se pudo dejar de seguir a %(nickname)s."
#: app/views.py:167
#, python-format
msgid "You have stopped following %(nickname)s."
msgstr "Ya no sigues más a %(nickname)s."
#: app/templates/404.html:5
msgid "File Not Found"
msgstr "Archivo no encontrado"
#: app/templates/404.html:6 app/templates/500.html:7
msgid "Back"
msgstr "Volver"
#: app/templates/500.html:5
msgid "An unexpected error has occurred"
msgstr "Un error inesperado ha ocurrido"
#: app/templates/500.html:6
msgid "The administrator has been notified. Sorry for the inconvenience!"
msgstr "El administrador ha sido notificado. ¡Lo lamento!"
#: app/templates/base.html:30
msgid "Home"
msgstr "Inicio"
#: app/templates/base.html:32
msgid "Your Profile"
msgstr "Tu Perfil"
#: app/templates/base.html:33
msgid "Logout"
msgstr "Desconectarse"
#: app/templates/base.html:38
msgid "Search"
msgstr "Buscar"
#: app/templates/edit.html:5
msgid "Edit Your Profile"
msgstr "Editar Tu Perfil"
#: app/templates/edit.html:11
msgid "Your nickname:"
msgstr "Tu nombre de usuario:"
#: app/templates/edit.html:20
msgid "About yourself:"
msgstr "Acerca tuyo:"
#: app/templates/edit.html:30
msgid "Save Changes"
msgstr "Guardar"
#: app/templates/follower_email.html:2
#, python-format
msgid "%(nickname)s is now a follower."
msgstr "%(nickname)s te está siguiendo."
#: app/templates/follower_email.html:12
msgid "Regards,"
msgstr "Cordialmente,"
#: app/templates/follower_email.html:13
msgid "The <code>microblog</code> admin"
msgstr "El administrador de <code>microblog</code>"
#: app/templates/index.html:5
#, python-format
msgid "Hi, %(nickname)s!"
msgstr "¡Hola, %(nickname)s!"
#: app/templates/index.html:11
msgid "Say something:"
msgstr "Dí algo:"
#: app/templates/index.html:21
msgid "Post!"
msgstr "¡Publicar!"
#: app/templates/index.html:31 app/templates/index.html:33
#: app/templates/user.html:30 app/templates/user.html:32
msgid "Newer posts"
msgstr "Artículos nuevos"
#: app/templates/index.html:36 app/templates/index.html:38
#: app/templates/user.html:35 app/templates/user.html:37
msgid "Older posts"
msgstr "Artículos viejos"
#: app/templates/login.html:20
msgid "Please Sign In"
msgstr "Por Favor Regístrate"
#: app/templates/login.html:23
msgid "Click on your OpenID provider below:"
msgstr "Haz click en tu proveedor de OpenID:"
#: app/templates/login.html:30
msgid "Or enter your OpenID here:"
msgstr "O ingresa tu OpenID aquí:"
#: app/templates/login.html:41
msgid "Remember Me"
msgstr "Recordarme"
#: app/templates/login.html:47
msgid "Sign In"
msgstr "Ingresar"
#: app/templates/post.html:6
#, python-format
msgid "%(nickname)s said %(when)s:"
msgstr "%(nickname)s dijo %(when)s:"
#: app/templates/search_results.html:5
#, python-format
msgid "Search results for \"%(query)s\":"
msgstr "Resultados de búsqueda de \"%(query)s\":"
#: app/templates/user.html:13
msgid "Last seen:"
msgstr "Último acceso:"
#: app/templates/user.html:15
msgid "Followers:"
msgstr "Seguidores:"
#: app/templates/user.html:15
msgid "Following:"
msgstr "Siguiendo:"
#: app/templates/user.html:17
msgid "Edit your profile"
msgstr "Editar Tu Perfil"
#: app/templates/user.html:19
msgid "Follow"
msgstr "Seguir"
#: app/templates/user.html:21
msgid "Unfollow"
msgstr "Dejar de seguir"

View File

@ -1,16 +1,22 @@
from flask import render_template, flash, redirect, session, url_for, request, g
from flask.ext.login import login_user, logout_user, current_user, login_required
from app import app, db, lm, oid
from flask.ext.babel import gettext
from app import app, db, lm, oid, babel
from forms import LoginForm, EditForm, PostForm, SearchForm
from models import User, ROLE_USER, ROLE_ADMIN, Post
from datetime import datetime
from emails import follower_notification
from config import POSTS_PER_PAGE, MAX_SEARCH_RESULTS
from config import LANGUAGES
@lm.user_loader
def load_user(id):
return User.query.get(int(id))
@babel.localeselector
def get_locale():
return request.accept_languages.best_match(LANGUAGES.keys())
@app.before_request
def before_request():
g.user = current_user
@ -19,6 +25,7 @@ def before_request():
db.session.add(g.user)
db.session.commit()
g.search_form = SearchForm()
g.locale = get_locale()
@app.errorhandler(404)
def internal_error(error):
@ -39,7 +46,7 @@ def index(page = 1):
post = Post(body = form.post.data, timestamp = datetime.utcnow(), author = g.user)
db.session.add(post)
db.session.commit()
flash('Your post is now live!')
flash(gettext('Your post is now live!'))
return redirect(url_for('index'))
posts = g.user.followed_posts().paginate(page, POSTS_PER_PAGE, False)
return render_template('index.html',
@ -64,13 +71,14 @@ def login():
@oid.after_login
def after_login(resp):
if resp.email is None or resp.email == "":
flash('Invalid login. Please try again.')
flash(gettext('Invalid login. Please try again.'))
redirect(url_for('login'))
user = User.query.filter_by(email = resp.email).first()
if user is None:
nickname = resp.nickname
if nickname is None or nickname == "":
nickname = resp.email.split('@')[0]
nickname = User.make_valid_nickname(nickname)
nickname = User.make_unique_nickname(nickname)
user = User(nickname = nickname, email = resp.email, role = ROLE_USER)
db.session.add(user)
@ -96,7 +104,7 @@ def logout():
def user(nickname, page = 1):
user = User.query.filter_by(nickname = nickname).first()
if user == None:
flash('User ' + nickname + ' not found.')
flash(gettext('User %(nickname)s not found.', nickname = nickname))
return redirect(url_for('index'))
posts = user.posts.paginate(page, POSTS_PER_PAGE, False)
return render_template('user.html',
@ -112,7 +120,7 @@ def edit():
g.user.about_me = form.about_me.data
db.session.add(g.user)
db.session.commit()
flash('Your changes have been saved.')
flash(gettext('Your changes have been saved.'))
return redirect(url_for('edit'))
elif request.method != "POST":
form.nickname.data = g.user.nickname
@ -128,15 +136,15 @@ def follow(nickname):
flash('User ' + nickname + ' not found.')
return redirect(url_for('index'))
if user == g.user:
flash('You can\'t follow yourself!')
flash(gettext('You can\'t follow yourself!'))
return redirect(url_for('user', nickname = nickname))
u = g.user.follow(user)
if u is None:
flash('Cannot follow ' + nickname + '.')
flash(gettext('Cannot follow %(nickname)s.', nickname = nickname))
return redirect(url_for('user', nickname = nickname))
db.session.add(u)
db.session.commit()
flash('You are now following ' + nickname + '!')
flash(gettext('You are now following %(nickname)s!', nickname = nickname))
follower_notification(user, g.user)
return redirect(url_for('user', nickname = nickname))
@ -148,15 +156,15 @@ def unfollow(nickname):
flash('User ' + nickname + ' not found.')
return redirect(url_for('index'))
if user == g.user:
flash('You can\'t unfollow yourself!')
flash(gettext('You can\'t unfollow yourself!'))
return redirect(url_for('user', nickname = nickname))
u = g.user.unfollow(user)
if u is None:
flash('Cannot unfollow ' + nickname + '.')
flash(gettext('Cannot unfollow %(nickname)s.', nickname = nickname))
return redirect(url_for('user', nickname = nickname))
db.session.add(u)
db.session.commit()
flash('You have stopped following ' + nickname + '.')
flash(gettext('You have stopped following %(nickname)s.', nickname = nickname))
return redirect(url_for('user', nickname = nickname))
@app.route('/search', methods = ['POST'])

4
babel.cfg Normal file
View File

@ -0,0 +1,4 @@
[python: **.py]
[jinja2: **/templates/**.html]
extensions=jinja2.ext.autoescape,jinja2.ext.with_

View File

@ -1,3 +1,4 @@
# -*- coding: utf8 -*-
import os
basedir = os.path.abspath(os.path.dirname(__file__))
@ -23,6 +24,12 @@ MAIL_USE_SSL = False
MAIL_USERNAME = 'you'
MAIL_PASSWORD = 'your-password'
# available languages
LANGUAGES = {
'en': 'English',
'es': 'Español'
}
# administrator list
ADMINS = ['you@example.com']

9
tr_compile.py Executable file
View File

@ -0,0 +1,9 @@
#!flask/bin/python
import os
import sys
if sys.platform == 'win32':
pybabel = 'flask\\Scripts\\pybabel'
else:
pybabel = 'flask/bin/pybabel'
os.system(pybabel + ' compile -d app/translations')

14
tr_init.py Executable file
View File

@ -0,0 +1,14 @@
#!flask/bin/python
import os
import sys
if sys.platform == 'win32':
pybabel = 'flask\\Scripts\\pybabel'
else:
pybabel = 'flask/bin/pybabel'
if len(sys.argv) != 2:
print "usage: tr_init <language-code>"
sys.exit(1)
os.system(pybabel + ' extract -F babel.cfg -k lazy_gettext -o messages.pot app')
os.system(pybabel + ' init -i messages.pot -d app/translations -l ' + sys.argv[1])
os.unlink('messages.pot')

11
tr_update.py Executable file
View File

@ -0,0 +1,11 @@
#!flask/bin/python
import os
import sys
if sys.platform == 'wiin32':
pybabel = 'flask\\Scripts\\pybabel'
else:
pybabel = 'flask/bin/pybabel'
os.system(pybabel + ' extract -F babel.cfg -k lazy_gettext -o messages.pot app')
os.system(pybabel + ' update -i messages.pot -d app/translations')
os.unlink('messages.pot')