ajax translations
This commit is contained in:
parent
22ba5eed17
commit
4081db19e1
|
@ -84,6 +84,7 @@ class Post(db.Model):
|
|||
body = db.Column(db.String(140))
|
||||
timestamp = db.Column(db.DateTime)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||||
language = db.Column(db.String(5))
|
||||
|
||||
def __repr__(self):
|
||||
return '<Post %r>' % (self.body)
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 673 B |
|
@ -15,6 +15,25 @@
|
|||
<script src="/static/js/moment-{{g.locale}}.min.js"></script>
|
||||
{% endif %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script>
|
||||
function translate(sourceLang, destLang, sourceId, destId, loadingId) {
|
||||
$(destId).hide();
|
||||
$(loadingId).show();
|
||||
$.post('/translate', {
|
||||
text: $(sourceId).text(),
|
||||
sourceLang: sourceLang,
|
||||
destLang: destLang
|
||||
}).done(function(translated) {
|
||||
$(destId).text(translated['text'])
|
||||
$(loadingId).hide();
|
||||
$(destId).show();
|
||||
}).fail(function() {
|
||||
$(destId).text("{{ _('Error: Could not contact server.') }}");
|
||||
$(loadingId).hide();
|
||||
$(destId).show();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
|
@ -48,4 +67,3 @@
|
|||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
@ -5,7 +5,15 @@
|
|||
{% 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>
|
||||
<p><strong><span id="post{{post.id}}">{{post.body}}</span></strong></p>
|
||||
{% if post.language != None and post.language != '' and post.language != g.locale %}
|
||||
<div>
|
||||
<span id="translation{{post.id}}">
|
||||
<a href="javascript:translate('{{post.language}}', '{{g.locale}}', '#post{{post.id}}', '#translation{{post.id}}', '#loading{{post.id}}');">{{ _('Translate') }}</a>
|
||||
</span>
|
||||
<img id="loading{{post.id}}" style="display: none" src="/static/img/loading.gif">
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
import urllib, httplib
|
||||
import json
|
||||
from app import app
|
||||
from flask.ext.babel import gettext
|
||||
from config import MS_TRANSLATOR_CLIENT_ID, MS_TRANSLATOR_CLIENT_SECRET
|
||||
|
||||
def microsoft_translate(text, sourceLang, destLang):
|
||||
if MS_TRANSLATOR_CLIENT_ID == "" or MS_TRANSLATOR_CLIENT_SECRET == "":
|
||||
return gettext('Error: translation service not configured.')
|
||||
try:
|
||||
# get access token
|
||||
params = urllib.urlencode({
|
||||
'client_id': MS_TRANSLATOR_CLIENT_ID,
|
||||
'client_secret': MS_TRANSLATOR_CLIENT_SECRET,
|
||||
'scope': 'http://api.microsofttranslator.com',
|
||||
'grant_type': 'client_credentials'
|
||||
})
|
||||
conn = httplib.HTTPSConnection("datamarket.accesscontrol.windows.net")
|
||||
conn.request("POST", "/v2/OAuth2-13", params)
|
||||
response = json.loads (conn.getresponse().read())
|
||||
token = response[u'access_token']
|
||||
|
||||
# translate
|
||||
conn = httplib.HTTPConnection('api.microsofttranslator.com')
|
||||
params = {
|
||||
'appId': 'Bearer ' + token,
|
||||
'from': sourceLang,
|
||||
'to': destLang,
|
||||
'text': text.encode("utf-8")
|
||||
}
|
||||
conn.request("GET", '/V2/Ajax.svc/Translate?' + urllib.urlencode(params))
|
||||
response = json.loads("{\"response\":" + conn.getresponse().read().decode('utf-8-sig') + "}")
|
||||
return response["response"]
|
||||
except:
|
||||
#return gettext('Error: Unexpected error.')
|
||||
raise
|
||||
|
||||
def google_translate(text, sourceLang, destLang):
|
||||
if not app.debug:
|
||||
return gettext('Error: translation service not available.')
|
||||
try:
|
||||
params = urllib.urlencode({
|
||||
'client': 't',
|
||||
'text': text.encode("utf-8"),
|
||||
'sl': sourceLang,
|
||||
'tl': destLang,
|
||||
'ie': 'UTF-8',
|
||||
'oe': 'UTF-8'
|
||||
})
|
||||
conn = httplib.HTTPSConnection("translate.google.com")
|
||||
conn.request("GET", "/translate_a/t?" + params, headers = { 'User-Agent': 'Mozilla/5.0' })
|
||||
httpresponse = conn.getresponse().read().replace(",,,", ",\"\",\"\",").replace(",,", ",\"\",")
|
||||
response = json.loads("{\"response\":" + httpresponse + "}")
|
||||
return response["response"][0][0][0]
|
||||
except:
|
||||
return gettext('Error: Unexpected error.')
|
|
@ -7,8 +7,8 @@ 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"
|
||||
"POT-Creation-Date: 2013-02-19 22:11-0800\n"
|
||||
"PO-Revision-Date: 2013-02-19 22:12-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"
|
||||
|
@ -34,47 +34,59 @@ msgstr ""
|
|||
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
|
||||
#: app/translate.py:9
|
||||
msgid "Error: translation service not configured."
|
||||
msgstr "Error: el servicio de traducción no está configurado."
|
||||
|
||||
#: app/translate.py:35 app/translate.py:55
|
||||
msgid "Error: Unexpected error."
|
||||
msgstr "Error: Un error inesperado ha ocurrido."
|
||||
|
||||
#: app/translate.py:39
|
||||
msgid "Error: translation service not available."
|
||||
msgstr "Error: servicio de traducción no disponible."
|
||||
|
||||
#: app/views.py:56
|
||||
msgid "Your post is now live!"
|
||||
msgstr "¡Tu artículo ha sido publicado!"
|
||||
|
||||
#: app/views.py:74
|
||||
#: app/views.py:81
|
||||
msgid "Invalid login. Please try again."
|
||||
msgstr "Credenciales inválidas. Por favor intenta de nuevo."
|
||||
|
||||
#: app/views.py:107
|
||||
#: app/views.py:114
|
||||
#, python-format
|
||||
msgid "User %(nickname)s not found."
|
||||
msgstr "El usuario %(nickname)s no existe."
|
||||
|
||||
#: app/views.py:123
|
||||
#: app/views.py:130
|
||||
msgid "Your changes have been saved."
|
||||
msgstr "Tus cambios han sido guardados."
|
||||
|
||||
#: app/views.py:139
|
||||
#: app/views.py:146
|
||||
msgid "You can't follow yourself!"
|
||||
msgstr "¡No te puedes seguir a tí mismo!"
|
||||
|
||||
#: app/views.py:143
|
||||
#: app/views.py:150
|
||||
#, python-format
|
||||
msgid "Cannot follow %(nickname)s."
|
||||
msgstr "No se pudo seguir a %(nickname)s."
|
||||
|
||||
#: app/views.py:147
|
||||
#: app/views.py:154
|
||||
#, python-format
|
||||
msgid "You are now following %(nickname)s!"
|
||||
msgstr "¡Ya estás siguiendo a %(nickname)s!"
|
||||
|
||||
#: app/views.py:159
|
||||
#: app/views.py:166
|
||||
msgid "You can't unfollow yourself!"
|
||||
msgstr "¡No te puedes dejar de seguir a tí mismo!"
|
||||
|
||||
#: app/views.py:163
|
||||
#: app/views.py:170
|
||||
#, python-format
|
||||
msgid "Cannot unfollow %(nickname)s."
|
||||
msgstr "No se pudo dejar de seguir a %(nickname)s."
|
||||
|
||||
#: app/views.py:167
|
||||
#: app/views.py:174
|
||||
#, python-format
|
||||
msgid "You have stopped following %(nickname)s."
|
||||
msgstr "Ya no sigues más a %(nickname)s."
|
||||
|
@ -95,19 +107,23 @@ msgstr "Un error inesperado ha ocurrido"
|
|||
msgid "The administrator has been notified. Sorry for the inconvenience!"
|
||||
msgstr "El administrador ha sido notificado. ¡Lo lamento!"
|
||||
|
||||
#: app/templates/base.html:30
|
||||
#: app/templates/base.html:31
|
||||
msgid "Error: Could not contact server."
|
||||
msgstr "Error: No es posible contactar al servidor."
|
||||
|
||||
#: app/templates/base.html:49
|
||||
msgid "Home"
|
||||
msgstr "Inicio"
|
||||
|
||||
#: app/templates/base.html:32
|
||||
#: app/templates/base.html:51
|
||||
msgid "Your Profile"
|
||||
msgstr "Tu Perfil"
|
||||
|
||||
#: app/templates/base.html:33
|
||||
#: app/templates/base.html:52
|
||||
msgid "Logout"
|
||||
msgstr "Desconectarse"
|
||||
|
||||
#: app/templates/base.html:38
|
||||
#: app/templates/base.html:57
|
||||
msgid "Search"
|
||||
msgstr "Buscar"
|
||||
|
||||
|
@ -188,6 +204,10 @@ msgstr "Ingresar"
|
|||
msgid "%(nickname)s said %(when)s:"
|
||||
msgstr "%(nickname)s dijo %(when)s:"
|
||||
|
||||
#: app/templates/post.html:12
|
||||
msgid "Translate"
|
||||
msgstr "Traducir"
|
||||
|
||||
#: app/templates/search_results.html:5
|
||||
#, python-format
|
||||
msgid "Search results for \"%(query)s\":"
|
||||
|
|
24
app/views.py
24
app/views.py
|
@ -1,4 +1,4 @@
|
|||
from flask import render_template, flash, redirect, session, url_for, request, g
|
||||
from flask import render_template, flash, redirect, session, url_for, request, g, jsonify
|
||||
from flask.ext.login import login_user, logout_user, current_user, login_required
|
||||
from flask.ext.babel import gettext
|
||||
from app import app, db, lm, oid, babel
|
||||
|
@ -6,8 +6,9 @@ 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
|
||||
from guess_language import guessLanguage
|
||||
from translate import microsoft_translate
|
||||
from config import POSTS_PER_PAGE, MAX_SEARCH_RESULTS, LANGUAGES
|
||||
|
||||
@lm.user_loader
|
||||
def load_user(id):
|
||||
|
@ -43,7 +44,13 @@ def internal_error(error):
|
|||
def index(page = 1):
|
||||
form = PostForm()
|
||||
if form.validate_on_submit():
|
||||
post = Post(body = form.post.data, timestamp = datetime.utcnow(), author = g.user)
|
||||
language = guessLanguage(form.post.data)
|
||||
if language == 'UNKNOWN' or len(language) > 5:
|
||||
language = ''
|
||||
post = Post(body = form.post.data,
|
||||
timestamp = datetime.utcnow(),
|
||||
author = g.user,
|
||||
language = language)
|
||||
db.session.add(post)
|
||||
db.session.commit()
|
||||
flash(gettext('Your post is now live!'))
|
||||
|
@ -182,3 +189,12 @@ def search_results(query):
|
|||
query = query,
|
||||
results = results)
|
||||
|
||||
@app.route('/translate', methods = ['POST'])
|
||||
@login_required
|
||||
def translate():
|
||||
return jsonify({
|
||||
'text': microsoft_translate(
|
||||
request.form['text'],
|
||||
request.form['sourceLang'],
|
||||
request.form['destLang']) })
|
||||
|
||||
|
|
|
@ -30,6 +30,10 @@ LANGUAGES = {
|
|||
'es': 'Español'
|
||||
}
|
||||
|
||||
# microsoft translation service
|
||||
MS_TRANSLATOR_CLIENT_ID = '' # enter your MS translator app id here
|
||||
MS_TRANSLATOR_CLIENT_SECRET = '' # enter your MS translator app secret here
|
||||
|
||||
# administrator list
|
||||
ADMINS = ['you@example.com']
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
from sqlalchemy import *
|
||||
from migrate import *
|
||||
|
||||
|
||||
from migrate.changeset import schema
|
||||
pre_meta = MetaData()
|
||||
post_meta = MetaData()
|
||||
post = Table('post', post_meta,
|
||||
Column('id', Integer, primary_key=True, nullable=False),
|
||||
Column('body', String(length=140)),
|
||||
Column('timestamp', DateTime),
|
||||
Column('user_id', Integer),
|
||||
Column('language', String(length=5)),
|
||||
)
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
# Upgrade operations go here. Don't create your own engine; bind
|
||||
# migrate_engine to your metadata
|
||||
pre_meta.bind = migrate_engine
|
||||
post_meta.bind = migrate_engine
|
||||
post_meta.tables['post'].columns['language'].create()
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
# Operations to reverse the above upgrade go here.
|
||||
pre_meta.bind = migrate_engine
|
||||
post_meta.bind = migrate_engine
|
||||
post_meta.tables['post'].columns['language'].drop()
|
1
setup.py
1
setup.py
|
@ -18,4 +18,5 @@ subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'sqlalchemy-migra
|
|||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'flask-whooshalchemy'])
|
||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'flask-wtf'])
|
||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'flask-babel'])
|
||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'guess-language'])
|
||||
subprocess.call([os.path.join('flask', bin, 'pip'), 'install', 'flup'])
|
||||
|
|
7
tests.py
7
tests.py
|
@ -1,4 +1,6 @@
|
|||
#!flask/bin/python
|
||||
# -*- coding: utf8 -*-
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from datetime import datetime, timedelta
|
||||
|
@ -6,6 +8,7 @@ from datetime import datetime, timedelta
|
|||
from config import basedir
|
||||
from app import app, db
|
||||
from app.models import User, Post
|
||||
from app.translate import microsoft_translate
|
||||
|
||||
class TestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
|
@ -113,5 +116,9 @@ class TestCase(unittest.TestCase):
|
|||
assert f3 == [p4, p3]
|
||||
assert f4 == [p4]
|
||||
|
||||
def test_translation(self):
|
||||
assert microsoft_translate(u'English', 'en', 'es') == u'Inglés'
|
||||
assert microsoft_translate(u'Español', 'es', 'en') == u'Spanish'
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Reference in New Issue