Ajax
这将是国际化和本地化的最后一篇文章,我们将会尽所能使得 microblog 应用程序对非英语用户可用和更加友好。
不知道大家平时有没有见过网站上有一个 “翻译” 的链接,点击后会把翻译后的内容在旁边显示给用户,这些链接触发一个实时自动翻译的内容。谷歌显示这个 “翻译” 链接是为了能够显示外国语言的搜索结果。Facebook 显示它为了能够翻译 blog 内容。今天我们将要添加同样的功能的 “翻译” 链接到我们的 microblog!
客户端 VS 服务器端在传统的沿用至今的服务器端的模型中,有一个客户端(用户的浏览器)发送请求到我们的服务器上。一个请求能够简单地请求一个页面,像当你点击 “你的信息” 链接,或者它能够让我们执行一个动作,像当用户编辑他的或者她的用户信息并且点击提交的按钮。在这两种类型的请求中服务器通过发送一个新的网页到客户端,直接或通过发出一个重定向的请求来完成这次请求。客户端接着使用新页代替目前的页面。这个循环就会重复只要用户停留在我们的网页上。我们叫这种模式为服务器端,因为服务器做了所有的工作而客户端只是在它们收到页面的时候显示出来。
在客户端模式中,我们有一个网页浏览器,再次发送请求到服务器上。服务器会像服务器端模式一样回应一个网页,但是不是所有的页面数据都是 HTML,同样也有代码,典型的就是用 Javascript 编写的。一旦客户端接收到页面会把它显示出来并且会执行携带的代码。从此,你有一个活跃的客户端,可以做自己的工作,没有接触外面的服务器。在严格的客户端,整个应用程序被下载到客户端在初始页面请求中,然后应用程序运行在客户端上不会刷新页面,只有向服务器获取或存储数据。这种类型的应用称为 单页应用 或者 SPAs。
大多数应用是这两种模式的结合体。我们的 microblog 应用程序是一个完全的服务器端应用,但是现在我们想要添加些客户端行为。为了实现实时翻译用户的 blog 内容,客户端浏览器将会发送一个请求到服务器,但是服务器将会回应一个翻译文本而且不需要页面刷新。客户端将会动态地插入翻译到当前页面。这种技术叫做 Ajax,这是 Asynchronous Javascript and XML 的简称。
翻译用户生成内容多亏了 Flask-Babel 我们现在比较好的支持了多语言。假设我们能找到愿意帮助我们的翻译器,我们可以在尽可能多的语言中发布我们的应用程序。
但是还有一个遗漏问题。因为有很多各种语言的用户使用系统,那么用户发布的 blog 内容的语言也是多种的。可能不是本语种的用户不知道内容的含义,如果我们能够提供一种自动翻译的服务这种会不会更好?
这是一个用 Ajax 服务来实现的理想的功能。我们的首页可以显示很多的 blog,它们中的一些可能是不同语言的。如果我们使用传统的服务器端模式来实现翻译的话,一个翻译的请求可能会让原始页面被新的只翻译了选择的 blog 的页面替换。在用户读完翻译后,我们必须点击回退键去获取 blog 列表。事实上请求一个翻译并不是需要更新全部的页面,这个功能让应用程序更好,因为翻译的文本是动态地插入到原始的文本下面,其他的内容不会发生变化。因此,今天我们将会实现我们的 Ajax 服务。
实现实时翻译需要几个步骤。首先,我们需要确定要翻译的文本的原语言类型。一旦我们知道原语言类型我们也就知道需不需要对一个给定的用户翻译,因为我们也知道这个用户选择的语言类型。当翻译被提供,用户希望看到它的时候,将会调用 Ajax 服务。最后一步就是客户端的 javascript 代码将会动态地把翻译文本插入到页面中。
确定 blog 语言我们首先的问题就是确定 blog 撰写的语言。这不是一门精确的科学,它不会总是能够检测的语言的类型,所以我们只能尽最大努力去做。我们将会使用 guess-language Python 模块。因此,请安装这个模块。针对 Linux 和 Mac OS X 用户:
flask/bin/pip install guess-language针对 Windows 用户:
flask\Scripts\pip install guess-language有了这个模块,我们将会扫描每一篇 blog 的内容试着猜测它的语言种类。因为我们不想一遍一遍地扫描同一篇 blog,我们将会针对每一篇 blog 只做一次,当用户提交 blog 的时候就去扫描。我们将会把每一篇 blog 的语言种类存储在数据库中。
因此让我们开始在我们的 Post 表中添加一个 language 字段:
class Post(db.Model): __searchable__ = ['body'] id = db.Column(db.Integer, primary_key = True) 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))每一次修改数据库,我们都需要做一次迁移:
$ ./db_migrate.py New migration saved as microblog/db_repository/versions/005_migration.py Current database version: 5现在我们已经在数据库中有了存储 blog 内容语言类型的地方,因此让我们检测每一个 blog 语言种类:
from guess_language import guessLanguage @app.route('http://my.oschina.net/', methods = ['GET', 'POST']) @app.route('/index', methods = ['GET', 'POST']) @app.route('/index/<int:page>', methods = ['GET', 'POST']) @login_required def index(page = 1): form = PostForm() if form.validate_on_submit(): 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!')) return redirect(url_for('index')) posts = g.user.followed_posts().paginate(page, POSTS_PER_PAGE, False) return render_template('index.html', title = 'Home', form = form, posts = posts)如果语言猜测不能工作或者返回一个非预期的结果,我们会在数据库中存储一个空的字符串。
显示 “翻译” 链接