Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Move translations to the openedx Transifex project #462

Merged
merged 4 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .tx/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[main]
host = https://www.transifex.com

[o:open-edx:p:edx-platform:r:tutor-contrib-aspects]
source_lang = en
type = KEYVALUEJSON
21 changes: 12 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ upgrade: $(COMMON_CONSTRAINTS_TXT)
pip install -r requirements/pip-tools.txt
$(UPGRADE) -o requirements/base.txt requirements/base.in
$(UPGRADE) -o requirements/dev.txt requirements/dev.in
$(UPGRADE) -o requirements/translations.txt requirements/translations.in

requirements: ## Install packages from base requirement files
pip install -r requirements/pip.txt
Expand All @@ -42,6 +43,10 @@ dev-requirements: ## Install packages from developer requirement files
pip uninstall --yes $(PROJECT)
pip install -e .

translation-requirements: ## Install packages from translation requirements
pip install -r requirements/pip.txt
pip install -r requirements/translations.txt

build-pythonpackage: ## Build Python packages ready to upload to pypi
python setup.py sdist bdist_wheel

Expand Down Expand Up @@ -78,17 +83,15 @@ release-push:
git push origin $(TAG)

###### Additional commands
push_translations:
TUTOR_ROOT=$(TUTOR_ROOT) tutor config save
python scripts/translate.py $(TUTOR_ROOT) push
pull_translations: translation-requirements
rm -rf tutoraspects/templates/aspects/apps/superset/conf/locale/*;
atlas pull $(OPENEDX_ATLAS_ARGS) translations/tutor-contrib-aspects/tutoraspects/templates/aspects/apps/superset/conf/locale/:tutoraspects/templates/aspects/apps/superset/conf/locale/

compile_translations:
TUTOR_ROOT=$(TUTOR_ROOT) tutor config save
python scripts/translate.py $(TUTOR_ROOT) compile
@echo "Translations have been pulled via Atlas."
python scripts/translate.py . compile

list_translations:
TUTOR_ROOT=$(TUTOR_ROOT) tutor config save
python scripts/translate.py $(TUTOR_ROOT) list
extract_translations: translation-requirements
python scripts/translate.py . extract

version: ## Print the current tutor version
@python -c 'import io, os; about = {}; exec(io.open(os.path.join("$(PACKAGE)", "__about__.py"), "rt", encoding="utf-8").read(), about); print(about["__version__"])'
Expand Down
3 changes: 2 additions & 1 deletion requirements/base.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
click
tutor>=15
bcrypt
openedx-atlas
transifex-python
tutor>=15
2 changes: 2 additions & 0 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ oauthlib==3.2.2
# via
# kubernetes
# requests-oauthlib
openedx-atlas==0.5.0
# via -r requirements/base.in
parsimonious==0.10.0
# via pyseeyou
pyasn1==0.5.0
Expand Down
2 changes: 2 additions & 0 deletions requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ oauthlib==3.2.2
# -r requirements/base.txt
# kubernetes
# requests-oauthlib
openedx-atlas==0.5.0
# via -r requirements/base.txt
packaging==23.2
# via
# black
Expand Down
2 changes: 2 additions & 0 deletions requirements/translations.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Requirements needed to run in openedx-translations to manage localization
click
8 changes: 8 additions & 0 deletions requirements/translations.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#
# This file is autogenerated by pip-compile with Python 3.9
# by the following command:
#
# make upgrade
#
click==8.1.7
# via -r requirements/translations.in
14 changes: 3 additions & 11 deletions scripts/translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,16 @@
import sys

import click
from transifex.native import init
from utils import (LANGUAGES, compile_translations, get_text_for_translations,
push_translations)

init(
os.getenv("TRANSIFEX_TOKEN"),
LANGUAGES,
os.getenv("TRANSIFEX_SECRET"),
)
from utils import compile_translations, extract_translations, get_text_for_translations


@click.command()
@click.argument("root")
@click.argument("action")
def command(root, action):
"""Interface for the translations."""
if action == "push":
push_translations(root)
if action == "extract":
extract_translations(root)
elif action == "compile":
compile_translations(root)
elif action == "list":
Expand Down
143 changes: 78 additions & 65 deletions scripts/utils.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,46 @@
import os
import yaml
from transifex.native import tx
from transifex.native.parsing import SourceString

ASSET_FOLDER_MAPPING = {
"dashboard_title": "dashboards",
"slice_name": "charts",
"database_name": "databases",
"table_name": "datasets",
}

LANGUAGES = [
"es",
"it",
"fr",
"zh",
"ja",
"de",
"pt",
"ru",
"ko",
"sk",
"sl",
"nl",
]


def get_text_for_translations(root_path):
assets_file = (
root_path + "/env/plugins/aspects/apps/superset/pythonpath/assets.yaml"
assets_path = (
os.path.join(root_path, "tutoraspects/templates/openedx-assets/assets/")
)

print(f"Assets path: {assets_path}")

strings = []

file = yaml.load(open(assets_file, "r"), Loader=yaml.FullLoader)
for root, dirs, files in os.walk(assets_path):
for file in files:
if not file.endswith(".yaml"):
continue
path = os.path.join(root, file)
print(f"Reading {path}")
with open(path, 'r') as asset_file:
asset_str = asset_file.read().replace("{{", "'")
asset_str = asset_str.replace("}}", "'")

for asset in file:
strings.extend(mark_text_for_translation(asset))
asset = yaml.safe_load(asset_str)
strings.extend(mark_text_for_translation(asset))

return strings


def mark_text_for_translation(asset):
"""For every asset extract the text and mark it for translation"""
"""
For every asset extract the text and mark it for translation
"""

def extract_text(asset, type):
"""Extract text from an asset"""
"""
Extract text from an asset
"""
strings = []
if type == "dashboards":
strings.append(asset["dashboard_title"])
Expand Down Expand Up @@ -73,64 +69,81 @@ def extract_text(asset, type):
if asset["params"].get("y_axis_label"):
strings.append(asset["params"]["y_axis_label"])

elif type == "databases":
# WARNING: Databases are not translated
pass
elif type == "datasets":
# WARNING: Datasets are not translated
pass
return strings

for key, value in ASSET_FOLDER_MAPPING.items():
if key in asset:
strings = extract_text(asset, value)
print(
f"Extracting text from {value} {asset.get('uuid')}",
strings,
f"Extracted {len(strings)} strings from {value} {asset.get('uuid')}"
)
return strings

# If we get here it's a type of asset that we don't translate, return nothing.
return []

def compile_translations(root_path):
print("Fetching translations...")
tx.fetch_translations()

translation_file = (
"tutoraspects/templates/aspects/apps/superset/localization/locale.yaml"
def compile_translations(root_path):
"""
Combine translated files into the single file we use for translation.

This should be called after we pull translations using Atlas, see the
pull_translations make target.
"""
translations_path = (
os.path.join(
root_path,
"tutoraspects/templates/aspects/apps/superset/conf/locale"
)
)
file = open(translation_file, "w")

STRINGS = get_text_for_translations(root_path)

translations = {}
all_translations = {}
for root, dirs, files in os.walk(translations_path):
for file in files:
if not file.endswith(".yaml"):
continue

lang = root.split(os.sep)[-1]
path = os.path.join(root, file)
with open(path, 'r') as asset_file:
loc_str = asset_file.read()
all_translations[lang] = yaml.safe_load(loc_str)[lang]

out_path = (
os.path.join(
root_path,
"tutoraspects/templates/aspects/apps/superset/localization/locale.yaml"
)
)

print("Compiling translations...")
print(f"Writing all translations out to {out_path}")
with open(out_path, 'w') as outfile:
outfile.write("---\n")
yaml.safe_dump(all_translations, outfile)
outfile.write("\n{{ patch('superset-extra-asset-translations')}}\n")

for language in LANGUAGES:
print("Processing language", language)
translations[language] = {}
for string in STRINGS:
if not translations[language].get(string):
translation = tx.get_translation(string, language, None)
translations[language][string] = translation if translation else ""

file.write("---\n")
file.write(yaml.dump(translations))

file.write("\n{{ patch('superset-extra-asset-translations')}}\n")
def extract_translations(root_path):
"""
This gathers all translatable text from the Superset assets.

An English locale file is created, which openedx-translations will send to
Transifex for translation.
"""
translation_file = (
"tutoraspects/templates/aspects/apps/superset/conf/locale/en/locale.yaml"
)

def push_translations(root_path):
print("Publishing translation strings...")
print("Gathering text for translations...")
STRINGS = set(get_text_for_translations(root_path))
translations = {'en': {}}

STRINGS = get_text_for_translations(root_path)
for string in STRINGS:
translations['en'][string] = string

source_strings = []
for text in STRINGS:
source_string = SourceString(
text,
)
source_strings.append(source_string)
print(f"Writing English strings to {translation_file}")
with open(translation_file, "w") as file:
file.write(yaml.dump(translations))

response_code, response_content = tx.push_source_strings(source_strings, purge=True)
print(response_code, response_content)
print("Done compiling translations.")
Loading