2013-07-21 8 views
23

Przypadek użycia: rejestrowanie komunikatów o błędach wyświetlanych użytkownikowi.Android: jak uzyskać ciąg znaków w określonych lokalizacjach BEZ zmiany bieżących ustawień narodowych

Jednak nie chcesz, aby w dzienniku były wiadomości zależne od ustawień regionalnych urządzenia użytkownika. Z drugiej strony nie chcesz zmieniać ustawień narodowych urządzenia użytkownika tylko dla twojego (technicznego) logowania. Czy można to osiągnąć? znalazłem kilka możliwych rozwiązań tutaj na stackoverflow:

one jednak wszystkie powodują zmianę ustawień regionalnych mój urządzenie (do następnej zmiany konfiguracji).

W każdym razie, mój obecny obejście jest tak:

public String getStringInDefaultLocale(int resId) { 
    Resources currentResources = getResources(); 
    AssetManager assets = currentResources.getAssets(); 
    DisplayMetrics metrics = currentResources.getDisplayMetrics(); 
    Configuration config = new Configuration(
      currentResources.getConfiguration()); 
    config.locale = DEFAULT_LOCALE; 
    /* 
    * Note: This (temporiarily) changes the devices locale! TODO find a 
    * better way to get the string in the specific locale 
    */ 
    Resources defaultLocaleResources = new Resources(assets, metrics, 
      config); 
    String string = defaultLocaleResources.getString(resId); 
    // Restore device-specific locale 
    new Resources(assets, metrics, currentResources.getConfiguration()); 
    return string; 
} 

Szczerze mówiąc, nie podoba mi się takie podejście w ogóle. To nie jest wydajne i - myśląc o współbieżności i tak dalej - może to spowodować pewien widok w "złym" locale.

Tak - Jakieś pomysły? Może to można osiągnąć przy użyciu ResourceBundle s, tak jak w standardowej Java?

+0

Cześć, czy masz jakieś rozwiązanie? Też mam prawie taki sam przypadek użycia. – Nitish

+0

Nie, nadal używam obejścia określonego w pytaniu – schnatterer

+0

, dlaczego nie używasz konkretnego łańcucha znaków w języku angielskim do celów rejestrowania. R.string.dev_error_clicking_button_b. Jeśli nie przetłumaczysz go na inne języki (i nie będzie to konieczne), otrzymasz je domyślnie. –

Odpowiedz

10

Można to wykorzystać do API +17

@NonNull 
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) 
public static String getStringByLocal(Activity context, int id, String locale) { 
    Configuration configuration = new Configuration(context.getResources().getConfiguration()); 
    configuration.setLocale(new Locale(locale)); 
    return context.createConfigurationContext(configuration).getResources().getString(id); 
} 

Update (1): Jak wspierać stare wersje.

@NonNull 
public static String getStringByLocal(Activity context, int resId, String locale) { 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) 
     return getStringByLocalPlus17(context, resId, locale); 
    else 
     return getStringByLocalBefore17(context, resId, locale); 
} 

@NonNull 
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) 
private static String getStringByLocalPlus17(Activity context, int resId, String locale) { 
    Configuration configuration = new Configuration(context.getResources().getConfiguration()); 
    configuration.setLocale(new Locale(locale)); 
    return context.createConfigurationContext(configuration).getResources().getString(resId); 
} 

private static String getStringByLocalBefore17(Context context,int resId, String language) { 
    Resources currentResources = context.getResources(); 
    AssetManager assets = currentResources.getAssets(); 
    DisplayMetrics metrics = currentResources.getDisplayMetrics(); 
    Configuration config = new Configuration(currentResources.getConfiguration()); 
    Locale locale = new Locale(language); 
    Locale.setDefault(locale); 
    config.locale = locale; 
/* 
* Note: This (temporarily) changes the devices locale! TODO find a 
* better way to get the string in the specific locale 
*/ 
    Resources defaultLocaleResources = new Resources(assets, metrics, config); 
    String string = defaultLocaleResources.getString(resId); 
    // Restore device-specific locale 
    new Resources(assets, metrics, currentResources.getConfiguration()); 
    return string; 
} 

Update (2):Check this article

+0

Mogę to sprawdzić bez zmiany bieżących ustawień narodowych. Rozwiązany nareszcie! Przynajmniej dla API 17+ – schnatterer

+0

@schnatterer Możesz pewnie sprawdzić wersję kompilacji, aby obsługiwać starszą wersję 'if (Build.VERSION.SDK_INT> = Build.VERSION_CODES.JELLY_BEAN_MR1) return getStringByLocal (context, resId, locale); else return getStringByLocalBefore17 (context, resId, locale); ' –

3

można zapisać wszystkie ciągi domyślnej lokalizacji w Map wewnątrz klasy światowej, jak Application że jest wykonywany przy uruchamianiu aplikacji:

public class DualLocaleApplication extends Application { 

    private static Map<Integer, String> defaultLocaleString; 

    public void onCreate() { 
     super.onCreate(); 
     Resources currentResources = getResources(); 
     AssetManager assets = currentResources.getAssets(); 
     DisplayMetrics metrics = currentResources.getDisplayMetrics(); 
     Configuration config = new Configuration(
       currentResources.getConfiguration()); 
     config.locale = Locale.ENGLISH; 
     new Resources(assets, metrics, config); 
     defaultLocaleString = new HashMap<Integer, String>(); 
     Class<?> stringResources = R.string.class; 
     for (Field field : stringResources.getFields()) { 
      String packageName = getPackageName(); 
      int resId = getResources().getIdentifier(field.getName(), "string", packageName); 
      defaultLocaleString.put(resId, getString(resId)); 
     } 
     // Restore device-specific locale 
     new Resources(assets, metrics, currentResources.getConfiguration()); 
    } 

    public static String getStringInDefaultLocale(int resId) { 
     return defaultLocaleString.get(resId); 
    } 

} 

To rozwiązanie nie jest optymalne, ale nie będzie mają problemy z współbieżnością.

+0

Dzięki za zaproponowanie innej pracy! Jest to kompromis: nie ma problemów z współbieżnością, a wyższe zużycie pamięci i wolniejsze uruchamianie aplikacji. Jednak w moim konkretnym przypadku użycia nie potrzebuję zasobów w większości przypadków (tylko po to, aby rejestrować błędy/wyjątki). Stworzenie mapy i zachowanie jej w pamięci przy każdym starcie wydaje się tu trochę zbyt dużym wysiłkiem. – schnatterer

+1

To będzie zależeć od tego, ile zasobów ciągów faktycznie używasz dla tych błędów/wyjątków. Jeśli podzbiór jest mały, być może możesz z nim żyć. Innym podejściem (podejście do pracy: D) byłoby tymczasowe zapisanie tych zasobów w pliku tmp i uzyskanie dostępu do nich na żądanie. Żadne dodatkowe użycie pamięci w tym podejściu i może pasować do twoich potrzeb, tak myślę. –

+0

Naprawdę jesteś kreatywny, jeśli chodzi o znalezienie obejścia! +1 za to;) Dziękuję za twoje wysiłki - jednak wciąż mam nadzieję, że ktoś pojawi się z rozwiązaniem nieobowiązującym. – schnatterer

-2

Spróbuj tego:

Configuration conf = getResources().getConfiguration(); 
    conf.locale = new Locale("ar"); // locale I used here is Arabic 

    Resources resources = new Resources(getAssets(), getResources().getDisplayMetrics(), conf); 
    /* get localized string */ 
    String appName = resources.getString(R.string.app_name); 
+0

Czy brakuje mi czegoś lub czy nie ma różnicy między twoim podejściem a tym w pytaniu? Jeśli wykonasz 'nowe zasoby (getAssets(), getResources(). GetDisplayMetrics(), getResources(). GetConfiguration()). GetString (R.string.app_name); ciąg będzie nadal w języku arabskim, nawet jeśli twoje urządzenie język był inny wcześniej! Szukam rozwiązania dla następującego przypadku użycia: Twoje urządzenie działa, powiedzmy chiński, ale dziennik powinien wydrukować komunikat domyślny (np. Angielski). – schnatterer

+2

Uważaj, wywołanie 'new Resources' zmienia konfigurację' AssetManager'. Spowoduje to problemy później w niepowiązanych sekcjach kodu. Zobacz to [pytanie] (http://stackoverflow.com/questions/18984397/android-is-taking-wrong-layout-on-inflating-view) i [odpowiedź] (http://stackoverflow.com/a/20522668/262789) w przypadku problemów, które może powodować. – Benjamin

0

Dla api +17 moglibyśmy to wykorzystać:

public static String getDefaultString(Context context, @StringRes int stringId){ 
    Resources resources = context.getResources(); 
    Configuration configuration = new Configuration(resources.getConfiguration()); 
    Locale defaultLocale = new Locale("en"); 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 
     LocaleList localeList = new LocaleList(defaultLocale); 
     configuration.setLocales(localeList); 
     return context.createConfigurationContext(configuration).getString(stringId); 
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){ 
     configuration.setLocale(defaultLocale); 
     return context.createConfigurationContext(configuration).getString(stringId); 
    } 
    return context.getString(stringId); 
} 
Powiązane problemy