Python flask web application with multilanguages support by host and prefix

后端 未结 4 1682
隐瞒了意图╮
隐瞒了意图╮ 2020-12-18 11:24

I have one server with flask application instance and have several domain which mapped to this server by DNS.

My site must support several languages by host and pref

相关标签:
4条回答
  • 2020-12-18 11:36

    It's in the official doc: http://flask.pocoo.org/docs/patterns/urlprocessors/ (This is basically the same answer as Matthew Scragg's).

    0 讨论(0)
  • 2020-12-18 11:38

    My own solution:

    from flask import Flask, g, render_template, redirect, request
    
    
    app = Flask(__name__)
    
    default_language = 'en'
    language_urls = {
        'en': 'mysite.com',
        'fr': 'mysite.com/fr',
        'ru': 'mysite.ru',
        'by': 'mysite.ru/by',
    }
    languages = ','.join(language_urls.keys())
    
    
    def get_language_by_request(request_host, request_path):
        '''
        Looking bad, but work.
        I cab't use request.view_args there,
        because this can't detect language for 404 pages
        like mysite.com/fr/unknown-page
        '''
        request_host_path = request_host + request_path
        request_paths = request_host_path.split('/', 2)
        if (len(request_paths) > 1 and request_paths[1] in language_urls.keys()):
            request_language_prefix = request_paths[1]
            return request_language_prefix
        for language, url in language_urls.items():
            host_prefix = url.split('/')
            if len(host_prefix) == 1:
                host, = host_prefix
                if request_host == host:
                    return language
        return default_language
    
    
    def get_language_url_parameter_value(language, request_host):
        host_prefix = language_urls[language]
        if host_prefix == request_host:
            return None
        return language
    
    
    def get_redirection_url_by_request(request_host, request_path, request_url):
        '''
        Looking bad, but work.
        I cab't use request.view_args there,
        because this can't detect language for 404 pages
        like mysite.com/fr/unknown-page
        '''
        request_host_path = request_host + request_path
        request_paths = request_host_path.split('/', 2)
        request_language_prefix = None
        if (len(request_paths) > 1 and request_paths[1] in language_urls.keys()):
            request_language_prefix = request_paths[1]
        hosts = []
        for language, url in language_urls.items():
            host_prefix = url.split('/')
            if len(host_prefix) == 1:
                host, = host_prefix
                language_prefix = None
            else:
                host, language_prefix = host_prefix
            if request_host == host and request_language_prefix == language_prefix:
                return None
            hosts.append(host)
        if request_host not in hosts:
            return None
        if request_language_prefix:
            request_host_prefix = request_host + '/' + request_language_prefix
            host_prefix = language_urls[request_language_prefix]
            return request_url.replace(request_host_prefix, host_prefix)
        return None
    
    
    @app.url_defaults
    def set_language_in_url(endpoint, values):
        if '_lang' not in values and hasattr(g, 'language_url_value'):
            values['_lang'] = g.language_url_value
    
    
    @app.url_value_preprocessor
    def get_language_from_url(endpoint, values):
        g.language = get_language_by_request(request.host, request.path)
        g.language_url_value = get_language_url_parameter_value(g.language, request.host)
        if values and '_lang' in values:
            del values['_lang']
    
    
    @app.before_request
    def check_language_redirection():
        redirection_url = get_redirection_url_by_request(request.host, request.path, request.url)
        return redirect(redirection_url) if redirection_url else None
    
    
    @app.route('/')
    @app.route('/<any(%s):_lang>/' % languages)
    def home():
        return render_template('home.html')
    
    
    @app.route('/other/')
    @app.route('/<any(%s):_lang>/other/' % languages)
    def other():
        return render_template('other.html')
    

    I don't use blueprints there because I also use flask-login and I can't set several login pages with different languages for each blueprint. For example if page required login, flask redirect me to login page and I must update language for this page. Also login pages can't be as mysite.com/login, mysite.com/fr/login and etc without several redirections.

    UPD: I can't use request.view_args for detect language or redirection, because on this case I can't detect language for error pages as mysite.com/fr/wrong-page-there (can't detect endpoint and view_args). To avoid this problem I can use hask: add url rule as /<lang_code>/<path:path> and raise 404 error there.

    0 讨论(0)
  • 2020-12-18 11:39

    I worked on something similar few months back. I modified it a bit and pushed to github. You can do what codegeek suggested if you are unable to make your templates language neutral. With this method you can cut down on the template files needed.

    https://github.com/scragg0x/Flask-Localisation-Example

    mysite.py

    from flask import Flask, Blueprint, g, redirect, request
    
    app = Flask(__name__)
    
    mod = Blueprint('mysite', __name__, url_prefix='/<lang_code>')
    
    sites = {
        'mysite.com': 'en',
        'myothersite.com': 'fr'
    }
    
    @app.url_defaults
    def add_language_code(endpoint, values):
        values.setdefault('lang_code', g.lang_code)
    
    @app.url_value_preprocessor
    def pull_lang_code(endpoint, values):
        url = request.url.split('/', 3)
        g.lang_code = sites[url[2]]
    
    @mod.url_defaults
    def add_language_code(endpoint, values):
        values.setdefault('lang_code', g.lang_code)
    
    @mod.url_value_preprocessor
    def pull_lang_code(endpoint, values):
        g.lang_code = values.pop('lang_code')
    
    @app.route('/')
    @mod.route('/')
    def index():
        # Use g.lang_code to pull localized data for template
        return 'lang = %s' % g.lang_code
    
    app.register_blueprint(mod)
    

    tests.py

    import os
    import unittest
    import re
    import requests
    import urllib2
    import json
    from mysite import app
    
    class MySiteTestCase(unittest.TestCase):
    
        def setUp(self):
            app.config['TESTING'] = True
            app.config['SERVER_NAME'] = 'mysite.com'
            self.domain = 'http://mysite.com/'
            self.app = app.test_client()
    
        def tearDown(self):
            pass
    
        def test_en_index(self):
            rv = self.app.get('/en/', self.domain)
            self.assertEqual(rv.data, 'lang = en')
            print self.domain, rv.data
    
        def test_fr_index(self):
            rv = self.app.get('/fr/', self.domain)
            self.assertEqual(rv.data, 'lang = fr')
            print self.domain, rv.data
    
        def test_default(self):
            rv = self.app.get('/', self.domain)
            self.assertEqual(rv.data, 'lang = en')
            print self.domain, rv.data
    
    class MyOtherSiteTestCase(unittest.TestCase):
    
        def setUp(self):
            app.config['TESTING'] = True
            app.config['SERVER_NAME'] = 'myothersite.com'
            self.domain = 'http://myothersite.com/'
            self.app = app.test_client()
    
        def tearDown(self):
            pass
    
        def test_en_index(self):
            rv = self.app.get('/en/', self.domain)
            self.assertEqual(rv.data, 'lang = en')
            print self.domain, rv.data
    
        def test_fr_index(self):
            rv = self.app.get('/fr/', self.domain)
            self.assertEqual(rv.data, 'lang = fr')
            print self.domain, rv.data
    
        def test_default(self):
            rv = self.app.get('/', self.domain)
            self.assertEqual(rv.data, 'lang = fr')
            print self.domain, rv.data
    
    if __name__ == '__main__':
        unittest.main()
    
    0 讨论(0)
  • 2020-12-18 11:56

    Disclaimer: This code is not tested. I am just giving you a ballpark idea of how to approach this.

    I suggest you use blueprints in combination with an extension like Flask-Babel. For example, you can do something like:

    views.py

    mysitebp = Blueprint('mysitebp',__name__)
    

    Then in your application package (usually __init__.py) , you can do:

    __init__.py

    from mysite.views import mysitebp
    app = Flask(__name__)
    app.register_blueprint(mysitebp,url_prefix='/en/',template_folder='en')
    app.register_blueprint(mysitebp,url_prefix='/fr',template_folder='fr')
    

    ..and so on

    Your directory structure could look like:

    mysite/
    __init__.py
    views.py
    templates/
        base.html
        404.html
        en/
            en.html
        fr/
            french.html
    

    Flask-Babel would help you translate the 404.html etc.

    0 讨论(0)
提交回复
热议问题