11

I want to change device locale (not just the application locale) from my app, like the user can do in Settings -> Language & Keyboard -> Language.

Can someone please explain how to do it? I've been searching for hours and can not find a way.

avicohh
  • 177
  • 1
  • 3
  • 11
  • If you mean just for our app, see http://stackoverflow.com/questions/2900023/change-language-programatically-in-android If you mean for any app, I don't think so- I think its a protected setting. Your best bet then is to launch the keyboard settings and let the user change it. – Gabe Sechan Aug 18 '14 at 02:23
  • 1
    I mean for any app. i can use root if needed. – avicohh Aug 18 '14 at 02:27
  • I don't think you can directly. The best you can do is pop up the settings page. – Gabe Sechan Aug 18 '14 at 02:29
  • So that's what they call an "open-system"? – Rodrigo Dec 24 '14 at 05:06

3 Answers3

7

Yes you can change device locale for any android version make sure that your app is system app.Heres the code to help you out.Please rate my answer if you like it.

//getting the languages that are shown same as in our device settings
     String[] systemLocaleIetfLanguageTags = getAssets().getLocales();
            Arrays.sort(systemLocaleIetfLanguageTags);
            this.locale_data = new ArrayList();
            for (String ietfLanguageTag : systemLocaleIetfLanguageTags)
            {
                if (ietfLanguageTag != null && ietfLanguageTag.length() == 5)
                {
                    this.locale_data.add(new Loc(ietfLanguageTag));
                }
            }
            adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, locale_data);
            // Assign adapter to ListView
            lv.setAdapter(adapter);
            lv.setOnItemClickListener(new OnItemClickListener()
            {
                @Override
                public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3)
                {
                    int itemPosition = position;
                    Loc loc = (Loc) ChangeLanguage.this.adapter.getItem(position);
                    Toast.makeText(ChangeLanguage.this, "language activated successfully !!", 0).show();
                    ChangeLanguage.change_setting(loc.getLocale());
                }
            });
        }
    //to change the locale
        public static void change_setting(Locale loc)
        {
            try
            {
                Class<?> activityManagerNative = Class.forName("android.app.ActivityManagerNative");
                Object am = activityManagerNative.getMethod("getDefault", new Class[0]).invoke(activityManagerNative, new Object[0]);
                Object config = am.getClass().getMethod("getConfiguration", new Class[0]).invoke(am, new Object[0]);
                config.getClass().getDeclaredField("locale").set(config, loc);
                config.getClass().getDeclaredField("userSetLocale").setBoolean(config, true);
                am.getClass().getMethod("updateConfiguration", new Class[] { Configuration.class }).invoke(am, new Object[] { config });
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
tanmeet
  • 220
  • 2
  • 11
6

This app does want you want https://play.google.com/store/apps/details?id=org.gnvo.langpicker&hl=en

Here is its source code https://code.google.com/p/languagepickerwidget/

Here is its main logic http://bin-liu.blogspot.in/2012/05/how-to-change-system-locale-calling.html

EDIT

After 4.2 new Jelly Bean, The protection level definition of CHANGE_CONFIGURATION has been changed,so the app will not work above 4.2, solution is (not valid anymore, this url is forwarding to a sex cam) http ://droider.eu/2013/07/22/morelocale-2-not-working-on-4-2-2-without-su-or-pm/

Christian
  • 4,596
  • 1
  • 26
  • 33
Nasrudeen
  • 425
  • 5
  • 18
  • Notice that this app hasn't been updated for 4 years. The technique it uses does not work on current versions of Android – zmarties Aug 27 '14 at 10:21
  • 1
    @zmarties : yes the issue because After 4.2 new Jelly Bean, The protection level definition of CHANGE_CONFIGURATION has been changed,so the app will not work above 4.2, solution is http://droider.eu/2013/07/22/morelocale-2-not-working-on-4-2-2-without-su-or-pm/ – Nasrudeen Aug 27 '14 at 10:33
  • @VivekPratapSingh I have answered this 5 years ago. I am not currently working in android app development. Many things might be changed in android in this 5 years. so Please check for google documentation. – Nasrudeen Jan 21 '20 at 09:46
2

Has someone else way. Like previous method, this request permissions:

    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION"/>

It possible get if we have admin access on device, this article https://snow.dog/blog/kiosk-mode-android determines how to access the admin access (root not needed).

And if we allow write settings:

if (!Settings.System.canWrite(this)) {
    Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS);
    intent.setData(Uri.parse("package:" + this.getPackageName()));
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivityForResult(intent, 12345);
}

And for get permissions we should run:

pm grant sk.lurity.defenqo android.permission.CHANGE_CONFIGURATION

Next, when I have access to change configuration, I try to set locale this way, but this way not working. Don't know why:

try {
  final String locale=(String)(Settings.System.class.getField("SYSTEM_LOCALES").get(null));
  Settings.System.putString(getContentResolver(),locale,"en-US,ru-UA");
} catch (NoSuchFieldException | IllegalAccessException ex){
  ex.printStackTrace();
}

with: You cannot change private secure settings. I found this article, https://www.fatalerrors.org/a/you-cannot-keep-your-settings-in-the-secure-settings.html But in fact this option has in sources:

        /**
         * These are all public system settings
         *
         * @hide
         */
        @UnsupportedAppUsage
        public static final Set<String> PUBLIC_SETTINGS = new ArraySet<>();
        static {
....
            PUBLIC_SETTINGS.add(SYSTEM_LOCALES);

in /android/provider/Settings.java So i can assume, this way start working, in API25, but i have only API24. i also try to grant permission:

<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>

But it is not solve nothing, at all.

However, i found solution in sources near SYSTEM_LOCALES definition:

        /**
         * The serialized system locale value.
         *
         * Do not use this value directory.
         * To get system locale, use {@link LocaleList#getDefault} instead.
         * To update system locale, use {@link com.android.internal.app.LocalePicker#updateLocales}
         * instead.
         * @hide
         */
        public static final String SYSTEM_LOCALES = "system_locales";

In this case i try call it internal class over reflection:

try {
    Class localePicker=Class.forName("com.android.internal.app.LocalePicker");
    Method updateLocales=localePicker.getMethod("updateLocales", LocaleList.class);
    LocaleList localeList=new LocaleList(new Locale("sk","SK"));
    updateLocales.invoke(null, localeList);
}catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException ex){
            ex.printStackTrace();
}

And this working, almost same like @Nasrudeen answer. It should working start from Android 7.0. And yes, again it is hack. Not as terrible as many of the solutions that I found, but also a hack.

Lubagov
  • 91
  • 1
  • 5