Wie bekomme ich einen String von verschiedenen Gebietsschemas in Android?

76

Daher möchte ich den Wert eines Strings in mehreren Gebietsschemas unabhängig von der aktuellen Gebietsschemaeinstellung des Geräts / der App abrufen. Wie soll ich das machen

Grundsätzlich brauche ich getString(int id, String locale)eher eine Funktion alsgetString(int id)

Wie könnte ich das machen?

Vielen Dank

Cheng Yang
quelle
1
Wie haben Sie das tatsächlich umgesetzt? Bitte antworten Sie :)
Rakeeb Rajbhandari
als akzeptierte Antwort
Cheng Yang
werden ResourceBundlesollen Vermögenswerte beziehen ?? oder können sie auf unseren res-Ordner zugreifen?
Rakeeb Rajbhandari
@chengyang bitte ändern Sie die akzeptierte Antwort
jonathanrz
Sie müssen eine andere Sprache als die übliche angeben? Wenn Sie mit Ressourcen in verschiedenen Sprachen arbeiten, erhalten Sie getString(R.strings.text)die Zeichenfolge in der Benutzersprache (oder in der Standardeinstellung, wenn die Benutzersprache keinen Ordner hat)
Lucas Queiroz Ribeiro

Antworten:

139

HINWEIS Wenn Ihre Mindest-API 17+ ist, gehen Sie direkt zum Ende dieser Antwort. Ansonsten lesen Sie weiter ...

HINWEIS Wenn Sie App-Bundles verwenden, müssen Sie sicherstellen, dass Sie entweder die Sprachaufteilung deaktivieren oder die andere Sprache dynamisch installieren. Siehe dazu https://stackoverflow.com/a/51054393 . Wenn Sie dies nicht tun, wird immer der Fallback verwendet.

Wenn Sie verschiedene Res-Ordner für verschiedene Gebietsschemas haben, können Sie Folgendes tun:

Configuration conf = getResources().getConfiguration();
conf.locale = new Locale("pl");
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
Resources resources = new Resources(getAssets(), metrics, conf);
String str = resources.getString(id);

Alternativ können Sie Ihre Aktivität einfach mit der von @jyotiprakash angegebenen Methode neu starten.

HINWEIS Wenn Sie den ResourcesKonstruktor so aufrufen, ändert sich intern etwas in Android. Sie müssen den Konstruktor mit Ihrem ursprünglichen Gebietsschema aufrufen, um die Dinge wieder so zu erhalten, wie sie waren.

BEARBEITEN Ein etwas anderes (und etwas saubereres) Rezept zum Abrufen von Ressourcen aus einem bestimmten Gebietsschema lautet:

Resources res = getResources();
Configuration conf = res.getConfiguration();
Locale savedLocale = conf.locale;
conf.locale = desiredLocale; // whatever you want here
res.updateConfiguration(conf, null); // second arg null means don't change

// retrieve resources from desired locale
String str = res.getString(id);

// restore original locale
conf.locale = savedLocale;
res.updateConfiguration(conf, null);

Ab API-Stufe 17 sollten Sie conf.setLocale()anstelle der direkten Einstellung verwenden conf.locale. Dadurch wird die Layoutrichtung der Konfiguration korrekt aktualisiert, wenn Sie zufällig zwischen Gebietsschemas von rechts nach links und von links nach rechts wechseln. (Layoutrichtung wurde in 17 eingeführt.)

Es macht keinen Sinn, ein neues ConfigurationObjekt zu erstellen (wie @Nulano in einem Kommentar vorschlägt), da durch das Aufrufen updateConfigurationdie ursprüngliche Konfiguration geändert wird, die durch das Aufrufen erhalten wurde res.getConfiguration().

Ich würde zögern, dies in eine getString(int id, String locale)Methode zu bündeln , wenn Sie mehrere Zeichenfolgenressourcen für ein Gebietsschema laden möchten . Das Ändern der Gebietsschemas (unter Verwendung eines der beiden Rezepte) erfordert, dass das Framework viel Arbeit leistet, um alle Ressourcen neu zu binden. Es ist viel besser, Gebietsschemas einmal zu aktualisieren, alles abzurufen, was Sie benötigen, und dann das Gebietsschema zurückzusetzen.

EDIT (Danke an @Mygod):

Wenn Ihre Mindest-API-Stufe 17+ ist, gibt es einen viel besseren Ansatz, wie in dieser Antwort in einem anderen Thread gezeigt. Sie können beispielsweise mehrere ResourceObjekte erstellen , eines für jedes Gebietsschema, das Sie benötigen:

@NonNull Resources getLocalizedResources(Context context, Locale desiredLocale) {
    Configuration conf = context.getResources().getConfiguration();
    conf = new Configuration(conf);
    conf.setLocale(desiredLocale);
    Context localizedContext = context.createConfigurationContext(conf);
    return localizedContext.getResources();
}

Rufen Sie dann einfach die gewünschten Ressourcen aus dem lokalisierten ResourceObjekt ab, das von dieser Methode zurückgegeben wird. Sie müssen nichts zurücksetzen, sobald Sie die Ressourcen abgerufen haben.

Ted Hopp
quelle
1
Sollten Sie nicht eine neue Konfiguration von der von zurückgebenen vornehmen getResources().getConfiguration()? Laut GrepCode wird eine lokale Instanz zurückgegeben, die in den Ressourcen gespeichert ist. Das spätere Ändern des Gebietsschemafelds ist wahrscheinlich der wahre Grund, warum Android verwirrt ist. Sie sollten wahrscheinlich conf = new Configuration(conf);(um die zurückgegebene Konfiguration zu klonen) zwischen der ersten und der zweiten Zeile tun .
Nulano
@Nulano - Es lohnt sich, es zu versuchen, aber ich glaube nicht, dass dies die Ursache dafür ist, warum Android "verwirrt" ist (wie Sie es ausdrücken). Schauen Sie sich die Quelle für den ResourcesKonstruktor an und Sie werden sehen, dass das Erstellen eines neuen ResourcesObjekts wie dieses die Dinge tief in den Assets des Prozesses verändert (anscheinend ein Singleton). Als ich merkte, dass die Dinge zurückgesetzt werden mussten, machte ich mir nicht die Mühe, genau herauszufinden, warum. Ein alternativer (möglicherweise sauberer) Ansatz zum Erstellen eines neuen ResourcesObjekts besteht darin, resources.updateConfiguration(conf, null)nach dem Ändern der Gebietsschemas aufzurufen .
Ted Hopp
1
Es gibt eine bessere API in API Level 17+: stackoverflow.com/a/33629163/2245107
Mygod
@Mygod - Ja, das scheint der richtige Weg zu sein. Ich werde meine Antwort aktualisieren, um dies anzuzeigen. Vielen Dank!
Ted Hopp
1
Die Antwort wurde mit einem Hinweis zu App-Bundles bearbeitet, die diese Funktionalität standardmäßig beeinträchtigen, da der Benutzer bei Bundles nur eine Sprache erhält. Informationen zum Beheben dieses Problems finden Sie unter stackoverflow.com/a/51054393 .
Carsten Hagemann
31

Hier ist eine kombinierte Version der von Ted Hopp beschriebenen Ansätze . Auf diese Weise funktioniert der Code für jede Android-Version:

public static String getLocaleStringResource(Locale requestedLocale, int resourceId, Context context) {
    String result;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { // use latest api
        Configuration config = new Configuration(context.getResources().getConfiguration());
        config.setLocale(requestedLocale);
        result = context.createConfigurationContext(config).getText(resourceId).toString();
    }
    else { // support older android versions
        Resources resources = context.getResources();
        Configuration conf = resources.getConfiguration();
        Locale savedLocale = conf.locale;
        conf.locale = requestedLocale;
        resources.updateConfiguration(conf, null);

        // retrieve resources from desired locale
        result = resources.getString(resourceId);

        // restore original locale
        conf.locale = savedLocale;
        resources.updateConfiguration(conf, null);
    }

    return result;
}

Anwendungsbeispiel:

String englishName = getLocaleStringResource(new Locale("en"), R.string.name, context);

Hinweis

Wie bereits in der ursprünglichen Antwort angegeben, ist es möglicherweise effizienter, mehrere Aufrufe des obigen Codes durch eine einzige Konfigurationsänderung und mehrere Aufrufe von resources.getString () zu ersetzen.

Johnson_145
quelle
2

Basierend auf den obigen Antworten, die fantastisch, aber ein wenig komplex sind. Probieren Sie diese einfache Funktion aus:

public String getResStringLanguage(int id, String lang){
    //Get default locale to back it
    Resources res = getResources();
    Configuration conf = res.getConfiguration();
    Locale savedLocale = conf.locale;
    //Retrieve resources from desired locale
    Configuration confAr = getResources().getConfiguration();
    confAr.locale = new Locale(lang);
    DisplayMetrics metrics = new DisplayMetrics();
    Resources resources = new Resources(getAssets(), metrics, confAr);
    //Get string which you want
    String string = resources.getString(id);
    //Restore default locale
    conf.locale = savedLocale;
    res.updateConfiguration(conf, null);
    //return the string that you want
    return string;
}

Dann nenne es einfach: String str = getResStringLanguage(R.string.any_string, "en");

Glücklicher Code :)

Amr Jyniat
quelle