Thay đổi ngôn ngữ trong chính ứng dụng


197

Người dùng của tôi có thể thay đổi Ngôn ngữ trong ứng dụng (họ có thể muốn giữ cài đặt điện thoại bằng tiếng Anh nhưng đọc nội dung ứng dụng của tôi bằng tiếng Pháp, tiếng Hà Lan hoặc bất kỳ ngôn ngữ nào khác ...)

Tại sao điều này hoạt động hoàn toàn tốt trong 1.5 / 1.6 nhưng KHÔNG còn trong 2.0 nữa ???

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch(item.getItemId()) {
    case 201:
        Locale locale2 = new Locale("fr"); 
        Locale.setDefault(locale2);
        Configuration config2 = new Configuration();
        config2.locale = locale2;
        getBaseContext().getResources().updateConfiguration(
            config2, getBaseContext().getResources().getDisplayMetrics());
        // loading data ...
        refresh();
        // refresh the tabs and their content
        refresh_Tab ();   
     break;
     case 201: etc...

Vấn đề là MENU "thu nhỏ" ngày càng nhiều hơn mỗi khi người dùng trải qua các dòng mã ở trên ...

Đây là Menu bị thu hẹp:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    menu.add(0, 100, 1, "REFRESH").setIcon(android.R.drawable.ic_menu_compass);
    SubMenu langMenu = menu.addSubMenu(0, 200, 2, "NL-FR").setIcon(android.R.drawable.ic_menu_rotate);
        langMenu.add(1, 201, 0, "Nederlands");
        langMenu.add(1, 202, 0, "Français");
    menu.add(0, 250, 4, R.string.OptionMenu2).setIcon(android.R.drawable.ic_menu_send);
    menu.add(0, 300, 5, R.string.OptionMenu3).setIcon(android.R.drawable.ic_menu_preferences);
    menu.add(0, 350, 3, R.string.OptionMenu4).setIcon(android.R.drawable.ic_menu_more);
    menu.add(0, 400, 6, "Exit").setIcon(android.R.drawable.ic_menu_delete);

    return super.onCreateOptionsMenu(menu);
}

Tôi nên làm gì trong API cấp 5 để làm cho công việc này hoạt động trở lại?

ĐÂY LÀ MÃ SỐ ĐẦY ĐỦ NẾU BẠN MUỐN KIỂM TRA NÀY:

import java.util.Locale;

import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.widget.Toast;

public class Main extends Activity {
    /** Called when the activity is first created. */


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        SubMenu langMenu = menu.addSubMenu(0, 200, 2, "NL-FR").setIcon(android.R.drawable.ic_menu_rotate);
            langMenu.add(1, 201, 0, "Nederlands");
            langMenu.add(1, 202, 0, "Français");

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()){

        case 201:

            Locale locale = new Locale("nl"); 
            Locale.setDefault(locale);
            Configuration config = new Configuration();
            config.locale = locale;
            getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
            Toast.makeText(this, "Locale in Nederlands !", Toast.LENGTH_LONG).show();
            break;

        case 202:

            Locale locale2 = new Locale("fr"); 
            Locale.setDefault(locale2);
            Configuration config2 = new Configuration();
            config2.locale = locale2;
            getBaseContext().getResources().updateConfiguration(config2, getBaseContext().getResources().getDisplayMetrics());

            Toast.makeText(this, "Locale en Français !", Toast.LENGTH_LONG).show();
            break;  

        }
        return super.onOptionsItemSelected(item);
    }
}

VÀ ĐÂY LÀ SỰ TUYỆT VỜI:

<?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.cousinHub.ChangeLocale"
          android:versionCode="1"
          android:versionName="1.0">
        <application android:icon="@drawable/icon" android:label="@string/app_name">
            <activity android:name=".Main"
                      android:label="@string/app_name">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
        <uses-sdk android:minSdkVersion="3" /> 
    </manifest>

Đây là những gì tôi thành lập:

<uses-sdk android:minSdkVersion="5" />

=> CÔNG TRÌNH NÓ CHỈ LÀ ...

<uses-sdk android:minSdkVersion="3" />

=> Menu co lại mỗi khi bạn thay đổi ngôn ngữ !!!

vì tôi muốn giữ cho ứng dụng của mình có thể truy cập được cho người dùng trên 1.5, tôi phải làm sao ??


Bạn có ý nghĩa gì bởi "thu nhỏ"?
CommonsWare

nó trở nên nhỏ hơn và nhỏ hơn mỗi lần Tôi có nên sử dụng cái gì khác ngoài getBaseContext không?
Hubert

Có lẽ đó không phải là những dòng tạo ra vấn đề như khi tôi làm một ứng dụng rất cơ bản chỉ thực hiện thay đổi trong Locale, tôi không có cùng "thu nhỏ menu". Tôi nghĩ rằng đó có thể là do Hoạt động của tôi thực tế là TabActivity, nhưng ngay cả với điều đó, tôi không thể tạo lại vấn đề. Tôi sẽ phải điều tra thêm nguyên nhân chính xác của lỗi này là gì ... đừng tìm kiếm thêm nữa. Tôi sẽ đăng câu trả lời ở đây khi tôi tìm thấy nó. Chúc mừng, H.
Hubert

Tôi đã chỉnh sửa bài viết đầu tiên của mình, đưa ra một ví dụ về cách tạo ra vấn đề. Và tôi nhận thấy rằng trên thực tế, khi tôi thay đổi dòng <used-sdk android: minSdkVersion = "5" /> thành "3" ... thực sự vấn đề đã xuất hiện!
Hubert

1
Bạn có thể sử dụng thư viện sau, cung cấp danh sách ngôn ngữ, tùy chọn cho màn hình cài đặt của bạn và ghi đè ngôn ngữ trong ứng dụng của bạn: github.com/delight-im/Android-Lacular
caw 24/03 '

Câu trả lời:


151

Thông qua câu hỏi ban đầu không chính xác về bản địa, tất cả các câu hỏi liên quan đến địa phương khác đều được đề cập đến câu hỏi này. Đó là lý do tại sao tôi muốn làm rõ vấn đề ở đây. Tôi đã sử dụng câu hỏi này như một điểm khởi đầu cho mã chuyển đổi ngôn ngữ địa phương của riêng tôi và phát hiện ra rằng phương pháp này không chính xác. Nó hoạt động, nhưng chỉ cho đến khi có bất kỳ thay đổi cấu hình nào (ví dụ xoay màn hình) và chỉ trong Hoạt động cụ thể đó. Chơi với một mã trong một thời gian tôi đã kết thúc với cách tiếp cận sau:

Tôi đã mở rộng android.app.Ứng dụng và thêm đoạn mã sau:

public class MyApplication extends Application
{
    private Locale locale = null;

    @Override
    public void onConfigurationChanged(Configuration newConfig)
    {
        super.onConfigurationChanged(newConfig);
        if (locale != null)
        {
            newConfig.locale = locale;
            Locale.setDefault(locale);
            getBaseContext().getResources().updateConfiguration(newConfig, getBaseContext().getResources().getDisplayMetrics());
        }
    }

    @Override
    public void onCreate()
    {
        super.onCreate();

        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);

        Configuration config = getBaseContext().getResources().getConfiguration();

        String lang = settings.getString(getString(R.string.pref_locale), "");
        if (! "".equals(lang) && ! config.locale.getLanguage().equals(lang))
        {
            locale = new Locale(lang);
            Locale.setDefault(locale);
            config.locale = locale;
            getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
        }
    }
}

Mã này đảm bảo rằng mọi Hoạt động sẽ có bộ ngôn ngữ tùy chỉnh và nó sẽ không được đặt lại khi xoay và các sự kiện khác.

Tôi cũng đã dành rất nhiều thời gian để cố gắng thay đổi tùy chọn để áp dụng ngay lập tức nhưng không thành công: ngôn ngữ đã thay đổi chính xác khi khởi động lại Activity, nhưng định dạng số và các thuộc tính miền địa phương khác không được áp dụng cho đến khi khởi động lại toàn bộ ứng dụng.

Thay đổi AndroidManifest.xml

Đừng quên thêm android:configChanges="layoutDirection|locale"vào mọi hoạt động tại AndroidManifest, cũng như android:name=".MyApplication"thành <application>phần.


1
Bạn có thích hoạt động được gọi từ bạn hoạt động chính? bạn có thể đặt cái này trong sơ yếu lý lịch ... @Override void void onResume () {if (! Làm tươi(); } super.onResume (); } private void refresh () {finish (); Intent myIntent = new Intent (Main.this, Main. Class); startActivity (myIntent); }
trgraglia

1
Đây thực sự là những gì tôi cần, tôi đã từng gọi updateConfiguration () trong MainActivity nhưng các menu không cập nhật khi Ứng dụng kết thúc sau đó bắt đầu. Bạn giải pháp là đúng. Nếu bạn muốn làm mới chuỗi ngay lập tức, tôi nghĩ bạn vẫn phải đặt lại chuỗi theo cách thủ công. (ví dụ: đặt lại nhãn nút hoặc menu tạo lại).
emeraldhieu

Điều này có ảnh hưởng đến ngôn ngữ hệ thống Android không?
Kostadin

17
Lưu ý rằng bạn không bao giờ phải sửa đổi một đối tượng Cấu hình mà Android cung cấp cho bạn ( confignewConfig). Bạn nên tạo một bản sao với config = new Configuration(config);đầu tiên. Tôi chỉ mất một giờ để gỡ lỗi mã này trước khi tìm ra vấn đề (nó đã khiến hoạt động này nhấp nháy vô tận).
imgx64

1
Kỹ thuật này có trục trặc trên Android 7+ khi khởi động ứng dụng từ lúc bắt đầu lạnh, với kích thước phông chữ hệ thống được đặt thành lớn. Google đã thay đổi cách updateConfiguration()hoạt động, do đó, đôi khi Hoạt động của bạn sẽ được hiển thị bằng 2 ngôn ngữ khác nhau (đặc biệt là trong Hộp thoại). Thông tin thêm: stackoverflow.com/questions/39705739/ Kẻ
Mr-IDE

61

Sau một đêm ngon giấc, tôi tìm thấy câu trả lời trên Web (một tìm kiếm đơn giản trên Google trên dòng sau " getBaseContext().getResources().updateConfiguration(mConfig, getBaseContext().getResources().getDisplayMetrics());"), đây là:

liên kết văn bản => liên kết này cũng cho thấy screenshotsnhững gì đang xảy ra!

Mật độ là vấn đề ở đây , tôi cần phải có điều này trong AndroidManifest.xml

<supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:anyDensity="true"
/>

Điều quan trọng nhất là android: anyD mật = "true" .

Đừng quên thêm các mục sau vào AndroidManifest.xmlcho mọi hoạt động (dành cho Android 4.1 trở xuống):

android:configChanges="locale"

Phiên bản này là cần thiết khi bạn xây dựng cho Android 4.2 (API cấp 17) giải thích tại đây :

android:configChanges="locale|layoutDirection"

3
Tôi đã có android: anyD mật = "sai" do lời khuyên của Google (Chiến lược cho các ứng dụng kế thừa - điểm 9: developer.android.com/intl/fr/guide/practices/ tựa ). Hãy cẩn thận rồi!
Hubert

2
Các văn bản trên các nút không thay đổi sau khi tôi thay đổi ngôn ngữ. Tôi tự hỏi là có giải pháp nào để làm mới tất cả các widget chứa tài nguyên chuỗi. Bây giờ tôi đang vẽ lại văn bản bằng cách sử dụng setText (getString (R.opes.button_label)) trong OnRestart (). (Tất nhiên là nó thay đổi sau khi khởi động lại Ứng dụng.)
emeraldhieu

bạn có thể gọi hoạt động tái tạoactivity.recreate()
Tới Kra

59

Trong Android M, giải pháp hàng đầu sẽ không hoạt động. Tôi đã viết một lớp trợ giúp để sửa lỗi mà bạn nên gọi từ lớp Ứng dụng và tất cả các Hoạt động (Tôi sẽ đề xuất tạo BaseActivity và sau đó làm cho tất cả các Hoạt động được kế thừa từ nó.

Ghi chú : Điều này cũng sẽ hỗ trợ hướng bố trí RTL đúng cách.

Lớp người trợ giúp:

public class LocaleUtils {

    private static Locale sLocale;

    public static void setLocale(Locale locale) {
        sLocale = locale;
        if(sLocale != null) {
            Locale.setDefault(sLocale);
        }
    }

    public static void updateConfig(ContextThemeWrapper wrapper) {
        if(sLocale != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            Configuration configuration = new Configuration();
            configuration.setLocale(sLocale);
            wrapper.applyOverrideConfiguration(configuration);
        }
    }

    public static void updateConfig(Application app, Configuration configuration) {
        if (sLocale != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
            //Wrapping the configuration to avoid Activity endless loop
            Configuration config = new Configuration(configuration);
            // We must use the now-deprecated config.locale and res.updateConfiguration here,
            // because the replacements aren't available till API level 24 and 17 respectively.
            config.locale = sLocale;
            Resources res = app.getBaseContext().getResources();
            res.updateConfiguration(config, res.getDisplayMetrics());
        }
    }
}

Ứng dụng:

public class App extends Application {
    public void onCreate(){
        super.onCreate();

        LocaleUtils.setLocale(new Locale("iw"));
        LocaleUtils.updateConfig(this, getBaseContext().getResources().getConfiguration());
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        LocaleUtils.updateConfig(this, newConfig);
    }
}

Cơ sở:

public class BaseActivity extends Activity {
    public BaseActivity() {
        LocaleUtils.updateConfig(this);
    }
}

11
Điều này nên được chấp nhận câu trả lời. Đảm bảo updateConfig nên được gọi từ nhà xây dựng chứ không phải từ onCreate của lớp hoạt động, tôi đã mắc lỗi này ..
Hitesh Sahu

1
nó phụ thuộc vào cách bạn thực hiện điều này trong chương trình của bạn. Nếu bạn đã ghi đè lên onConfigurationChanged (), bạn không cần gọi .updateConfig () và đó là lý do tại sao bạn gặp lỗi, vì nó được gọi hai lần. nếu bạn đang nói rằng chỉ gọi .setLocale () không hoạt động, thì điều này là do bạn cần làm mới giao diện người dùng của mình, bằng cách gọi tái tạo () trên hoạt động hoặc phương thức của riêng bạn trong các hoạt động / đoạn (tôi thậm chí còn thích cách trước nó không được suôn sẻ nhưng bạn sẽ không bao giờ lo lắng về các vấn đề)
RJFares

3
// nhập android.support.v7.view.ContextThemeWrapper; nhập android.view.ContextThemeWrapper; // không gian tên chính xác
Mehdi Rostami

1
Không nên BaseActivity()gọi nhà xây dựng super()?
LarsH

1
Liệu giải pháp này ngầm bao gồm đưa <application android:name=".App">vào bảng kê khai? Làm thế nào về việc thêm android:configChanges="layoutDirection|locale"vào mọi hoạt động trong bảng kê khai?
LarsH

10

Đây là nhận xét của tôi về câu trả lời của Andrey nhưng tôi không thể bao gồm mã trong các bình luận.

Bạn có thích hoạt động được gọi từ bạn hoạt động chính? bạn có thể đặt nó trong sơ yếu lý lịch ...

@Override
protected void onResume() {
    if (!(PreferenceManager.getDefaultSharedPreferences(
            getApplicationContext()).getString("listLanguage", "en")
            .equals(langPreference))) {
        refresh();
    }
    super.onResume();
}

private void refresh() {
    finish();
    Intent myIntent = new Intent(Main.this, Main.class);
    startActivity(myIntent);
}

2
Ý bạn là nếu tôi muốn thay đổi Ngôn ngữ trong thời gian chạy thì tôi phải kết thúc hoạt động đó và bắt đầu lại hoạt động đó để có thay đổi? Có cách nào khác để khiến nó thay đổi vì nó không tốt khi mỗi lần kết thúc hoạt động và bắt đầu lại.
Shreyash Mahajan

1
làm tốt hơnactivity.recreate()
Đến Kra

@trgradlia, tôi muốn sử dụng mã trên. Xin vui lòng cho tôi biết, langPreference là gì? Tôi nghĩ rằng chúng ta đang so sánh lang đã thay đổi gần đây và lang trước đó.
Mahen

1
Hoạt động có recreate()phương thức để sử dụng recreate()thay refresh()cho mã của bạn. Điều này sẽ lưu 3 dòng mã của bạn theo refresh()phương thức
Inzimam Tariq IT

1
@InzimamTariqIT, Cảm ơn vì tiền boa ... Câu trả lời đã gần 7 năm rồi nên tôi sẽ để nguyên như vậy và mọi người có thể tìm thấy sự cải thiện của bạn ở đây trong các bình luận.
trgraglia

6

Tôi không thể sử dụng android: anyD mật = "true" vì các đối tượng trong trò chơi của tôi sẽ được định vị hoàn toàn khác nhau ... có vẻ như điều này cũng có mẹo:

// tạo miền địa phương
Locale locale2 = new Locale (loc); 
Locale.setDefault (locale2);
Cấu hình config2 = new Cấu hình ();
config2.locale = locale2;

// cập nhật ngôn ngữ
mContext.getResource (). updateConfiguration (config2, null);

Giải pháp này là tốt nhất cho tôi chỉ sử dụng nàycode Locale.setDefault(mLocale); Configuration config = getBaseContext().getResources().getConfiguration(); if (!config.locale.equals(mLocale)) { config.locale = mLocale; getBaseContext().getResources().updateConfiguration(config, null); }
Huds0nHawk

1

Nếu bạn muốn thực hiện các tùy chọn menu để thay đổi ngôn ngữ ngay lập tức. Bạn phải làm như thế này.

//onCreate method calls only once when menu is called first time.
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    //1.Here you can add your  locale settings . 
    //2.Your menu declaration.
}
//This method is called when your menu is opend to again....
@Override
public boolean onMenuOpened(int featureId, Menu menu) {
    menu.clear();
    onCreateOptionsMenu(menu);
    return super.onMenuOpened(featureId, menu);
}

Nhưng không phải giải pháp này sẽ xóa menu trên mỗi menu đang mở? Tôi cho rằng onMenuOpenedchỉ nên được gọi một lần, sau khi thay đổi ngôn ngữ.
trante
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.