Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Apply selected language to the whole app, not just LanguageActivity.
Persist English without a country tag so switching back from Hindi works, and move remaining hardcoded UI copy into translated string resources.
  • Loading branch information
gyanu2507 committed Sep 3, 2026
commit 2d9dea2840e77286702285a04f8fe766dacb19ca
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ protected void attachBaseContext(Context base) {
@Override
public void onCreate() {
super.onCreate();
LocaleHelper.setLocale(
this,
LocaleHelper.getLanguage(this),
LocaleHelper.getCountry(this)
);
LocaleHelper.applyPersistedLocales(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ public void onBindViewHolder(@NonNull LanguageViewHolder holder, int position) {
selected ? R.drawable.bg_language_item_selected : R.drawable.bg_language_item
);
holder.imgSelected.setVisibility(selected ? View.VISIBLE : View.INVISIBLE);
holder.imgSelected.setContentDescription(
selected ? context.getString(R.string.selected_language) : null
);
holder.txtLanguageName.setTextColor(
ContextCompat.getColor(context, selected ? R.color.themeColor : R.color.black)
);
Expand Down
119 changes: 47 additions & 72 deletions app/src/main/java/com/example/updateapp/utils/LocaleHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,114 +4,89 @@
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.os.LocaleList;

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

import java.util.Locale;

/**
* Single source of truth for the in-app language.
* English is stored and applied as {@code en} with no country, so Android falls
* back to {@code values/} instead of hunting for {@code values-en-rUS}.
*/
public final class LocaleHelper {

public class LocaleHelper {
private static final String SELECTED_LANGUAGE = "Locale.Helper.Selected.Language";
private static final String SELECTED_COUNTRY = "Locale.Helper.Selected.Country";
private static final String PREFS_NAME = "app_prefs";
private static final String KEY_LANGUAGE = "Locale.Helper.Selected.Language";
private static final String KEY_COUNTRY = "Locale.Helper.Selected.Country";
public static final String DEFAULT_LANGUAGE = "en";

private LocaleHelper() {}

public static Context onAttach(Context context) {
return onAttach(context, DEFAULT_LANGUAGE);
return onAttach(context, "en");
}

public static Context onAttach(Context context, String defaultLanguage) {
String lang = getLanguage(context, defaultLanguage);
String country = getCountry(context);
return updateResources(context, lang, country);
String lang = getPersistedData(context, defaultLanguage);
String country = getPersistedCountry(context, "");
return wrap(context, lang, country);
}

public static String getLanguage(Context context) {
return getLanguage(context, DEFAULT_LANGUAGE);
}

public static String getLanguage(Context context, String defaultLanguage) {
SharedPreferences preferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
String lang = preferences.getString(KEY_LANGUAGE, defaultLanguage);
if (lang == null || lang.isEmpty()) {
return defaultLanguage;
}
return lang;
return getPersistedData(context, "en");
}

public static String getCountry(Context context) {
SharedPreferences preferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
String country = preferences.getString(KEY_COUNTRY, "");
return country == null ? "" : country;
return getPersistedCountry(context, "");
}

public static boolean isEnglish(String language) {
return language == null || language.isEmpty() || language.equalsIgnoreCase(DEFAULT_LANGUAGE);
public static void applyPersistedLocales(Context context) {
applyAppLocales(getLanguage(context), getCountry(context));
}

public static Context setLocale(Context context, String language, String country) {
String lang = isEnglish(language) ? DEFAULT_LANGUAGE : language;
String region = isEnglish(lang) ? "" : (country == null ? "" : country);
persist(context, lang, region);
applyAppLocales(lang, region);
Context app = context.getApplicationContext();
if (app != null && app != context) {
updateResources(app, lang, region);
if (language == null || language.isEmpty()) {
language = "en";
}
return updateResources(context, lang, region);
if (country == null || "en".equals(language)) {
country = "";
}
persist(context, language, country);
applyAppLocales(language, country);
return wrap(context, language, country);
}

public static Locale toLocale(String language, String country) {
if (isEnglish(language)) {
return Locale.ENGLISH;
}
if (country != null && !country.isEmpty()) {
return new Locale(language, country);
}
return new Locale(language);
private static void applyAppLocales(String language, String country) {
String tag = (country == null || country.isEmpty())
? language
: language + "-" + country;
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(tag));
}

private static void persist(Context context, String language, String country) {
SharedPreferences.Editor editor =
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit();
editor.putString(KEY_LANGUAGE, language);
editor.putString(KEY_COUNTRY, country == null ? "" : country);
editor.apply();
private static String getPersistedData(Context context, String defaultLanguage) {
SharedPreferences preferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
return preferences.getString(SELECTED_LANGUAGE, defaultLanguage);
}

private static void applyAppLocales(String language, String country) {
String tag = isEnglish(language)
? "en"
: (country != null && !country.isEmpty() ? language + "-" + country : language);
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(tag));
private static String getPersistedCountry(Context context, String defaultCountry) {
SharedPreferences preferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
return preferences.getString(SELECTED_COUNTRY, defaultCountry);
}

private static void persist(Context context, String language, String country) {
SharedPreferences preferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putString(SELECTED_LANGUAGE, language);
editor.putString(SELECTED_COUNTRY, country == null ? "" : country);
editor.apply();
}

private static Context updateResources(Context context, String language, String country) {
Locale locale = toLocale(language, country);
private static Context wrap(Context context, String language, String country) {
Locale locale;
if (country != null && !country.isEmpty()) {
locale = new Locale(language, country);
} else {
locale = new Locale(language);
}
Locale.setDefault(locale);

Resources resources = context.getResources();
Configuration configuration = new Configuration(resources.getConfiguration());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
configuration.setLocale(locale);
configuration.setLocales(new LocaleList(locale));
configuration.setLayoutDirection(locale);
return context.createConfigurationContext(configuration);
}

configuration.locale = locale;
configuration.setLocale(locale);
configuration.setLayoutDirection(locale);
resources.updateConfiguration(configuration, resources.getDisplayMetrics());
return context;
return context.createConfigurationContext(configuration);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.example.updateapp.views.activites;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
Expand All @@ -16,7 +14,6 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.example.updateapp.Helpers.SaveState;
import com.example.updateapp.MainActivity;
import com.example.updateapp.R;
import com.example.updateapp.adapters.LanguageAdapter;
Expand All @@ -31,17 +28,9 @@ public class LanguageActivity extends AppCompatActivity implements LanguageAdapt
private RecyclerView recyclerView;
private LanguageAdapter languageAdapter;
private List<LanguageModel> languageList;
private ImageView imgDone;
private ImageView imgBack;
private ImageView imgDone, imgBack;
private TextView txtTitle;
private TextView txtSubtitle;
private int selectedPosition = 0;
private boolean isFirstLaunch;

@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(LocaleHelper.onAttach(newBase));
}

@Override
protected void onCreate(Bundle savedInstanceState) {
Expand All @@ -54,33 +43,29 @@ protected void onCreate(Bundle savedInstanceState) {
return insets;
});

isFirstLaunch = getIntent().getBooleanExtra("isFirstLaunch", false);

initViews();
setupLanguageList();
setupRecyclerView();
setupClickListeners();
}

private void initViews() {
recyclerView = findViewById(R.id.rev_language);
recyclerView = findViewById(R.id.rev_langauge);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
imgDone = findViewById(R.id.img_done);
imgBack = findViewById(R.id.img_back);
txtTitle = findViewById(R.id.txt_title);
txtSubtitle = findViewById(R.id.txt_subtitle);

txtTitle.setText(getString(R.string.select_language));
if (txtSubtitle != null) {
txtSubtitle.setText(getString(R.string.choose_language));
}

imgDone.setContentDescription(getString(R.string.done));
imgBack.setContentDescription(getString(R.string.back));
imgBack.setVisibility(isFirstLaunch ? View.GONE : View.VISIBLE);

// Set title from string resource
txtTitle.setText(getString(R.string.language));

// Show back button
imgBack.setVisibility(android.view.View.VISIBLE);
}

private void setupLanguageList() {
languageList = new ArrayList<>();

// Add all 10 supported languages
languageList.add(new LanguageModel(getString(R.string.lang_english), "en", ""));
languageList.add(new LanguageModel(getString(R.string.lang_chinese), "zh", "CN"));
languageList.add(new LanguageModel(getString(R.string.lang_hindi), "hi", "IN"));
Expand All @@ -93,15 +78,16 @@ private void setupLanguageList() {
languageList.add(new LanguageModel(getString(R.string.lang_urdu), "ur", "PK"));

String currentLanguage = LocaleHelper.getLanguage(this);
selectedPosition = 0;
boolean found = false;
for (int i = 0; i < languageList.size(); i++) {
boolean match = languageList.get(i).getLanguageCode().equalsIgnoreCase(currentLanguage);
languageList.get(i).setSelected(match);
if (match) {
if (languageList.get(i).getLanguageCode().equals(currentLanguage)) {
languageList.get(i).setSelected(true);
selectedPosition = i;
found = true;
break;
}
}
if (!languageList.get(selectedPosition).isSelected()) {
if (!found) {
languageList.get(0).setSelected(true);
selectedPosition = 0;
}
Expand All @@ -111,12 +97,12 @@ private void setupRecyclerView() {
languageAdapter = new LanguageAdapter(this, languageList, this);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(languageAdapter);
recyclerView.setHasFixedSize(true);
}

private void setupClickListeners() {
imgDone.setOnClickListener(v -> applyLanguageChange());
imgBack.setOnClickListener(v -> getOnBackPressedDispatcher().onBackPressed());

imgBack.setOnClickListener(v -> onBackPressed());
}

@Override
Expand All @@ -126,25 +112,33 @@ public void onLanguageSelected(LanguageModel language, int position) {
}

private void applyLanguageChange() {
LanguageModel selectedLanguage = languageList.get(selectedPosition);
LocaleHelper.setLocale(
this,
selectedLanguage.getLanguageCode(),
selectedLanguage.getCountryCode()
);

new SaveState(this, "LANGUAGE_SELECTED").setState(1);
Toast.makeText(this, getString(R.string.language_changed), Toast.LENGTH_SHORT).show();
restartApp();
if (selectedPosition >= 0 && selectedPosition < languageList.size()) {
LanguageModel selectedLanguage = languageList.get(selectedPosition);

// Apply the language change immediately
LocaleHelper.setLocale(this, selectedLanguage.getLanguageCode(), selectedLanguage.getCountryCode());

// Show success message
Toast.makeText(this, getString(R.string.language_changed), Toast.LENGTH_SHORT).show();

// Restart the main activity to apply changes completely
restartApp();
}
}

private void restartApp() {
Intent intent = isFirstLaunch
? new Intent(this, OnboardingActivity.class)
: new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
// Create intent to restart the main activity
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();

// Apply transition animation
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
}

@Override
protected void attachBaseContext(android.content.Context newBase) {
super.attachBaseContext(LocaleHelper.onAttach(newBase));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -183,15 +183,15 @@ private void verifyCredential(PhoneAuthCredential credential) {
dialog.dismiss();

Exception e = linkTask.getException();
String msg = e != null ? e.getMessage() : "Unknown linking error";
String msg = e != null ? e.getMessage() : getString(R.string.unknown_linking_error);

if (e instanceof FirebaseAuthUserCollisionException) {
Toast.makeText(OTPActivity.this,
"This email is already registered with a different account. Please login with that email or use another email.",
getString(R.string.email_already_registered_different),
Toast.LENGTH_LONG).show();
} else {
Toast.makeText(OTPActivity.this,
"Failed to link email: " + msg,
getString(R.string.failed_to_link_email, msg),
Toast.LENGTH_LONG).show();
}
}
Expand Down Expand Up @@ -224,7 +224,7 @@ private void saveUserToFirestoreAndContinue(String uid, String name, String emai
})
.addOnFailureListener(e -> {
if (dialog.isShowing()) dialog.dismiss();
Toast.makeText(OTPActivity.this, getString(R.string.failed_save_user, e.getMessage()), Toast.LENGTH_LONG).show();
Toast.makeText(OTPActivity.this, getString(R.string.failed_to_save_user, e.getMessage()), Toast.LENGTH_LONG).show();
});
}

Expand Down
Loading