Como nenhuma resposta está completa para a maneira atual de resolver esse problema, tento dar instruções para uma solução completa. Comente se algo está faltando ou se pode ser melhorado.
Informação geral
Primeiro, existem algumas bibliotecas que desejam resolver o problema, mas todas parecem desatualizadas ou faltam alguns recursos:
Além disso, acho que escrever uma biblioteca pode não ser uma maneira boa / fácil de resolver esse problema, porque não há muito o que fazer, e o que precisa ser feito é mudar o código existente do que usar algo completamente dissociado. Portanto, eu compus as seguintes instruções que devem estar completas.
Minha solução é baseada principalmente em https://github.com/gunhansancar/ChangeLanguageExample (como já vinculado pelo localhost ). É o melhor código que encontrei para orientar. Algumas observações:
- Conforme necessário, ele fornece implementações diferentes para alterar a localidade do Android N (e acima) e abaixo
- Ele usa um método
updateViews()
em cada Atividade para atualizar manualmente todas as seqüências de caracteres após alterar a localidade (usando o habitual getString(id)
), o que não é necessário na abordagem mostrada abaixo
- Ele suporta apenas idiomas e não localidades completas (que também incluem região (país) e códigos variantes)
Mudei um pouco, dissociando a parte que persiste no código do idioma escolhido (como se pode fazer isso separadamente, conforme sugerido abaixo).
Solução
A solução consiste nas duas etapas a seguir:
- Alterar permanentemente o código do idioma a ser usado pelo aplicativo
- Faça com que o aplicativo use o conjunto de códigos de idioma personalizado, sem reiniciar
Etapa 1: alterar a localidade
Use a classe LocaleHelper
, com base no LocaleHelper de gunhansancar :
- Adicione um
ListPreference
em um PreferenceFragment
com os idiomas disponíveis (deve ser mantido quando os idiomas forem adicionados posteriormente)
import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.preference.PreferenceManager;
import java.util.Locale;
import mypackage.SettingsFragment;
/**
* Manages setting of the app's locale.
*/
public class LocaleHelper {
public static Context onAttach(Context context) {
String locale = getPersistedLocale(context);
return setLocale(context, locale);
}
public static String getPersistedLocale(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
return preferences.getString(SettingsFragment.KEY_PREF_LANGUAGE, "");
}
/**
* Set the app's locale to the one specified by the given String.
*
* @param context
* @param localeSpec a locale specification as used for Android resources (NOTE: does not
* support country and variant codes so far); the special string "system" sets
* the locale to the locale specified in system settings
* @return
*/
public static Context setLocale(Context context, String localeSpec) {
Locale locale;
if (localeSpec.equals("system")) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
locale = Resources.getSystem().getConfiguration().getLocales().get(0);
} else {
//noinspection deprecation
locale = Resources.getSystem().getConfiguration().locale;
}
} else {
locale = new Locale(localeSpec);
}
Locale.setDefault(locale);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return updateResources(context, locale);
} else {
return updateResourcesLegacy(context, locale);
}
}
@TargetApi(Build.VERSION_CODES.N)
private static Context updateResources(Context context, Locale locale) {
Configuration configuration = context.getResources().getConfiguration();
configuration.setLocale(locale);
configuration.setLayoutDirection(locale);
return context.createConfigurationContext(configuration);
}
@SuppressWarnings("deprecation")
private static Context updateResourcesLegacy(Context context, Locale locale) {
Resources resources = context.getResources();
Configuration configuration = resources.getConfiguration();
configuration.locale = locale;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
configuration.setLayoutDirection(locale);
}
resources.updateConfiguration(configuration, resources.getDisplayMetrics());
return context;
}
}
Crie SettingsFragment
como o seguinte:
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import mypackage.LocaleHelper;
import mypackage.R;
/**
* Fragment containing the app's main settings.
*/
public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String KEY_PREF_LANGUAGE = "pref_key_language";
public SettingsFragment() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_settings, container, false);
return view;
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
switch (key) {
case KEY_PREF_LANGUAGE:
LocaleHelper.setLocale(getContext(), PreferenceManager.getDefaultSharedPreferences(getContext()).getString(key, ""));
getActivity().recreate(); // necessary here because this Activity is currently running and thus a recreate() in onResume() would be too late
break;
}
}
@Override
public void onResume() {
super.onResume();
// documentation requires that a reference to the listener is kept as long as it may be called, which is the case as it can only be called from this Fragment
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
}
}
Crie um recurso locales.xml
listando todos os códigos de idioma com traduções disponíveis da seguinte maneira ( lista de códigos de código de idioma ):
<!-- Lists available locales used for setting the locale manually.
For now only language codes (locale codes without country and variant) are supported.
Has to be in sync with "settings_language_values" in strings.xml (the entries must correspond).
-->
<resources>
<string name="system_locale" translatable="false">system</string>
<string name="default_locale" translatable="false"></string>
<string-array name="locales">
<item>@string/system_locale</item> <!-- system setting -->
<item>@string/default_locale</item> <!-- default locale -->
<item>de</item>
</string-array>
</resources>
No seu, PreferenceScreen
você pode usar a seção a seguir para permitir que o usuário selecione os idiomas disponíveis:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="@string/preferences_category_general">
<ListPreference
android:key="pref_key_language"
android:title="@string/preferences_language"
android:dialogTitle="@string/preferences_language"
android:entries="@array/settings_language_values"
android:entryValues="@array/locales"
android:defaultValue="@string/system_locale"
android:summary="%s">
</ListPreference>
</PreferenceCategory>
</PreferenceScreen>
que usa as seguintes seqüências de caracteres de strings.xml
:
<string name="preferences_category_general">General</string>
<string name="preferences_language">Language</string>
<!-- NOTE: Has to correspond to array "locales" in locales.xml (elements in same orderwith) -->
<string-array name="settings_language_values">
<item>Default (System setting)</item>
<item>English</item>
<item>German</item>
</string-array>
Etapa 2: faça com que o aplicativo use a localidade personalizada
Agora configure cada atividade para usar o conjunto de localizações personalizadas. A maneira mais fácil de fazer isso é ter uma classe base comum para todas as atividades com o seguinte código (onde o código importante está attachBaseContext(Context base)
e onResume()
):
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import mypackage.LocaleHelper;
import mypackage.R;
/**
* {@link AppCompatActivity} with main menu in the action bar. Automatically recreates
* the activity when the locale has changed.
*/
public class MenuAppCompatActivity extends AppCompatActivity {
private String initialLocale;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initialLocale = LocaleHelper.getPersistedLocale(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_settings:
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(LocaleHelper.onAttach(base));
}
@Override
protected void onResume() {
super.onResume();
if (initialLocale != null && !initialLocale.equals(LocaleHelper.getPersistedLocale(this))) {
recreate();
}
}
}
O que faz é
- Substituir
attachBaseContext(Context base)
para usar o código de idioma persistido anteriormente comLocaleHelper
- Detecte uma alteração do código do idioma e recrie a Atividade para atualizar suas seqüências
Notas sobre esta solução
A recriação de uma atividade não atualiza o título da ActionBar (como já observado aqui: https://github.com/gunhansancar/ChangeLanguageExample/issues/1 ).
- Isso pode ser alcançado simplesmente com um método
setTitle(R.string.mytitle)
no onCreate()
método de cada atividade.
Ele permite que o usuário escolha o local padrão do sistema, bem como o local padrão do aplicativo (que pode ser nomeado, neste caso, "inglês").
fr-rCA
Até o momento, apenas códigos de idioma, nenhuma região (país) e códigos de variantes (como ) são suportados. Para oferecer suporte a especificações completas de código de idioma, pode ser usado um analisador semelhante ao da biblioteca de idiomas do Android (que suporta região, mas nenhum código de variante).
- Se alguém encontrar ou tiver escrito um bom analisador, adicione um comentário para que eu possa incluí-lo na solução.