Skip to content

Commit

Permalink
Issue 62 localization of UI strings (#128)
Browse files Browse the repository at this point in the history
* Issue-62: Localization of UI strings

* generating POT file
* integrated gettext calls into py and glade files
* linting and testing

* implemented a strings file
  • Loading branch information
dorschs57 committed Jun 1, 2021
1 parent 35f12a8 commit 56cfd19
Show file tree
Hide file tree
Showing 18 changed files with 588 additions and 201 deletions.
8 changes: 7 additions & 1 deletion .github/workflows/main.yml
Expand Up @@ -93,7 +93,7 @@ jobs:
id: install_deps
run: |
python -m pip install --upgrade pip
pip install flake8
pip install flake8 flake8-i18n
- name: Lint with flake8
run: |
cd python
Expand All @@ -114,13 +114,19 @@ jobs:
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install extra locales
id: install_locales
run: |
sudo apt-get -y install locales
sudo locale-gen es_ES.UTF-8
- name: Install dependencies
id: install_deps
run: |
python -m pip install --upgrade pip
pip install pipenv
cd python
pipenv install --dev
pipenv run compile_catalog
echo ::set-output name=virt_env::$(pipenv --venv)
- name: Download bindings package
id: download
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -18,5 +18,6 @@ dist
*.glade~
.coverage
.env
*.mo

src/bin
1 change: 1 addition & 0 deletions python/.flake8
@@ -1,3 +1,4 @@
[flake8]
ignore = E402,W503
max-line-length = 120
i18nfuncs = gettext ngettext _
5 changes: 5 additions & 0 deletions python/Pipfile
Expand Up @@ -15,6 +15,9 @@ pytest = "*"
pytest-cov = "*"
pytest-mock = "*"
pytest-watch = "*"
babel = "*"
babelgladeextractor = "*"
flake8-i18n = "*"

[packages]
pycairo = "1.20.0"
Expand All @@ -30,3 +33,5 @@ allow_prereleases = true
test = "pipenv run xvfb-run -a pytest -s --cov-report term-missing --cov=ui fapolicy_analyzer/"
lint = "pipenv run flake8 fapolicy_analyzer/"
watch-test = "pipenv run xvfb-run -a pytest-watch fapolicy_analyzer/ -- -s"
extract_messages = "pipenv run python setup.py extract_messages"
compile_catalog = "pipenv run python setup.py compile_catalog -f"
303 changes: 134 additions & 169 deletions python/Pipfile.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions python/babel.cfg
@@ -0,0 +1,2 @@
[python: **.py]
[glade: **.glade]
16 changes: 16 additions & 0 deletions python/fapolicy_analyzer/tests/test_main_window.py
@@ -1,5 +1,6 @@
import context # noqa: F401
import pytest
import locale
import gi

gi.require_version("Gtk", "3.0")
Expand Down Expand Up @@ -29,6 +30,13 @@ def mainWindow():
return StubMainWindow()


@pytest.fixture
def es_locale():
locale.setlocale(locale.LC_ALL, "es_ES.UTF-8")
yield
locale.setlocale(locale.LC_ALL, "")


def test_displays_window(mainWindow):
assert type(mainWindow.window) is Gtk.Window
assert mainWindow.window.get_title() == "File Access Policy Analyzer"
Expand Down Expand Up @@ -69,3 +77,11 @@ def test_raises_bad_selection_error(mainWindow, mocker):
)
with pytest.raises(Exception, match="Bad Selection"):
mainWindow.original_on_start()


def test_localization(es_locale):
mainWindow = StubMainWindow()
assert type(mainWindow.window) is Gtk.Window
assert (
mainWindow.window.get_title() == "Analizador de políticas de acceso a archivos"
)
36 changes: 23 additions & 13 deletions python/fapolicy_analyzer/ui/ancillary_trust_database_admin.py
@@ -1,10 +1,13 @@
import gi
import ui.strings as strings

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib
from concurrent.futures import ThreadPoolExecutor
from fapolicy_analyzer import Changeset, System
from fapolicy_analyzer.util import fs
from locale import gettext as _
from fapolicy_analyzer.util.format import f
from fapolicy_analyzer.util import fs # noqa: F401
from .ui_widget import UIWidget
from .trust_file_list import TrustFileList
from .trust_file_details import TrustFileDetails
Expand Down Expand Up @@ -65,14 +68,14 @@ def get_content(self):

def add_trusted_files(self, *files):
changeset = Changeset()
for f in files:
changeset.add_trust(f)
for file in files:
changeset.add_trust(file)
self.__apply_changeset(changeset)

def delete_trusted_files(self, *files):
changeset = Changeset()
for f in files:
changeset.del_trust(f)
for file in files:
changeset.del_trust(file)
self.__apply_changeset(changeset)

def on_file_selection_change(self, trust):
Expand All @@ -86,22 +89,30 @@ def on_file_selection_change(self, trust):
untrustBtn.set_sensitive(trusted)

self.trustFileDetails.set_in_databae_view(
f"""File: {trust.path}
f(
_(
"""File: {trust.path}
Size: {trust.size}
SHA256: {trust.hash}"""
)
)
)

self.trustFileDetails.set_on_file_system_view(
f"""{fs.stat(trust.path)}
f(
_(
"""{fs.stat(trust.path)}
SHA256: {fs.sha(trust.path)}"""
)
)
)

self.trustFileDetails.set_trust_status(
"This file is trusted."
strings.TRUSTED_FILE_MESSAGE
if trusted
else "There is a discrepancy with this file."
else strings.DISCREPANCY_FILE_MESSAGE
if status == "d"
else "The trust status of this file is unknown."
else strings.UNKNOWN_FILE_MESSAGE
)
else:
trustBtn.set_sensitive(False)
Expand All @@ -122,9 +133,8 @@ def on_untrustBtn_clicked(self, *args):
def on_deployBtn_clicked(self, *args):
parent = self.content.get_toplevel()
confirmDialog = ConfirmDialog(
"Deploy Ancillary Trust Changes?",
"Are you sure you wish to deploy your changes to the ancillary trust database? "
+ "This will update the fapolicy trust and restart the service.",
strings.DEPLOY_ANCILLARY_CONFIRM_DIALOG_TITLE,
strings.DEPLOY_ANCILLARY_CONFIRM_DIALOG_TEXT,
parent,
).get_content()
confirm_resp = confirmDialog.run()
Expand Down
5 changes: 3 additions & 2 deletions python/fapolicy_analyzer/ui/database_admin_page.py
@@ -1,4 +1,5 @@
import gi
import ui.strings as strings

gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
Expand All @@ -18,11 +19,11 @@ def __init__(self):

self.notebook.append_page(
self.systemTrustDbAdmin.get_content(),
Gtk.Label(label="System Trust Database"),
Gtk.Label(label=strings.SYSTEM_TRUST_TAB_LABEL),
)
self.notebook.append_page(
self.ancillaryTrustDbAdmin.get_content(),
Gtk.Label(label="Ancillary Trust Database"),
Gtk.Label(label=strings.ANCILLARY_TRUST_TAB_LABEL),
)

self.notebook.set_current_page(1)
Expand Down
4 changes: 3 additions & 1 deletion python/fapolicy_analyzer/ui/deploy_confirm_dialog.py
Expand Up @@ -4,7 +4,9 @@
from gi.repository import Gtk, GLib
from threading import Thread
from time import sleep
from locale import gettext as _
from .ui_widget import UIWidget
from fapolicy_analyzer.util.format import f


class DeployConfirmDialog(UIWidget):
Expand All @@ -27,7 +29,7 @@ def reset_countdown(self):
for i in reversed(range(0, self.cancel_time)):
GLib.idle_add(
self.dialog.format_secondary_text,
f"Reverting to previous settings in {i+1} seconds",
f(_("Reverting to previous settings in {i+1} seconds")),
)
sleep(1)
GLib.idle_add(self.dialog.response, Gtk.ResponseType.NO)
25 changes: 25 additions & 0 deletions python/fapolicy_analyzer/ui/strings.py
@@ -0,0 +1,25 @@
from locale import gettext as _

DEPLOY_ANCILLARY_CONFIRM_DIALOG_TITLE = _("Deploy Ancillary Trust Changes?")
DEPLOY_ANCILLARY_CONFIRM_DIALOG_TEXT = _(
"""Are you sure you wish to deploy your changes to the ancillary trust database?
This will update the fapolicy trust and restart the service."""
)

TRUSTED_FILE_MESSAGE = _("This file is trusted.")
DISCREPANCY_FILE_MESSAGE = _("There is a discrepancy with this file.")
UNKNOWN_FILE_MESSAGE = _("The trust status of this file is unknown.")

SYSTEM_TRUST_TAB_LABEL = _("System Trust Database")
ANCILLARY_TRUST_TAB_LABEL = _("Ancillary Trust Database")

FILE_LIST_TRUST_HEADER = _("Trust")
FILE_LIST_FILE_HEADER = _("File")

ADD_FILE_BUTTON_LABEL = _("Add File")

WHITESPACE_WARNING_DIALOG_TITLE = _("File path(s) contains embedded whitespace.")
WHITESPACE_WARNING_DIALOG_TEXT = _(
"fapolicyd currently does not support paths containing spaces. The following paths will not be added to the "
+ "Trusted Files List.\n(fapolicyd: V TBD)\n\n"
)
23 changes: 17 additions & 6 deletions python/fapolicy_analyzer/ui/system_trust_database_admin.py
@@ -1,11 +1,14 @@
import gi
import ui.strings as strings

gi.require_version("Gtk", "3.0")
from gi.repository import GLib
from events import Events
from concurrent.futures import ThreadPoolExecutor
from fapolicy_analyzer import System
from fapolicy_analyzer.util import fs
from locale import gettext as _
from fapolicy_analyzer.util.format import f
from fapolicy_analyzer.util import fs # noqa: F401
from .trust_file_list import TrustFileList
from .trust_file_details import TrustFileDetails
from .ui_widget import UIWidget
Expand Down Expand Up @@ -63,20 +66,28 @@ def on_file_selection_change(self, trust):
addBtn.set_sensitive(not trusted)

self.trustFileDetails.set_in_databae_view(
f"""File: {trust.path}
f(
_(
"""File: {trust.path}
Size: {trust.size}
SHA256: {trust.hash}"""
)
)
)
self.trustFileDetails.set_on_file_system_view(
f"""{fs.stat(trust.path)}
f(
_(
"""{fs.stat(trust.path)}
SHA256: {fs.sha(trust.path)}"""
)
)
)
self.trustFileDetails.set_trust_status(
"This file is trusted."
strings.TRUSTED_FILE_MESSAGE
if trusted
else "There is a discrepancy with this file."
else strings.DISCREPANCY_FILE_MESSAGE
if status == "d"
else "The trust status of this file is unknown."
else strings.UNKNOWN_FILE_MESSAGE
)
else:
addBtn.set_sensitive(False)
Expand Down
23 changes: 14 additions & 9 deletions python/fapolicy_analyzer/ui/trust_file_list.py
@@ -1,6 +1,7 @@

import gi
import re
import ui.strings as strings


gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GdkPixbuf
Expand All @@ -17,11 +18,16 @@ def setup_trustView():
self.trustView = self.get_object("trustView")
trustCell = Gtk.CellRendererText()
trustCell.set_property("background", "light gray")
trustColumn = Gtk.TreeViewColumn("Trust", trustCell, markup=0)
trustColumn = Gtk.TreeViewColumn(
strings.FILE_LIST_TRUST_HEADER, trustCell, markup=0
)
trustColumn.set_sort_column_id(0)
self.trustView.append_column(trustColumn)
fileColumn = Gtk.TreeViewColumn(
"File", Gtk.CellRendererText(), text=1, cell_background=3
strings.FILE_LIST_FILE_HEADER,
Gtk.CellRendererText(),
text=1,
cell_background=3,
)
fileColumn.set_sort_column_id(1)
self.trustView.append_column(fileColumn)
Expand Down Expand Up @@ -96,7 +102,7 @@ def on_search_changed(self, search):

def on_addBtn_clicked(self, *args):
fcd = Gtk.FileChooserDialog(
"Add File",
strings.ADD_FILE_BUTTON_LABEL,
self.trustFileList.get_toplevel(),
Gtk.FileChooserAction.OPEN,
(
Expand Down Expand Up @@ -125,16 +131,15 @@ def on_addBtn_clicked(self, *args):
flags=0,
message_type=Gtk.MessageType.INFO,
buttons=Gtk.ButtonsType.OK,
text="File path(s) contains embedded whitespace.")
text=strings.WHITESPACE_WARNING_DIALOG_TITLE,
)

# Convert list of paths to a single string
strListRejected = "\n".join(listRejected)

dlgWhitespaceInfo.format_secondary_text(
" fapolicyd currently does not support paths containing\n"
" spaces. The following paths will not be added to the\n"
" Trusted Files List.(fapolicyd: V TBD)\n\n"
+ strListRejected)
strings.WHITESPACE_WARNING_DIALOG_TEXT + strListRejected
)
dlgWhitespaceInfo.run()
dlgWhitespaceInfo.destroy()
files = listAccepted
Expand Down
9 changes: 9 additions & 0 deletions python/fapolicy_analyzer/ui/ui_widget.py
@@ -1,3 +1,4 @@
import locale
import os
import sys
import gi
Expand All @@ -7,12 +8,20 @@
from abc import ABC


DOMAIN = "fapolicy_analyzer"
locale_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), "../../locale")
locale.setlocale(locale.LC_ALL, locale.getlocale())
locale.bindtextdomain(DOMAIN, locale_path)
locale.textdomain(DOMAIN)


class UIWidget(ABC):
def __init__(self):
gladeFile = self.absolute_file_path(
f"../../glade/{self.__module__.split('.')[-1]}.glade"
)
self.builder = Gtk.Builder()
self.builder.set_translation_domain(DOMAIN)
self.builder.add_from_file(gladeFile)
self.builder.connect_signals(self)

Expand Down
6 changes: 6 additions & 0 deletions python/fapolicy_analyzer/util/format.py
@@ -0,0 +1,6 @@
from inspect import currentframe


def f(formatString):
frame = currentframe().f_back
return eval(f'f"""{formatString}"""', frame.f_locals, frame.f_globals)

0 comments on commit 56cfd19

Please sign in to comment.