Skip to content

Commit

Permalink
Merge pull request #186 from learningequality/cheeky_strings
Browse files Browse the repository at this point in the history
Update strings in Android App
  • Loading branch information
rtibbles authored Jan 10, 2024
2 parents 8243c72 + be42b5c commit 3340ce1
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 42 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
src/kolibri
# A generated source file
src/strings.py
tmpenv
tar
python-for-android/build/
Expand All @@ -17,6 +18,7 @@ python-for-android/dists/kolibri/webview_includes
python-for-android/dists/kolibri/*.*
python-for-android/dists/kolibri/gradlew
python-for-android/dists/kolibri/src/main/res/*/html_content.xml
python-for-android/dists/kolibri/src/main/res/values-*/strings.xml
!python-for-android/dists/kolibri/build.gradle
!python-for-android/dists/kolibri/gradle.properties

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -358,12 +358,9 @@ protected void showLoadingScreen() {
* 'lightgray', 'darkgray', 'grey', 'lightgrey', 'darkgrey', 'aqua', 'fuchsia',
* 'lime', 'maroon', 'navy', 'olive', 'purple', 'silver', 'teal'.
*/
String backgroundColor = resourceManager.getString("presplash_color");
if (backgroundColor != null) {
try {
mImageView.setBackgroundColor(Color.parseColor(backgroundColor));
} catch (IllegalArgumentException e) {}
}
try {
mImageView.setBackgroundColor(Color.parseColor("#FFFFFF"));
} catch (IllegalArgumentException e) {}
mImageView.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@
import java.util.ArrayList;
import java.util.regex.Pattern;

import org.learningequality.Kolibri.BuildConfig;
import org.renpy.android.AssetExtract;

public class PythonUtil {
private static final String TAG = "pythonutil";

// We read this directly from the VERSION_CODE,
// so that any upgrade of the app causes the Python
// code to be extracted again.
private static final String PrivateVersion = Integer.toString(BuildConfig.VERSION_CODE);

protected static void addLibraryIfExists(ArrayList<String> libsList, String pattern, File libsDir) {
// pattern should be the name of the lib file, without the
// preceding "lib" or suffix ".so", for instance "ssl.*" will
Expand Down Expand Up @@ -129,10 +135,17 @@ public static void unpackAsset(
File target,
boolean cleanup_on_version_update) {

// We only have version information for the private package
// and this should only ever be called for the "private" package
if (resource != "private") {
Log.v(TAG, "Cannot unpack " + resource + " " + target.getName());
return;
}

Log.v(TAG, "Unpacking " + resource + " " + target.getName());

// The version of data in memory and on disk.
String dataVersion = getResourceString(ctx, resource + "_version");
String dataVersion = PrivateVersion;
String diskVersion = null;

Log.v(TAG, "Data version is " + dataVersion);
Expand Down Expand Up @@ -198,7 +211,7 @@ public static void unpackPyBundle(
Log.v(TAG, "Unpacking " + resource + " " + target.getName());

// The version of data in memory and on disk.
String dataVersion = getResourceString(ctx, "private_version");
String dataVersion = PrivateVersion;
String diskVersion = null;

Log.v(TAG, "Data version is " + dataVersion);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ public class App extends Application implements Configuration.Provider {
@Override
public void onCreate() {
super.onCreate();
NotificationRef.initialize(this);
createNotificationChannels();
}

Expand All @@ -44,14 +43,14 @@ private void createNotificationChannels() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Context context = getApplicationContext();
NotificationChannelCompat serviceChannel = new NotificationChannelCompat.Builder(
context.getString(R.string.notification_service_channel_id),
NotificationRef.ID_CHANNEL_SERVICE,
NotificationManagerCompat.IMPORTANCE_MIN
)
.setName(context.getString(R.string.notification_service_channel_title))
.setShowBadge(false)
.build();
NotificationChannelCompat taskChannel = new NotificationChannelCompat.Builder(
context.getString(R.string.notification_default_channel_id),
NotificationRef.ID_CHANNEL_DEFAULT,
NotificationManagerCompat.IMPORTANCE_DEFAULT
)
.setName(context.getString(R.string.notification_default_channel_title))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public NotificationBuilder(Context context, String channelId) {
setOngoing(true);
setCategory(NotificationCompat.CATEGORY_SERVICE);
setContentText(context.getString(R.string.notification_service_channel_content));
setTicker(context.getString(R.string.notification_service_channel_ticker));
setTicker(context.getString(R.string.notification_channel_ticker));

// Add settings button to notification for quick access to the minimize setting for this
// foreground notification channel
Expand All @@ -42,7 +42,7 @@ public NotificationBuilder(Context context, String channelId) {
}
} else if (channelId.equals(NotificationRef.ID_CHANNEL_DEFAULT)) {
setCategory(NotificationCompat.CATEGORY_PROGRESS);
setTicker(context.getString(R.string.notification_default_channel_ticker));
setTicker(context.getString(R.string.notification_channel_ticker));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ public final class NotificationRef {
public static final int ID_DEFAULT = 1;
public static final int REF_CHANNEL_SERVICE = 1;
public static final int REF_CHANNEL_DEFAULT = 2;
public static String ID_CHANNEL_DEFAULT = null;
public static String ID_CHANNEL_SERVICE = null;
private static boolean initialized = false;
public static final String ID_CHANNEL_DEFAULT = "task_notifications";
public static final String ID_CHANNEL_SERVICE = "background_notifications";
private final int channelRef;
private final String tag;
private final int id;
Expand Down Expand Up @@ -41,17 +40,7 @@ public String getTag() {
return tag;
}

public static void initialize(Context context) {
if (initialized) {
return;
}
ID_CHANNEL_DEFAULT = context.getString(R.string.notification_default_channel_id);
ID_CHANNEL_SERVICE = context.getString(R.string.notification_service_channel_id);
initialized = true;
}

public static String getChannelId(Context context, int channelRef) {
initialize(context);
switch (channelRef) {
case REF_CHANNEL_SERVICE:
return ID_CHANNEL_SERVICE;
Expand Down
17 changes: 6 additions & 11 deletions python-for-android/dists/kolibri/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Kolibri</string>
<string name="task_worker_process">:task_worker</string>
<string name="private_version">65f6c4c9631cdfd89ef2e450b4769e2df67a0a8f</string>
<string name="presplash_color">#FFFFFF</string>
<string name="notification_service_channel_id">background_notifications</string>
<string name="notification_service_channel_title">Background Notifications</string>
<string name="notification_service_channel_content">Background tasks</string>
<string name="notification_service_channel_ticker">A persistent notification of Kolibri\'s background processing</string>
<string name="notification_service_channel_action">Manage</string>
<string name="notification_default_channel_id">task_notifications</string>
<string name="notification_default_channel_title">Task Notifications</string>
<string name="notification_default_channel_ticker">A progress notification of Kolibri\'s background processing</string>
<string name="task_worker_process" translatable="false">:task_worker</string>
<string name="notification_channel_ticker">Task started…</string>
<string name="notification_service_channel_title">Important Tasks Running</string>
<string name="notification_service_channel_content">In progress</string>
<string name="notification_service_channel_action">View task manager</string>
<string name="notification_default_channel_title">Tasks</string>
</resources>
110 changes: 108 additions & 2 deletions scripts/create_strings.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
"""
Tooling for generating i18n strings using Kolibri's translation machinery.
"""
import json
import os
import tempfile
import xml.etree.ElementTree as ET
from importlib import resources

from version import apk_version

Expand Down Expand Up @@ -47,11 +50,79 @@ def generate_loading_pages(output_dir):
)


def create_resource_files(output_dir):
def _find_string(lang, string):
from kolibri.main import initialize
from django.utils.translation import override
from django.utils.translation import ugettext as _
from django.utils.translation import to_locale

initialize(skip_update=True)

with override(lang):
new_string = _(string)
if new_string != string and new_string:
return new_string

for message_file in os.listdir(
resources.files("kolibri") / "locale" / "en" / "LC_MESSAGES"
):
if message_file.endswith(".json"):
with open(
resources.files("kolibri")
/ "locale"
/ "en"
/ "LC_MESSAGES"
/ message_file,
"r",
) as f:
messages = json.load(f)
for key, value in messages.items():
if value == string:
try:
# Do this in case we have a legacy translation file - this should be cleaned up
# in a future version of Kolibri
with open(
resources.files("kolibri")
/ "locale"
/ to_locale(lang)
/ "LC_MESSAGES"
/ message_file,
"r",
) as lang_message_file:
messages = json.load(lang_message_file)
new_string = messages[key]
if new_string != string and new_string:
return new_string
# If we have a translation but the string is no different in translation, it means we should
# not include it in the strings.xml file
return None
except FileNotFoundError:
break
return None


# Strings that we only access from Python, so we don't need to include them in the strings.xml file
PYTHON_ONLY_STRINGS = [
"Learner",
]


def create_resource_files(output_dir): # noqa: C901
"""
Read each language directory and create resource files in the corresponding Android values folder.
"""
for lang_dir in os.listdir(output_dir):
en_strings_file = os.path.join(
os.path.dirname(__file__),
"../python-for-android/dists/kolibri/src/main/res/values/strings.xml",
)

en_strings_tree = ET.parse(en_strings_file)

en_strings_root = en_strings_tree.getroot()

all_langs = list(os.listdir(output_dir))

for lang_dir in all_langs:
if lang_dir == DEFAULT_LANGUAGE:
dir_name = "values"
else:
Expand Down Expand Up @@ -83,6 +154,41 @@ def create_resource_files(output_dir):
with open(os.path.join(values_dir, "html_content.xml"), "w") as f:
f.write(xml_content)

if lang_dir == DEFAULT_LANGUAGE:
continue

new_root = ET.Element("resources")
new_tree = ET.ElementTree(element=new_root)

for string in en_strings_root.findall("string"):
name = string.get("name")
value = _find_string(lang_dir, string.text)
if value is None:
continue
new_string = ET.SubElement(new_root, "string", attrib={"name": name})
new_string.text = value

new_tree.write(
os.path.join(values_dir, "strings.xml"),
encoding="utf-8",
xml_declaration=True,
)

# Create the Python strings file
output = "# This file is auto-generated by the create_strings.py script. Do not edit it directly."
output += "\ni18n_strings = {"
for python_string in PYTHON_ONLY_STRINGS:
output += "\n " + f"'{python_string}': " + "{"
for lang_dir in all_langs:
value = _find_string(lang_dir, python_string)
if value is None:
continue
output += f"\n '{lang_dir}': '{value}', "
output += "\n },"
output += "\n}\n"
with open(os.path.join(os.path.dirname(__file__), "../src/strings.py"), "w") as f:
f.write(output)


def main():
"""
Expand Down
6 changes: 4 additions & 2 deletions src/android_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

from cryptography import x509
from cryptography.hazmat.backends import default_backend
from i18n import get_string
from jnius import autoclass
from jnius import cast
from le_utils.proquint import generate


def is_service_context():
Expand Down Expand Up @@ -229,7 +229,9 @@ def get_dummy_user_name():
cache_key = "DUMMY_USER_NAME"
value = value_cache.get(cache_key)
if value is None:
value = generate()
Locale = autoclass("java.util.Locale")
currentLocale = Locale.getDefault().toLanguageTag()
value = get_string("Learner", currentLocale)
value_cache.set(cache_key, value)
return value

Expand Down
11 changes: 11 additions & 0 deletions src/i18n.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
try:
from strings import i18n_strings
except ImportError:
i18n_strings = {}


def get_string(name, language):
try:
return i18n_strings[language][name]
except KeyError:
return name

0 comments on commit 3340ce1

Please sign in to comment.