Android - 如何通过程序更改设备系统语言?

11

我想从我的应用程序中更改设备语言环境(不仅仅是应用程序语言环境),就像用户可以在“设置”->“语言和键盘”->“语言”中做的那样。

请问有人能够解释一下如何做到这一点吗? 我已经搜索了几个小时,但没有找到方法。


如果你只是针对我们的应用程序,可以参考https://dev59.com/nHE85IYBdhLWcg3wMAfc。如果你是指任何应用程序,我认为不行——我认为这是一项受保护的设置。那么你最好启动键盘设置并让用户自己更改。 - Gabe Sechan
1
我是指任何应用程序。如果需要,我可以使用root权限。 - avicohh
我认为你不能直接这样做。最好的方法是弹出设置页面。 - Gabe Sechan
那就是他们所谓的“开放系统”吗? - Rodrigo
3个回答

7

是的,您可以更改任何Android版本的设备区域设置,但请确保您的应用程序是系统应用程序。以下是帮助您完成此操作的代码。如果您觉得有用,请为我的回答评分。

//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();
            }
        }

1
这很混乱。为什么要使用那么多反射?只需使用一个Context对象:context.getResources().getConfiguration().locale; - Chef Pharaoh
@ChefPharaoh 这只会改变应用程序的语言环境,而不是系统语言环境。 - user1506104
3
非常感谢!我在安卓8.1.0上测试了一下,它仍然可以使用! - Prizoff

6

这个应用程序提供了您所需的功能。 https://play.google.com/store/apps/details?id=org.gnvo.langpicker&hl=en

以下是它的源代码 https://code.google.com/p/languagepickerwidget/

以下是其主要逻辑 http://bin-liu.blogspot.in/2012/05/how-to-change-system-locale-calling.html

编辑

在4.2新版Jelly Bean之后,CHANGE_CONFIGURATION的保护级别定义已更改,因此该应用程序将无法在4.2及以上版本上运行。解决方法是(此网址不再有效,已转发到色情网站)http://droider.eu/2013/07/22/morelocale-2-not-working-on-4-2-2-without-su-or-pm/。


请注意,该应用程序已经4年没有更新了。它使用的技术在当前版本的Android上不起作用。 - zmarties
1
@zmarties:是的,问题在于4.2新版本的Jelly Bean中,CHANGE_CONFIGURATION的保护级别定义已更改,因此该应用程序将无法在4.2以上的系统上工作。解决方法请参见http://droider.eu/2013/07/22/morelocale-2-not-working-on-4-2-2-without-su-or-pm/。 - Nasrudeen
@VivekPratapSingh 我在5年前回答过这个问题。我目前不从事Android应用程序开发工作。在这5年中,许多事情可能已经在Android上发生了变化。因此,请查阅Google文档。 - Nasrudeen

2
有其他方法。与以前的方法类似,此请求需要权限:
    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION"/>

如果我们在设备上有管理员访问权限,就可以实现这一点。本文https://snow.dog/blog/kiosk-mode-android介绍了如何获取管理员访问权限(无需root)。
如果我们允许写入设置:
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);
}

获取权限的方法是运行以下命令:

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

接下来,当我有更改配置的访问权限时,我尝试使用以下方式设置区域设置,但这种方式不起作用。不知道为什么:

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();
}

关于:您无法更改私有安全设置。 我找到了这篇文章:https://www.fatalerrors.org/a/you-cannot-keep-your-settings-in-the-secure-settings.html 但实际上,这个选项已经在源代码中存在:

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

/android/provider/Settings.java中,我可以假设这种方式在API25中开始工作,但是我只有API24。我也尝试授予权限:
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>

但这并没有解决任何问题。

然而,我在SYSTEM_LOCALES定义附近的源代码中找到了解决方案:

        /**
         * 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";

在这种情况下,我尝试通过反射调用它的内部类:
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();
}

这个方法与 @Nasrudeen 的答案几乎相同,它可以在 Android 7.0 及以上版本中使用。是的,它仍然是一种 hack,虽然不像我发现的许多解决方案那么糟糕,但也是一种 hack。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接