diff --git a/.gitignore b/.gitignore index d4cb36bd..ed5ce899 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ /iati_datastore/iatilib/frontend/docs /iati_datastore/iatilib/frontend/querybuilder /dump.rdb +/iati_datastore/iatilib/messages.pot +/iati_datastore/iatilib/translations/*/LC_MESSAGES/messages.mo # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/README.md b/README.md index 1c8a8d32..1a3d5343 100644 --- a/README.md +++ b/README.md @@ -347,3 +347,22 @@ Then in the 2 requirements*.txt files, look for the line: And edit them to: -e iati_datastore + +I18N - Flask Application +------------------------ + + cd iati_datastore/iatilib + +To add a new locale: + + pybabel extract -F babel.cfg -o messages.pot . + pybabel init -i messages.pot -d translations -l fr + +If strings change in app and you want to reparse the app for new strings, run: + + pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot . + pybabel update -i messages.pot -d translations + +When .po files change with new content, or when deploying the app: + + pybabel compile -d translations diff --git a/fabfile.py b/fabfile.py index 90b3987d..8743bc5e 100644 --- a/fabfile.py +++ b/fabfile.py @@ -22,6 +22,8 @@ def deploy(conn): conn.run('iati db upgrade') # build the docs conn.run('iati build-docs') + # create translations + conn.run('(cd iati_datastore/iatilib && pybabel compile -d translations)') # build the query builder conn.run('iati build-query-builder --deploy-url https://datastore.codeforiati.org') # webserver diff --git a/iati_datastore/iatilib/babel.cfg b/iati_datastore/iatilib/babel.cfg new file mode 100644 index 00000000..efceab81 --- /dev/null +++ b/iati_datastore/iatilib/babel.cfg @@ -0,0 +1 @@ +[python: **.py] diff --git a/iati_datastore/iatilib/frontend/api1.py b/iati_datastore/iatilib/frontend/api1.py index d8a5f2f9..01f3c83f 100644 --- a/iati_datastore/iatilib/frontend/api1.py +++ b/iati_datastore/iatilib/frontend/api1.py @@ -287,6 +287,7 @@ def validate_args(self): if not hasattr(self, "_valid_args"): args = MultiDict(request.args) args.pop("ref", None) + args.pop("locale", None) self._valid_args = validators.activity_api_args(args) return self._valid_args diff --git a/iati_datastore/iatilib/frontend/app.py b/iati_datastore/iatilib/frontend/app.py index 4615840a..d72bb69e 100644 --- a/iati_datastore/iatilib/frontend/app.py +++ b/iati_datastore/iatilib/frontend/app.py @@ -1,4 +1,5 @@ -from flask import Flask, render_template +from flask import Flask, render_template, request +from flask_babel import Babel from flask_cors import CORS from iatilib import db, rq, migrate @@ -13,6 +14,8 @@ def create_app(config_object='iatilib.config.Config'): app = Flask(__name__.split('.')[0]) app.config.from_object(config_object) + babel = Babel(app, configure_jinja=False) + babel.localeselector(get_locale) register_extensions(app) register_blueprints(app) register_error_handlers(app) @@ -40,3 +43,7 @@ def register_error_handlers(app): for code in (500, 501, 502, 503, 504): app.register_error_handler( code, lambda x: (render_template('error/5xx.html'), code)) + + +def get_locale(): + return request.args.get("locale", "en") diff --git a/iati_datastore/iatilib/frontend/serialize/csv.py b/iati_datastore/iatilib/frontend/serialize/csv.py index 5241035e..ab24ba4e 100644 --- a/iati_datastore/iatilib/frontend/serialize/csv.py +++ b/iati_datastore/iatilib/frontend/serialize/csv.py @@ -7,6 +7,71 @@ from iatilib import codelists from pyexcelerate import Workbook from openpyxl_copy.utils import get_column_letter +from flask_babel import gettext, lazy_gettext + + +# I18N +# Call lazy_gettext on everything we are using as column header. This way Babel will pick it up as a translate-able string. +# TODO this is a pain as we have to duplicate all headers. Instead, write a custom extraction method later: https://babel.pocoo.org/en/latest/messages.html#writing-extraction-methods +lazy_gettext("iati-identifier") +lazy_gettext("hierarchy") +lazy_gettext("last-updated-datetime") +lazy_gettext("default-language") +lazy_gettext("reporting-org") +lazy_gettext("reporting-org-ref") +lazy_gettext("reporting-org-type") +lazy_gettext("reporting-org-type-code") +lazy_gettext("title") +lazy_gettext("description") +lazy_gettext("activity-status-code") +lazy_gettext("start-planned") +lazy_gettext("end-planned") +lazy_gettext("start-actual") +lazy_gettext("end-actual") +lazy_gettext("participating-org (Accountable)") +lazy_gettext("participating-org-ref (Accountable)") +lazy_gettext("participating-org-type (Accountable)") +lazy_gettext("participating-org-type-code (Accountable)") +lazy_gettext("participating-org (Funding)") +lazy_gettext("participating-org-ref (Funding)") +lazy_gettext("participating-org-type (Funding)") +lazy_gettext("participating-org-type-code (Funding)") +lazy_gettext("participating-org (Extending)") +lazy_gettext("participating-org-ref (Extending)") +lazy_gettext("participating-org-type (Extending)") +lazy_gettext("participating-org-type-code (Extending)") +lazy_gettext("participating-org (Implementing)") +lazy_gettext("participating-org-ref (Implementing)") +lazy_gettext("participating-org-type (Implementing)") +lazy_gettext("participating-org-type-code (Implementing)") +lazy_gettext("recipient-country-code") +lazy_gettext("recipient-country") +lazy_gettext("recipient-country-percentage") +lazy_gettext("sector-code") +lazy_gettext("sector") +lazy_gettext("sector-percentage") +lazy_gettext("sector-vocabulary") +lazy_gettext("sector-vocabulary-code") +lazy_gettext("collaboration-type-code") +lazy_gettext("default-finance-type-code") +lazy_gettext("default-flow-type-code") +lazy_gettext("default-aid-type-code") +lazy_gettext("default-tied-status-code") +lazy_gettext("recipient-country-code") +lazy_gettext("recipient-country") +lazy_gettext("recipient-country-percentage") +lazy_gettext("recipient-region-code") +lazy_gettext("recipient-region") +lazy_gettext("recipient-region-percentage") +lazy_gettext("default-currency") +lazy_gettext("currency") +lazy_gettext('total-Commitment') +lazy_gettext("total-Disbursement") +lazy_gettext("total-Expenditure") +lazy_gettext("total-Incoming Funds") +lazy_gettext("total-Interest Repayment") +lazy_gettext("total-Loan Repayment") +lazy_gettext("total-Reimbursement") def total(column): @@ -414,7 +479,7 @@ def line(row): writer = unicodecsv.writer(out) writer.writerow(row) return out.getvalue() - yield line(self.fields_by_major_version['1'].keys()) + yield line([gettext(label) for label in self.fields_by_major_version['1'].keys()]) get_major_version = self.get_major_version for obj in data.items: row = [accessor(obj) for accessor in self.fields_by_major_version[get_major_version(obj)].values()] @@ -436,7 +501,7 @@ def __call__(self, data, wrapped=True): wb = Workbook() ws = wb.new_sheet("data") # Headers - headers = self.fields_by_major_version['1'].keys() + headers = [gettext(label) for label in self.fields_by_major_version['1'].keys()] final_column = get_column_letter(len(headers)) ws.range("A1", final_column+"1").value = [headers] # Data diff --git a/iati_datastore/iatilib/translations/fr/LC_MESSAGES/messages.po b/iati_datastore/iatilib/translations/fr/LC_MESSAGES/messages.po new file mode 100644 index 00000000..432a70cc --- /dev/null +++ b/iati_datastore/iatilib/translations/fr/LC_MESSAGES/messages.po @@ -0,0 +1,284 @@ +# French translations for PROJECT. +# Copyright (C) 2022 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2022-05-18 12:05+0000\n" +"PO-Revision-Date: 2022-05-18 10:34+0000\n" +"Last-Translator: FULL NAME \n" +"Language: fr\n" +"Language-Team: fr \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 2.9.1\n" + +#: crawler.py:175 +msgid "Duplicate identifier {0} in same resource document" +msgstr "" + +#: parse.py:135 +msgid "" +"Failed to import a valid reporting-org.type in activity {0}, error was: " +"{1}" +msgstr "" + +#: parse.py:157 +msgid "" +"Failed to import a valid sector percentage:{0} in activity {1}, error " +"was: {2}" +msgstr "" + +#: parse.py:258 parse.py:501 +msgid "Failed to import a valid {0} in activity {1}, error was: {2}" +msgstr "" + +#: parse.py:273 +msgid "Failed to import a valid transaction in activity {0}, error was: {1}" +msgstr "" + +#: parse.py:297 +msgid "uFailed to import a valid {0} in activity {1}, error was: {2}" +msgstr "" + +#: parse.py:343 +msgid "uFailed to import a valid budget:{0} in activity {1}, error was: {2}" +msgstr "" + +#: parse.py:377 +msgid "Failed to import a valid related-activity in activity {0}, error was: {1}" +msgstr "" + +#: parse.py:535 +msgid "Failed to import a valid Activity error was: {0}" +msgstr "" + +#: frontend/serialize/csv.py:16 +msgid "iati-identifier" +msgstr "" + +#: frontend/serialize/csv.py:17 +msgid "hierarchy" +msgstr "" + +#: frontend/serialize/csv.py:18 +msgid "last-updated-datetime" +msgstr "" + +#: frontend/serialize/csv.py:19 +msgid "default-language" +msgstr "" + +#: frontend/serialize/csv.py:20 +msgid "reporting-org" +msgstr "" + +#: frontend/serialize/csv.py:21 +msgid "reporting-org-ref" +msgstr "" + +#: frontend/serialize/csv.py:22 +msgid "reporting-org-type" +msgstr "" + +#: frontend/serialize/csv.py:23 +msgid "reporting-org-type-code" +msgstr "" + +#: frontend/serialize/csv.py:24 +msgid "title" +msgstr "titre" + +#: frontend/serialize/csv.py:25 +msgid "description" +msgstr "" + +#: frontend/serialize/csv.py:26 +msgid "activity-status-code" +msgstr "" + +#: frontend/serialize/csv.py:27 +msgid "start-planned" +msgstr "" + +#: frontend/serialize/csv.py:28 +msgid "end-planned" +msgstr "" + +#: frontend/serialize/csv.py:29 +msgid "start-actual" +msgstr "" + +#: frontend/serialize/csv.py:30 +msgid "end-actual" +msgstr "" + +#: frontend/serialize/csv.py:31 +msgid "participating-org (Accountable)" +msgstr "" + +#: frontend/serialize/csv.py:32 +msgid "participating-org-ref (Accountable)" +msgstr "" + +#: frontend/serialize/csv.py:33 +msgid "participating-org-type (Accountable)" +msgstr "" + +#: frontend/serialize/csv.py:34 +msgid "participating-org-type-code (Accountable)" +msgstr "" + +#: frontend/serialize/csv.py:35 +msgid "participating-org (Funding)" +msgstr "" + +#: frontend/serialize/csv.py:36 +msgid "participating-org-ref (Funding)" +msgstr "" + +#: frontend/serialize/csv.py:37 +msgid "participating-org-type (Funding)" +msgstr "" + +#: frontend/serialize/csv.py:38 +msgid "participating-org-type-code (Funding)" +msgstr "" + +#: frontend/serialize/csv.py:39 +msgid "participating-org (Extending)" +msgstr "" + +#: frontend/serialize/csv.py:40 +msgid "participating-org-ref (Extending)" +msgstr "" + +#: frontend/serialize/csv.py:41 +msgid "participating-org-type (Extending)" +msgstr "" + +#: frontend/serialize/csv.py:42 +msgid "participating-org-type-code (Extending)" +msgstr "" + +#: frontend/serialize/csv.py:43 +msgid "participating-org (Implementing)" +msgstr "" + +#: frontend/serialize/csv.py:44 +msgid "participating-org-ref (Implementing)" +msgstr "" + +#: frontend/serialize/csv.py:45 +msgid "participating-org-type (Implementing)" +msgstr "" + +#: frontend/serialize/csv.py:46 +msgid "participating-org-type-code (Implementing)" +msgstr "" + +#: frontend/serialize/csv.py:47 frontend/serialize/csv.py:60 +msgid "recipient-country-code" +msgstr "" + +#: frontend/serialize/csv.py:48 frontend/serialize/csv.py:61 +msgid "recipient-country" +msgstr "" + +#: frontend/serialize/csv.py:49 frontend/serialize/csv.py:62 +msgid "recipient-country-percentage" +msgstr "" + +#: frontend/serialize/csv.py:50 +msgid "sector-code" +msgstr "" + +#: frontend/serialize/csv.py:51 +msgid "sector" +msgstr "" + +#: frontend/serialize/csv.py:52 +msgid "sector-percentage" +msgstr "" + +#: frontend/serialize/csv.py:53 +msgid "sector-vocabulary" +msgstr "" + +#: frontend/serialize/csv.py:54 +msgid "sector-vocabulary-code" +msgstr "" + +#: frontend/serialize/csv.py:55 +msgid "collaboration-type-code" +msgstr "" + +#: frontend/serialize/csv.py:56 +msgid "default-finance-type-code" +msgstr "" + +#: frontend/serialize/csv.py:57 +msgid "default-flow-type-code" +msgstr "" + +#: frontend/serialize/csv.py:58 +msgid "default-aid-type-code" +msgstr "" + +#: frontend/serialize/csv.py:59 +msgid "default-tied-status-code" +msgstr "" + +#: frontend/serialize/csv.py:63 +msgid "recipient-region-code" +msgstr "" + +#: frontend/serialize/csv.py:64 +msgid "recipient-region" +msgstr "" + +#: frontend/serialize/csv.py:65 +msgid "recipient-region-percentage" +msgstr "" + +#: frontend/serialize/csv.py:66 +msgid "default-currency" +msgstr "" + +#: frontend/serialize/csv.py:67 +msgid "currency" +msgstr "" + +#: frontend/serialize/csv.py:68 +msgid "total-Commitment" +msgstr "" + +#: frontend/serialize/csv.py:69 +msgid "total-Disbursement" +msgstr "" + +#: frontend/serialize/csv.py:70 +msgid "total-Expenditure" +msgstr "" + +#: frontend/serialize/csv.py:71 +msgid "total-Incoming Funds" +msgstr "" + +#: frontend/serialize/csv.py:72 +msgid "total-Interest Repayment" +msgstr "" + +#: frontend/serialize/csv.py:73 +msgid "total-Loan Repayment" +msgstr "" + +#: frontend/serialize/csv.py:74 +msgid "total-Reimbursement" +msgstr "" + diff --git a/iati_datastore/iatilib/translations/pt/LC_MESSAGES/messages.po b/iati_datastore/iatilib/translations/pt/LC_MESSAGES/messages.po new file mode 100644 index 00000000..85da2594 --- /dev/null +++ b/iati_datastore/iatilib/translations/pt/LC_MESSAGES/messages.po @@ -0,0 +1,284 @@ +# Portuguese translations for PROJECT. +# Copyright (C) 2022 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2022. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2022-05-18 12:05+0000\n" +"PO-Revision-Date: 2022-05-18 10:35+0000\n" +"Last-Translator: FULL NAME \n" +"Language: pt\n" +"Language-Team: pt \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 2.9.1\n" + +#: crawler.py:175 +msgid "Duplicate identifier {0} in same resource document" +msgstr "" + +#: parse.py:135 +msgid "" +"Failed to import a valid reporting-org.type in activity {0}, error was: " +"{1}" +msgstr "" + +#: parse.py:157 +msgid "" +"Failed to import a valid sector percentage:{0} in activity {1}, error " +"was: {2}" +msgstr "" + +#: parse.py:258 parse.py:501 +msgid "Failed to import a valid {0} in activity {1}, error was: {2}" +msgstr "" + +#: parse.py:273 +msgid "Failed to import a valid transaction in activity {0}, error was: {1}" +msgstr "" + +#: parse.py:297 +msgid "uFailed to import a valid {0} in activity {1}, error was: {2}" +msgstr "" + +#: parse.py:343 +msgid "uFailed to import a valid budget:{0} in activity {1}, error was: {2}" +msgstr "" + +#: parse.py:377 +msgid "Failed to import a valid related-activity in activity {0}, error was: {1}" +msgstr "" + +#: parse.py:535 +msgid "Failed to import a valid Activity error was: {0}" +msgstr "" + +#: frontend/serialize/csv.py:16 +msgid "iati-identifier" +msgstr "" + +#: frontend/serialize/csv.py:17 +msgid "hierarchy" +msgstr "" + +#: frontend/serialize/csv.py:18 +msgid "last-updated-datetime" +msgstr "" + +#: frontend/serialize/csv.py:19 +msgid "default-language" +msgstr "" + +#: frontend/serialize/csv.py:20 +msgid "reporting-org" +msgstr "" + +#: frontend/serialize/csv.py:21 +msgid "reporting-org-ref" +msgstr "" + +#: frontend/serialize/csv.py:22 +msgid "reporting-org-type" +msgstr "" + +#: frontend/serialize/csv.py:23 +msgid "reporting-org-type-code" +msgstr "" + +#: frontend/serialize/csv.py:24 +msgid "title" +msgstr "" + +#: frontend/serialize/csv.py:25 +msgid "description" +msgstr "" + +#: frontend/serialize/csv.py:26 +msgid "activity-status-code" +msgstr "" + +#: frontend/serialize/csv.py:27 +msgid "start-planned" +msgstr "" + +#: frontend/serialize/csv.py:28 +msgid "end-planned" +msgstr "" + +#: frontend/serialize/csv.py:29 +msgid "start-actual" +msgstr "" + +#: frontend/serialize/csv.py:30 +msgid "end-actual" +msgstr "" + +#: frontend/serialize/csv.py:31 +msgid "participating-org (Accountable)" +msgstr "" + +#: frontend/serialize/csv.py:32 +msgid "participating-org-ref (Accountable)" +msgstr "" + +#: frontend/serialize/csv.py:33 +msgid "participating-org-type (Accountable)" +msgstr "" + +#: frontend/serialize/csv.py:34 +msgid "participating-org-type-code (Accountable)" +msgstr "" + +#: frontend/serialize/csv.py:35 +msgid "participating-org (Funding)" +msgstr "" + +#: frontend/serialize/csv.py:36 +msgid "participating-org-ref (Funding)" +msgstr "" + +#: frontend/serialize/csv.py:37 +msgid "participating-org-type (Funding)" +msgstr "" + +#: frontend/serialize/csv.py:38 +msgid "participating-org-type-code (Funding)" +msgstr "" + +#: frontend/serialize/csv.py:39 +msgid "participating-org (Extending)" +msgstr "" + +#: frontend/serialize/csv.py:40 +msgid "participating-org-ref (Extending)" +msgstr "" + +#: frontend/serialize/csv.py:41 +msgid "participating-org-type (Extending)" +msgstr "" + +#: frontend/serialize/csv.py:42 +msgid "participating-org-type-code (Extending)" +msgstr "" + +#: frontend/serialize/csv.py:43 +msgid "participating-org (Implementing)" +msgstr "" + +#: frontend/serialize/csv.py:44 +msgid "participating-org-ref (Implementing)" +msgstr "" + +#: frontend/serialize/csv.py:45 +msgid "participating-org-type (Implementing)" +msgstr "" + +#: frontend/serialize/csv.py:46 +msgid "participating-org-type-code (Implementing)" +msgstr "" + +#: frontend/serialize/csv.py:47 frontend/serialize/csv.py:60 +msgid "recipient-country-code" +msgstr "" + +#: frontend/serialize/csv.py:48 frontend/serialize/csv.py:61 +msgid "recipient-country" +msgstr "" + +#: frontend/serialize/csv.py:49 frontend/serialize/csv.py:62 +msgid "recipient-country-percentage" +msgstr "" + +#: frontend/serialize/csv.py:50 +msgid "sector-code" +msgstr "" + +#: frontend/serialize/csv.py:51 +msgid "sector" +msgstr "" + +#: frontend/serialize/csv.py:52 +msgid "sector-percentage" +msgstr "" + +#: frontend/serialize/csv.py:53 +msgid "sector-vocabulary" +msgstr "" + +#: frontend/serialize/csv.py:54 +msgid "sector-vocabulary-code" +msgstr "" + +#: frontend/serialize/csv.py:55 +msgid "collaboration-type-code" +msgstr "" + +#: frontend/serialize/csv.py:56 +msgid "default-finance-type-code" +msgstr "" + +#: frontend/serialize/csv.py:57 +msgid "default-flow-type-code" +msgstr "" + +#: frontend/serialize/csv.py:58 +msgid "default-aid-type-code" +msgstr "" + +#: frontend/serialize/csv.py:59 +msgid "default-tied-status-code" +msgstr "" + +#: frontend/serialize/csv.py:63 +msgid "recipient-region-code" +msgstr "" + +#: frontend/serialize/csv.py:64 +msgid "recipient-region" +msgstr "" + +#: frontend/serialize/csv.py:65 +msgid "recipient-region-percentage" +msgstr "" + +#: frontend/serialize/csv.py:66 +msgid "default-currency" +msgstr "" + +#: frontend/serialize/csv.py:67 +msgid "currency" +msgstr "" + +#: frontend/serialize/csv.py:68 +msgid "total-Commitment" +msgstr "" + +#: frontend/serialize/csv.py:69 +msgid "total-Disbursement" +msgstr "" + +#: frontend/serialize/csv.py:70 +msgid "total-Expenditure" +msgstr "" + +#: frontend/serialize/csv.py:71 +msgid "total-Incoming Funds" +msgstr "" + +#: frontend/serialize/csv.py:72 +msgid "total-Interest Repayment" +msgstr "" + +#: frontend/serialize/csv.py:73 +msgid "total-Loan Repayment" +msgstr "" + +#: frontend/serialize/csv.py:74 +msgid "total-Reimbursement" +msgstr "" + diff --git a/iati_datastore/query_builder_source/pages/index.vue b/iati_datastore/query_builder_source/pages/index.vue index 45adb352..fdd72b0b 100644 --- a/iati_datastore/query_builder_source/pages/index.vue +++ b/iati_datastore/query_builder_source/pages/index.vue @@ -594,6 +594,7 @@ export default { _urlQueryFilters.push(['limit', '1']) } _urlQueryFilters.push(['ref', 'qb']) + _urlQueryFilters.push(['locale', this.$i18n.locale]) const _params = _urlQueryFilters.map(item => { return `${item[0]}=${item[1]}` }).join("&") diff --git a/iati_datastore/setup.py b/iati_datastore/setup.py index 281840d0..a871d6cb 100644 --- a/iati_datastore/setup.py +++ b/iati_datastore/setup.py @@ -24,6 +24,7 @@ sentry-sdk[flask]==1.3.1 docutils==0.17.1 PyExcelerate==0.10.0 +Flask-Babel==2.0.0 """ tests_require = """ diff --git a/requirements.txt b/requirements.txt old mode 100644 new mode 100755 index 51711a4e..aa7e6e11 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,9 @@ alabaster==0.7.12 alembic==1.7.7 # via flask-migrate babel==2.10.1 - # via sphinx + # via + # flask-babel + # sphinx blinker==1.4 # via sentry-sdk certifi==2022.5.18.1 @@ -38,12 +40,15 @@ docutils==0.17.1 flask==1.1.2 # via # -r requirements.in + # flask-babel # flask-cors # flask-migrate # flask-rq2 # flask-sqlalchemy # iati-datastore # sentry-sdk +flask-babel==2.0.0 + # via iati-datastore flask-cors==3.0.9 # via # -r requirements.in @@ -93,6 +98,7 @@ jinja2==3.0.3 # via # -r requirements.in # flask + # flask-babel # pyexcelerate # sphinx lxml==4.6.2 @@ -131,7 +137,9 @@ python-dateutil==2.8.1 # iati-datastore # rq-scheduler pytz==2022.1 - # via babel + # via + # babel + # flask-babel redis==3.5.3 # via # -r requirements.in diff --git a/requirements_dev.txt b/requirements_dev.txt old mode 100644 new mode 100755 index 67afdb2d..7008797e --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -17,6 +17,7 @@ alembic==1.7.7 babel==2.10.1 # via # -r requirements.txt + # flask-babel # sphinx blinker==1.4 # via @@ -70,12 +71,17 @@ fakeredis==1.4.5 flask==1.1.2 # via # -r requirements.txt + # flask-babel # flask-cors # flask-migrate # flask-rq2 # flask-sqlalchemy # iati-datastore # sentry-sdk +flask-babel==2.0.0 + # via + # -r requirements.txt + # iati-datastore flask-cors==3.0.9 # via # -r requirements.txt @@ -134,6 +140,7 @@ jinja2==3.0.3 # via # -r requirements.txt # flask + # flask-babel # pyexcelerate # sphinx lxml==4.6.2 @@ -195,6 +202,7 @@ pytz==2022.1 # via # -r requirements.txt # babel + # flask-babel redis==3.5.3 # via # -r requirements.txt