├── LICENCE
├── README.md
├── README.rst
├── demo
├── demo.py
├── templates
│ └── locale.html
└── translations
│ └── tr_TR.csv
├── flask_locale
├── __init__.py
└── version.py
├── requirements.txt
├── setup.cfg
└── setup.py
/LICENCE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015 by Erkan Durmus.
2 |
3 | Some rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are
7 | met:
8 |
9 | * Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above
13 | copyright notice, this list of conditions and the following
14 | disclaimer in the documentation and/or other materials provided
15 | with the distribution.
16 |
17 | * The names of the contributors may not be used to endorse or
18 | promote products derived from this software without specific
19 | prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Flask-Locale
2 |
3 | Implements i18n and l10n support for Flask. This is based on the old [Flask-Locale](http://github.com/whtsky/whtsky-locale/) extension. Uses files or database to get translations.
4 |
5 | You can use this extension to translate your applications really easily. No babel preperation is needed. Just put your English text and its translation in a file.
6 |
7 | ## Install
8 |
9 | ```sh
10 | pip install Flask-Locale
11 | ```
12 |
13 | ## Quick Start
14 | - Py3 ready
15 | - For very quick test look at `demo` directory.
16 |
17 | - Create a directory `translations` at app root.
18 | - Create file `translations/tr_TR.csv` with this content:
19 |
20 | ```csv
21 | "Hello %(name)s","Merhaba %(name)s"
22 | "Hello","Merhaba"
23 | ```
24 | - Create `templates` directory at app root.
25 |
26 | - Create `locale.html` file with this content:
27 |
28 | ```html
29 |
30 |
31 |
32 |
33 | Flask-Locale
34 |
35 |
36 | Translate with parameters in template
37 | {{ _('Hello %(name)s', name=name) }}
38 |
39 | Translated in Python Code:
40 | {{ py_translated }}
41 |
42 |
43 | ```
44 | -- Create your application main file `demo.py`:
45 |
46 | ```py
47 | # -*- coding: utf-8 -*-
48 |
49 | from flask import Flask, request, render_template, g
50 | from flask_locale import Locale, _
51 |
52 | app = Flask(__name__)
53 | app.config['LOCALE_PATH'] = 'translations'
54 |
55 | locale = Locale(app)
56 |
57 |
58 | @locale.localeselector
59 | def get_locale():
60 | # if a user is logged in, use the locale from the user settings
61 | user = getattr(g, 'user', None)
62 | if user is not None:
63 | return user.locale
64 | # otherwise try to guess the language from the user accept
65 | # header the browser transmits. We support tr/fr/en in this
66 | # example. The best match wins.
67 | return request.accept_languages.best_match(['tr_TR', 'fr_FR', 'en_US'])
68 |
69 |
70 | @app.route("/")
71 | def index():
72 | # How we do translation in python code:
73 | py_translated = _('Hello')
74 | # How we do translation in template:
75 | return render_template('locale.html', name='Erkan', py_translated=py_translated)
76 |
77 |
78 | if __name__ == '__main__':
79 | app.run(debug=True)
80 |
81 | ```
82 |
83 | - Run yout app:
84 |
85 | ```sh
86 | python demo.py
87 | ```
88 |
89 | - Now access your app: `http://127.0.0.1:5000/`
90 |
91 | ## Usage
92 |
93 | ### Loading translations from a file:
94 | Loads translations from CSV files having locale extension in a directory. File should be `utf-8` encoded.
95 |
96 | Translations are strings with optional Python-style named placeholders (e.g., ``"My name is %(name)s"``) and their associated translations.
97 |
98 | The directory should have translation files of the form filename: LOCALE, e.g. tr_TR.
99 |
100 | Translation files should have two or three columns: string, translation, and an optional plural indicator. Plural indicators should be one of ``"plural"`` or ``"singular"``.
101 |
102 | A given string can have both singular and plural forms. For example ``"%(name)s liked this"`` may have a different verb conjugation depending on whether %(name)s is one name or a list of names. There should be two rows in the CSV file for that string, one with plural indicator "singular", and one "plural".
103 |
104 | For strings with no verbs that would change on translation, simply
105 | use ``"unknown"`` or the empty string (or don't include the column at all).
106 |
107 | The file is read using the csv module in the default "excel" dialect.
108 | In this format there should not be spaces after the commas.
109 |
110 | Example translation tr_TR.csv::
111 |
112 | ```
113 | "I love you","Seni seviyorum"
114 | "%(name)s liked these","A %(name)s bunları sevdi","plural"
115 | "%(name)s liked this","A %(name)s bunu sevdi","singular"
116 | ```
117 |
118 | ### Loading translations from database:
119 |
120 | ```py
121 | @locale.db_loader
122 | def get_translations():
123 | """Translations selector for db"""
124 | sql = select(
125 | [Locale.c.code, TranslationKey.c.name, Translation.c.translated, Translation.c.singular],
126 | from_obj=[Locale.join(Translation).join(TranslationKey)])
127 | q = db.session.execute(sql)
128 | data = q.fetchall()
129 | q.close()
130 | return list(data)
131 | ```
132 |
133 | ### Reloading translations
134 |
135 | When user's locale is changed, call `refresh()` method:
136 |
137 | ```py
138 | user.locale = request.form['locale']
139 | locale.refresh()
140 | flash(_('Language is changed'))
141 | ```
142 |
143 | ### Translate Functions
144 |
145 | `translate()` (or its alias `_()`) method does a lazy translation, that means its actual translate function is called when you access it. So you can use translate functions in your forms before Flask-Locale is initialized.
146 |
147 |
148 | ```py
149 | from flask.ext.wtf import Form
150 | from wtforms.fields import TextField, PasswordField
151 | from wtforms.validators import Required, Email
152 | from extensions import _
153 |
154 | class EmailPasswordForm(Form):
155 | email = TextField(_('Email'), validators=[Required(), Email()])
156 | password = PasswordField(_('Password'), validators=[Required()])
157 | ```
158 |
159 | If you want immediate translation, use `do_translate` method.
160 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | Flask-Locale
2 | =================
3 |
4 | Implements i18n and l10n support for Flask. This is based on the old [Flask-Locale](http://github.com/whtsky/whtsky-locale/) extension. Uses files or database to get translations.
5 |
6 | You can use this extension to translate your applications really easily. No babel preperation is needed. Just put your English text and its translation in a file.
7 |
8 | Install
9 | -------
10 |
11 | pip install Flask-Locale
12 |
13 |
14 | Quick Start
15 | -----------
16 |
17 | - Py3 ready
18 | - For very quick test look at `demo` directory.
19 |
20 | - Create a directory `translations` at app root.
21 | - Create file `translations/tr_TR.csv` with this content:
22 |
23 |
24 | "Hello %(name)s","Merhaba %(name)s"
25 |
26 | "Hello","Merhaba"
27 |
28 | - Create `templates` directory at app root.
29 |
30 | - Create `locale.html` file with this content:
31 |
32 | >>>
33 |
34 |
35 |
36 |
37 | Flask-Locale
38 |
39 |
40 | Translate with parameters in template
41 | {{ _('Hello %(name)s', name=name) }}
42 |
43 | Translated in Python Code:
44 | {{ py_translated }}
45 |
46 |
47 |
48 | -- Create your application main file `demo.py`:
49 |
50 | >>>
51 | # -*- coding: utf-8 -*-
52 | from flask import Flask, request, render_template, g
53 | from flask_locale import Locale, _
54 | app = Flask(__name__)
55 | app.config['LOCALE_PATH'] = 'translations'
56 | locale = Locale(app)
57 | @locale.localeselector
58 | def get_locale():
59 | # if a user is logged in, use the locale from the user settings
60 | user = getattr(g, 'user', None)
61 | if user is not None:
62 | return user.locale
63 | # otherwise try to guess the language from the user accept
64 | # header the browser transmits. We support tr/fr/en in this
65 | # example. The best match wins.
66 | return request.accept_languages.best_match(['tr_TR', 'fr_FR', 'en_US'])
67 | @app.route("/")
68 | def index():
69 | # How we do translation in python code:
70 | py_translated = _('Hello')
71 | # How we do translation in template:
72 | return render_template('locale.html', name='Erkan',
73 | py_translated=py_translated)
74 | if __name__ == '__main__':
75 | app.run(debug=True)
76 |
77 | - Run your app:
78 |
79 | >>> python demo.py
80 |
81 |
82 | - Now access your app: `http://127.0.0.1:5000/`
83 |
84 | Usage
85 | -----
86 |
87 | **Loading translations from a file:**
88 |
89 | Loads translations from CSV files having locale extension in a directory. File should be `utf-8` encoded.
90 |
91 | Translations are strings with optional Python-style named placeholders (e.g., ``"My name is %(name)s"``) and their associated translations.
92 |
93 | The directory should have translation files of the form filename: LOCALE, e.g. tr_TR.
94 |
95 | Translation files should have two or three columns: string, translation, and an optional plural indicator. Plural indicators should be one of ``"plural"`` or ``"singular"``.
96 |
97 | A given string can have both singular and plural forms. For example ``"%(name)s liked this"`` may have a different verb conjugation depending on whether %(name)s is one name or a list of names. There should be two rows in the CSV file for that string, one with plural indicator "singular", and one "plural".
98 |
99 | For strings with no verbs that would change on translation, simply
100 | use ``"unknown"`` or the empty string (or don't include the column at all).
101 |
102 | The file is read using the csv module in the default "excel" dialect.
103 | In this format there should not be spaces after the commas.
104 |
105 | Example translation tr_TR.csv:
106 |
107 | >>>
108 | "I love you","Seni seviyorum"
109 | "%(name)s liked these","A %(name)s bunları sevdi","plural"
110 | "%(name)s liked this","A %(name)s bunu sevdi","singular"
111 |
112 |
113 | **Loading translations from database:**
114 |
115 |
116 | >>>
117 | @locale.db_loader
118 | def get_translations():
119 | """Translations selector for db"""
120 | sql = select(
121 | [Locale.c.code, TranslationKey.c.name, Translation.c.translated, Translation.c.singular],
122 | from_obj=[Locale.join(Translation).join(TranslationKey)])
123 | q = db.session.execute(sql)
124 | data = q.fetchall()
125 | q.close()
126 | return list(data)
127 |
128 |
129 | **Reloading translations**
130 |
131 | When user's locale is changed, call `refresh()` method:
132 |
133 | >>>
134 | user.locale = request.form['locale']
135 | locale.refresh()
136 | flash(_('Language is changed'))
137 |
138 | **Translate Functions**
139 |
140 | `translate()` (or its alias `_()`) method does a lazy translation, that means its actual translate function is called when you access it. So you can use translate functions in your forms before Flask-Locale is initialized.
141 |
142 |
143 | >>>
144 | from flask.ext.wtf import Form
145 | from wtforms.fields import TextField, PasswordField
146 | from wtforms.validators import Required, Email
147 | from extensions import _
148 | class EmailPasswordForm(Form):
149 | email = TextField(_('Email'), validators=[Required(), Email()])
150 | password = PasswordField(_('Password'), validators=[Required()])
151 |
152 |
153 | If you want immediate translation, use `do_translate` method.
154 |
--------------------------------------------------------------------------------
/demo/demo.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from flask import Flask, request, render_template, g, session, redirect, current_app
4 | from flask_locale import Locale, _
5 |
6 | app = Flask(__name__)
7 | # DEFAULT_LOCALE is the language used for keys ins translation files:
8 | app.config['DEFAULT_LOCALE'] = 'tr_TR'
9 | app.config['LOCALE_PATH'] = 'translations'
10 | app.config['SECRET_KEY'] = 'translations****'
11 |
12 | locale = Locale(app)
13 |
14 |
15 | @locale.localeselector
16 | def get_locale():
17 | # if a user is logged in, use the locale from the session
18 | # define a default value instead of None to set it to specific locale if not setting is found.
19 | locale_code = session.get('locale', None)
20 | if locale_code is not None:
21 | current_app.logger.info("Locale is: %s" % locale_code)
22 | return locale_code
23 |
24 | # otherwise try to guess the language from the user accept
25 | # header the browser transmits. We support tr/fr/en in this
26 | # example. The best match wins.
27 | locale_code = request.accept_languages.best_match(['tr_TR', 'fr_FR', 'en_US'])
28 | current_app.logger.info("Locale match: %s" % locale_code)
29 | return locale_code
30 |
31 |
32 | @app.route("/")
33 | def index():
34 | # How we do translation in python code:
35 | py_translated = _('Hello')
36 | # How we do translation in template:
37 | return render_template('locale.html', name='Erkan', py_translated=py_translated)
38 |
39 |
40 | @app.route("/locale")
41 | def change_locale():
42 | new_locale = request.args.get('locale', None)
43 | session['locale'] = new_locale
44 | return redirect('/')
45 |
46 |
47 | if __name__ == '__main__':
48 | app.run(debug=True)
49 |
--------------------------------------------------------------------------------
/demo/templates/locale.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Flask-Locale
6 |
7 |
8 | Translate with parameters in template
9 | {{ _('Hello %(name)s', name=name) }}
10 |
11 | Translated in Python Code:
12 | {{ py_translated }}
13 |
14 |
--------------------------------------------------------------------------------
/demo/translations/tr_TR.csv:
--------------------------------------------------------------------------------
1 | "Hello %(name)s","Merhaba %(name)s"
2 | "Hello","Merhaba"
--------------------------------------------------------------------------------
/flask_locale/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Flask-Locale
4 | ----
5 |
6 |
7 | Some code is from Tornado.Locale
8 | """
9 |
10 | import os
11 | import sys
12 | import re
13 | import csv
14 | import unicodedata
15 |
16 | from flask import _request_ctx_stack, request
17 | from speaklater import make_lazy_string
18 |
19 | __all__ = ('Locale', 'refresh', 'translate', 'to_unicode', '_', 'do_translate')
20 | is_python3 = sys.version_info.major == 3
21 | if is_python3:
22 | unicode = str
23 |
24 |
25 | def get_app():
26 | ctx = _request_ctx_stack.top
27 | if not ctx:
28 | return None
29 | return ctx.app
30 |
31 |
32 | def to_unicode(value):
33 | """Converts a string argument to a unicode string.
34 |
35 | If the argument is already a unicode string or None, it is returned
36 | unchanged. Otherwise it must be a byte string and is decoded as utf8.
37 | """
38 | if isinstance(value, unicode):
39 | return value
40 | assert isinstance(value, bytes)
41 | return value.decode("utf-8")
42 |
43 |
44 | class Locale(object):
45 | """Central controller class that can be used to configure how
46 | Flask-Locale behaves. Each application that wants to use Flask-Locale
47 | has to create, or run :meth:`init_app` on, an instance of this class
48 | after the configuration was initialized.
49 | """
50 |
51 | def __init__(self, app=None, default_locale='en_US', configure_jinja=True):
52 | self._default_locale = default_locale
53 | self._configure_jinja = configure_jinja
54 | self._supported_locales = frozenset(default_locale)
55 | self._translations = None
56 | self.db_loader_func = None
57 | self.locale_selector_func = None
58 | self.app = None
59 | if app:
60 | self.init_app(app)
61 |
62 | @staticmethod
63 | def refresh():
64 | refresh()
65 |
66 | def init_app(self, app):
67 | """Set up this instance for use with *app*, if no app was passed to
68 | the constructor.
69 | """
70 | self.app = app
71 | if not hasattr(app, 'extensions'):
72 | app.extensions = {}
73 | app.extensions['locale'] = self
74 |
75 | app.config.setdefault('DEFAULT_LOCALE', self._default_locale)
76 | locale_path = os.path.join(app.root_path, 'translations')
77 | app.config.setdefault('LOCALE_PATH', locale_path)
78 |
79 | if self._configure_jinja:
80 | app.jinja_env.add_extension('jinja2.ext.i18n')
81 | app.jinja_env.install_gettext_callables(
82 | translate,
83 | translate,
84 | newstyle=True
85 | )
86 |
87 | def load_translations(self, directory):
88 | """Loads translations from CSV files having locale extension in a directory.
89 |
90 | Translations are strings with optional Python-style named placeholders
91 | (e.g., ``"My name is %(name)s"``) and their associated translations.
92 |
93 | The directory should have translation files of the form filename: LOCALE,
94 | e.g. common.es_GT. Translation files should have two or three columns: string,
95 | translation, and an optional plural indicator. Plural indicators should
96 | be one of ``"plural"`` or ``"singular"``. A given string can have both singular
97 | and plural forms. For example ``"%(name)s liked this"`` may have a
98 | different verb conjugation depending on whether %(name)s is one
99 | name or a list of names. There should be two rows in the CSV file for
100 | that string, one with plural indicator "singular", and one "plural".
101 | For strings with no verbs that would change on translation, simply
102 | use ``"unknown"`` or the empty string (or don't include the column at all).
103 |
104 | The file is read using the csv module in the default "excel" dialect.
105 | In this format there should not be spaces after the commas.
106 |
107 | Example translation es_LA.csv::
108 |
109 | "I love you","Te amo"
110 |
111 | "%(name)s liked this","A %(name)s les gust\u00f3 esto","plural"
112 |
113 | "%(name)s liked this","A %(name)s le gust\u00f3 esto","singular"
114 | """
115 | app = get_app()
116 | logger = app.logger
117 | _translations = {}
118 |
119 | if getattr(self, 'db_loader_func', None):
120 | trans = self.db_loader_func()
121 | for row in trans:
122 | locale = row[0]
123 | locale = unicodedata.normalize('NFKD', locale).encode('ascii', 'ignore')
124 | if locale not in _translations:
125 | _translations[locale] = {}
126 |
127 | if not row or len(row) < 3:
128 | continue
129 | plural = ('plural' if row[2] == False else 'unknown') or "unknown"
130 | row = [to_unicode(c).strip() for c in row[1:3]]
131 | english, translation = row[:2]
132 | english = unicodedata.normalize('NFKD', english).encode('ascii', 'ignore')
133 | if plural not in ("plural", "singular", "unknown"):
134 | logger.error("Unrecognized plural '%s' indicator for %s", plural, english)
135 | continue
136 |
137 | _translations[locale].setdefault(plural, {})[english] = translation
138 | else:
139 | for path in os.listdir(directory):
140 | locale, ext = path.split(".")
141 | if not re.match("[a-z]+(_[A-Z]+)?$", locale):
142 | logger.error("Unrecognized locale %r (path: %s)", locale,
143 | os.path.join(directory, path))
144 | continue
145 | full_path = os.path.join(directory, path)
146 | try:
147 | # python 3: csv.reader requires a file open in text mode.
148 | # Force utf8 to avoid dependence on $LANG environment variable.
149 | f = open(full_path, "r", encoding="utf-8")
150 | except TypeError:
151 | # python 2: files return byte strings, which are decoded below.
152 | # Once we drop python 2.5, this could use io.open instead
153 | # on both 2 and 3.
154 | f = open(full_path, "r")
155 |
156 | if locale not in _translations:
157 | _translations[locale] = {}
158 |
159 | for i, row in enumerate(csv.reader(f, delimiter=',', skipinitialspace=True)):
160 | if not row or len(row) < 2:
161 | continue
162 | row = [to_unicode(c).strip() for c in row]
163 | english, translation = row[:2]
164 | if len(row) > 2:
165 | plural = row[2] or "unknown"
166 | else:
167 | plural = "unknown"
168 | if plural not in ("plural", "singular", "unknown"):
169 | logger.error("Unrecognized plural indicator %r in %s line %d",
170 | plural, path, i + 1)
171 | continue
172 | _translations[locale].setdefault(plural, {})[english] = translation
173 | f.close()
174 | _supported_locales = frozenset(_translations.keys())
175 | self._supported_locales = _supported_locales
176 | self._translations = _translations
177 |
178 | def db_loader(self, f):
179 | """Registers a callback function for loading translations
180 | """
181 | assert not getattr(self, 'db_loader_func', None), 'a db_loader function is already registered'
182 | self.db_loader_func = f
183 | return f
184 |
185 | def localeselector(self, f):
186 | """Registers a callback function for locale selection. The default
187 | behaves as if a function was registered that returns `None` all the
188 | time. If `None` is returned, the locale falls back to the one from
189 | the configuration.
190 |
191 | This has to return the locale as string (eg: ``'de_AT'``, ``'en_US'``)
192 | """
193 | assert getattr(self, 'locale_selector_func', None) is None, 'a localeselector function is already registered'
194 | self.locale_selector_func = f
195 | return f
196 |
197 | def get_browser_locale(self):
198 | """Determines the user's locale from Accept-Language header.
199 |
200 | See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
201 | """
202 |
203 | def get_loc_key(l, s):
204 | return s
205 |
206 | if "Accept-Language" in request.headers:
207 | languages = request.headers["Accept-Language"].split(",")
208 | locales = []
209 | for language in languages:
210 | parts = language.strip().split(";")
211 | if len(parts) > 1 and parts[1].startswith("q="):
212 | try:
213 | score = float(parts[1][2:])
214 | except (ValueError, TypeError):
215 | score = 0.0
216 | else:
217 | score = 1.0
218 | locales.append((parts[0], score))
219 | if locales:
220 | locales.sort(reverse=True)
221 | codes = [l[0] for l in locales]
222 | return self.get_closest(*codes)
223 | app = get_app()
224 | return self.get_closest(app.config['DEFAULT_LOCALE'])
225 |
226 | def get_closest(self, *locale_codes):
227 | """Returns the closest match for the given locale code."""
228 | app = get_app()
229 | if self._translations is None:
230 | self.load_translations(app.config['LOCALE_PATH'])
231 |
232 | for code in locale_codes:
233 | if not code:
234 | continue
235 | code = code.replace("-", "_")
236 | parts = code.split("_")
237 | if code in self._supported_locales:
238 | return self.get(code)
239 | for c in self._supported_locales:
240 | if c.startswith(parts[0].lower()+'_'):
241 | return self.get(c)
242 | return self.get(app.config['DEFAULT_LOCALE'])
243 |
244 | def get(self, code):
245 | """Returns the translate dict for the given locale code."""
246 | return self._translations.get(code, {})
247 |
248 |
249 | def get_translation():
250 | """Returns the correct gettext translations that should be used for
251 | this request. This will never fail and return a dummy translation
252 | object if used outside of the request or if a translation cannot be
253 | found.
254 | """
255 | app = get_app()
256 | if not app:
257 | return None
258 | locale_instance = app.extensions['locale']
259 | locale = None
260 | if getattr(locale_instance, 'locale_selector_func', None):
261 | rv = locale_instance.locale_selector_func()
262 | if rv is not None:
263 | locale = locale_instance.get_closest(rv)
264 | if locale is None:
265 | locale = locale_instance.get_browser_locale()
266 | return locale
267 |
268 |
269 | def refresh():
270 | """Refreshes the cached locale information. This can be used to switch
271 | a translation between a request and if you want the changes to take place
272 | immediately, not just with the next request::
273 |
274 | user.locale = request.form['locale']
275 | refresh()
276 | flash(translate('Language was changed'))
277 |
278 | Without that refresh, the :func:`~flask.flash` function would probably
279 | return English text and a now German page.
280 | """
281 | app = get_app()
282 | if not app:
283 | return None
284 | app.extensions['locale']._translations = None
285 |
286 |
287 | def translate(message, **variables):
288 | return make_lazy_string(do_translate, message, **variables)
289 |
290 |
291 | _ = translate
292 |
293 |
294 | def do_translate(message, plural_message=None, count=None):
295 | """Returns the translation for the given message for this locale.
296 |
297 | If plural_message is given, you must also provide count. We return
298 | plural_message when count != 1, and we return the singular form
299 | for the given message when count == 1.
300 | """
301 | translation = get_translation()
302 | if plural_message is not None:
303 | assert count
304 | if count != 1:
305 | message = plural_message
306 | message_dict = translation.get("plural", {})
307 | else:
308 | message_dict = translation.get("singular", {})
309 | else:
310 | message_dict = translation.get("singular", None)
311 | if message_dict is None:
312 | message_dict = translation.get("unknown", {})
313 | return str(message_dict.get(message, message))
314 |
--------------------------------------------------------------------------------
/flask_locale/version.py:
--------------------------------------------------------------------------------
1 | __version__ = '1.0.5'
2 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | speaklater
2 | Flask
3 | speaklater
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.md
3 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | """
2 | Flask-Locale
3 | -----------
4 |
5 | Adds i18n/l10n support to Flask applications.
6 | """
7 | from setuptools import setup
8 |
9 | setup(
10 | name='Flask-Locale',
11 | version='1.0.4',
12 | url='http://github.com/derkan/flask-locale',
13 | license='BSD',
14 | author='Erkan Durmus',
15 | author_email='derkan@gmail.com',
16 | description='Adds i18n/l10n support to Flask applications easily. Uses CSV files(or database) to load translations.',
17 | long_description=open('README.rst').read(),
18 | packages=['flask_locale'],
19 | zip_safe=False,
20 | platforms='any',
21 | install_requires=[
22 | 'Flask',
23 | 'speaklater'
24 | ],
25 | classifiers=[
26 | 'Environment :: Web Environment',
27 | 'Intended Audience :: Developers',
28 | 'License :: OSI Approved :: BSD License',
29 | 'Operating System :: OS Independent',
30 | 'Programming Language :: Python',
31 | 'Programming Language :: Python :: 3',
32 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
33 | 'Topic :: Software Development :: Libraries :: Python Modules'
34 | ]
35 | )
36 |
--------------------------------------------------------------------------------