Skip to content

Commit

Permalink
Improve multi-language support (bepass-org#347)
Browse files Browse the repository at this point in the history
On issue bepass-org#292
- Based on the [best
practices](https://developer.android.com/guide/topics/resources/app-languages)
recommended by Google
- If other languages ​​are added, there is no need to do anything
manually.
  • Loading branch information
erfansn authored Jul 19, 2024
1 parent 3931a42 commit 742a51f
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 205 deletions.
4 changes: 4 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ android {
useSupportLibrary true
}
}
androidResources {
generateLocaleConfig true
}
buildFeatures {
dataBinding = true
}
Expand Down Expand Up @@ -59,5 +62,6 @@ dependencies {
implementation 'com.github.bumptech.glide:glide:4.16.0'
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation 'com.vdurmont:emoji-java:5.1.1'
implementation 'com.github.erfansn:locale-config-x:1.0.1'
implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'])
}
10 changes: 10 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@
</intent-filter>

</service>

<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"
android:exported="false">
<meta-data
android:name="autoStoreLocales"
android:value="true" />
</service>

<activity android:name="org.bepass.oblivion.ui.SplashScreenActivity"
android:exported="true">
<intent-filter>
Expand Down
4 changes: 1 addition & 3 deletions app/src/main/java/org/bepass/oblivion/ui/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import org.bepass.oblivion.R;
import org.bepass.oblivion.base.StateAwareBaseActivity;
import org.bepass.oblivion.databinding.ActivityMainBinding;
import org.bepass.oblivion.utils.ThemeHelper;

import java.util.HashSet;
import java.util.Set;
Expand Down Expand Up @@ -137,8 +136,7 @@ private void requestNotificationPermission() {
}

private void setupUI() {
binding.floatingActionButton.setOnClickListener(v -> localeHandler.showLanguageSelectionDialog(() ->
localeHandler.restartActivity(this)));
binding.floatingActionButton.setOnClickListener(v -> localeHandler.showLanguageSelectionDialog());
binding.infoIcon.setOnClickListener(v -> startActivity(new Intent(MainActivity.this, InfoActivity.class)));
binding.bugIcon.setOnClickListener(v -> startActivity(new Intent(MainActivity.this, LogActivity.class)));
binding.settingIcon.setOnClickListener(v -> startActivity(new Intent(MainActivity.this, SettingsActivity.class)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.bepass.oblivion.R;
import org.bepass.oblivion.base.BaseActivity;
import org.bepass.oblivion.databinding.ActivitySplashScreenBinding;
import org.bepass.oblivion.utils.LocaleHandler;

/**
* A simple splash screen activity that shows a splash screen for a short duration before navigating
Expand Down Expand Up @@ -41,10 +42,16 @@ protected int getStatusBarColor() {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LocaleHandler localeHandler = new LocaleHandler(this);
localeHandler.setPersianAsDefaultLocaleIfNeeds();
binding.setHandler(new ClickHandler());
// 1 second
int SHORT_SPLASH_DISPLAY_LENGTH = 1000;
new Handler().postDelayed(() -> {
// First locale change to persian cause activity recreation
// with this check we can sure we don't do start twice
if (isDestroyed()) return;

MainActivity.start(this);
finish();
}, SHORT_SPLASH_DISPLAY_LENGTH);
Expand Down
103 changes: 28 additions & 75 deletions app/src/main/java/org/bepass/oblivion/utils/LocaleHandler.java
Original file line number Diff line number Diff line change
@@ -1,101 +1,54 @@
package org.bepass.oblivion.utils;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.util.DisplayMetrics;

import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.os.LocaleListCompat;

import com.github.erfansn.localeconfigx.LocaleConfigXKt;

import org.bepass.oblivion.R;

import java.util.Locale;
import java.util.Objects;

public class LocaleHandler {
private static final String SELECTED_LANGUAGE = "SelectedLanguage";
private static final String DEFAULT_LANGUAGE = "fa";
private static final String[] AVAILABLE_LANGUAGES = {"fa", "en", "ru", "zh"};
private final Context context;
private final FileManager fileManager;
private final LocaleListCompat configuredLocales;

private static final String DEFAULT_LOCALE = "fa";
private static final String IS_SET_DEFAULT_LOCALE = "is_set_default_locale";

public LocaleHandler(Context context) {
this.context = context;
fileManager = FileManager.getInstance(context);
setLocale(); // Ensure the locale is set when the handler is created
}

public void setLocale() {
String language = fileManager.getString(SELECTED_LANGUAGE, DEFAULT_LANGUAGE);
setLanguage(language);
this.configuredLocales = LocaleConfigXKt.getConfiguredLocales(context);
}

private void setLanguage(String language) {
Locale locale = new Locale(language);
Locale.setDefault(locale);

Resources resources = context.getResources();
Configuration configuration = resources.getConfiguration();
DisplayMetrics displayMetrics = resources.getDisplayMetrics();

configuration.setLocale(locale);

resources.updateConfiguration(configuration, displayMetrics);
public void setPersianAsDefaultLocaleIfNeeds() {
FileManager fileManager = FileManager.getInstance(context);
if (!fileManager.getBoolean(IS_SET_DEFAULT_LOCALE)) {
AppCompatDelegate.setApplicationLocales(LocaleListCompat.create(configuredLocales.getFirstMatch(new String[] { DEFAULT_LOCALE })));
fileManager.set(IS_SET_DEFAULT_LOCALE, true);
}
}

public void showLanguageSelectionDialog(Runnable onLanguageSelected) {
public void showLanguageSelectionDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("Select Language")
.setItems(getAvailableLanguagesNames(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int which) {
String selectedLanguage = AVAILABLE_LANGUAGES[which];
saveSelectedLanguage(selectedLanguage);
setLanguage(selectedLanguage);
onLanguageSelected.run(); // Run the callback
}
builder.setTitle(R.string.select_language)
.setItems(getAvailableLanguagesNames(), (dialogInterface, which) -> {
Locale selectedLocale = configuredLocales.get(which);
LocaleListCompat desiredLocales = LocaleListCompat.create(selectedLocale);
AppCompatDelegate.setApplicationLocales(desiredLocales);
})
.show();
}


private void saveSelectedLanguage(String language) {
fileManager.set(SELECTED_LANGUAGE, language);
}

private String[] getAvailableLanguagesNames() {
String[] languageNames = new String[AVAILABLE_LANGUAGES.length];
for (int i = 0; i < AVAILABLE_LANGUAGES.length; i++) {
languageNames[i] = getLanguageName(AVAILABLE_LANGUAGES[i]);
String[] languageNames = new String[configuredLocales.size()];
for (int index = 0; index < configuredLocales.size(); index++) {
languageNames[index] = Objects.requireNonNull(configuredLocales.get(index)).getDisplayName();
}
return languageNames;
}

private String getLanguageName(String languageCode) {
switch (languageCode) {
case "fa":
return "Persian";
case "en":
return "English";
case "ru":
return "Russian";
case "zh":
return "Chinese";
default:
return languageCode;
}
}
@SuppressLint("ObsoleteSdkInt")
public void restartActivity(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
((Activity) context).recreate();
} else {
Intent intent = ((Activity) context).getIntent();
context.startActivity(intent);
((Activity) context).finish();
context.startActivity(intent);
}
}
}

1 change: 1 addition & 0 deletions app/src/main/res/resources.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
unqualifiedResLocale=en
9 changes: 6 additions & 3 deletions app/src/main/res/values-fa/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@
<string name="dialBtText">برای اطمینان از عملکرد صحیح برنامه، لطفاً بهینه‌سازی باتری برای این برنامه را غیرفعال کنید.</string>
<string name="goToSettings">به تنظیمات بروید</string>
<string name="copytoclip">کپی کردن لاگ به کلیپ‌بورد</string>
<string name="turn_on_dark_mode">Turn on dark mode</string>
<string name="turn_on_dark_mode_desc">turn on dark mode</string>
<string name="turn_on_dark_mode">حالت تاریک</string>
<string name="turn_on_dark_mode_desc">اعمال کردن تم تاریک</string>
<string-array name="countries">
<item>اتریش</item>
<item>بلژیک</item>
Expand Down Expand Up @@ -80,4 +80,7 @@
<item>بریتانیا</item>
<item>ایالات متحده</item>
</string-array>
</resources>
<string name="select_language">انتخاب زبان</string>
<string name="endpointTypeText">نوع Endpoint</string>
<string name="endpointTypeTextDesc">مورد استفاده برای endpoint IP</string>
</resources>
7 changes: 5 additions & 2 deletions app/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@
<string name="dialBtText">Чтобы приложение работало правильно, отключите оптимизацию батареи для этого приложения.</string>
<string name="goToSettings">Перейти к настройкам</string>
<string name="copytoclip">Скопировать лог в буфер обмена</string>
<string name="turn_on_dark_mode">Turn on dark mode</string>
<string name="turn_on_dark_mode_desc">turn on dark mode</string>
<string name="turn_on_dark_mode">темный режим</string>
<string name="turn_on_dark_mode_desc">Применить темную тему</string>
<string-array name="countries">
<item>Австрия</item>
<item>Бельгия</item>
Expand Down Expand Up @@ -80,4 +80,7 @@
<item>Великобритания</item>
<item>Соединенные Штаты</item>
</string-array>
<string name="endpointTypeText">Тип конечной точки</string>
<string name="endpointTypeTextDesc">Используется для определения IP-адреса конечной точки.</string>
<string name="select_language">Выберите язык</string>
</resources>
3 changes: 3 additions & 0 deletions app/src/main/res/values-zh/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
<string name="dialBtText">为了确保应用程序正常运行,请为此应用程序关闭电池优化。</string>
<string name="goToSettings">前往设置</string>
<string name="copytoclip">复制日志到剪贴板</string>
<string name="turn_on_dark_mode">深色模式</string>
<string name="turn_on_dark_mode_desc">申请 深色模式</string>
<string name="endpointTypeText">端点类型</string>
<string name="endpointTypeTextDesc">用于查找终点IP</string>
<string-array name="countries">
Expand Down Expand Up @@ -80,5 +82,6 @@
<item>英国</item>
<item>美国</item>
</string-array>
<string name="select_language">选择语言</string>

</resources>
Loading

0 comments on commit 742a51f

Please sign in to comment.