如何让 Android 布局文件知道选择了不同的语言

How to let Android layout files know that a different language is selected

我有一个带有语言选择片段的应用程序 (FR_LanguageSelection ),您可以在这里看到:

package com.example.td.bapp;

import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.example.td.bapp.databinding.FragmentLanguageSelectionBinding;

/**

  Fragment for selecting the language of the app via ImageButtons
 *
 */

public class FR_LanguageSelection extends Fragment implements View.OnClickListener {



    /*
    String specifying the language of the App
     */
    public static String currentLanguageOfTheApp;
    public static final String LANGUAGE_GERMAN = "German";
    public static final String LANGUAGE_ENGLISH = "English";


    public FR_LanguageSelection() {
        // Required empty public constructor
    }


    public static FR_LanguageSelection newInstance(String param1, String param2) {
        FR_LanguageSelection fragment = new FR_LanguageSelection();

        return fragment;
    }

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

    }


    private FragmentLanguageSelectionBinding binding;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        binding = FragmentLanguageSelectionBinding.inflate(inflater, container, false);
        return binding.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        binding.imageButtonGermany.setOnClickListener(this);
        binding.imageButtonUK.setOnClickListener(this);
    }


    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
    }

    @Override
    public void onClick(View view) {

        if(view.getId() == R.id.imageButtonGermany) {
            this.currentLanguageOfTheApp = LANGUAGE_GERMAN;
            Navigation.findNavController(view).navigate
                    (FR_LanguageSelectionDirections.actionFRLanguageSelectionToFRMenu());


        }

        if(view.getId() == R.id.imageButtonUK) {
            this.currentLanguageOfTheApp = LANGUAGE_ENGLISH;
            Navigation.findNavController(view).navigate(FR_LanguageSelectionDirections.actionFRLanguageSelectionToFRMenu());
        }

        }




}

所以用户可以只选择一种语言,然后在我需要的其他片段中的静态变量中选择该语言,例如用于数据库查询。基本上这可以正常工作。但是,我创建了 XML-Layout 文件,这些文件使用两种不同语言的字符串资源。这是一个带有字符串资源“android:text="@string/size""

的 textView 示例
<TextView
    android:id="@+id/textViewS"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/size"
    android:textColor="#000000"
    android:textSize="@dimen/_15ssp"
    android:textStyle="bold"
    app:layout_constraintBottom_toBottomOf="@+id/radioGroup_Size"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.025"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="@+id/radioGroup_Size"
    app:layout_constraintVertical_bias="0.50" />

现在我的问题是,我如何告诉这个布局文件它应该根据用户选择的语言使用字符串资源?我可以直接从 FR_LanguageSelection 执行此操作,还是必须以编程方式在每个片段的 Java class 中执行此操作(我认为这没有意义,因为如果是这样,我为什么要定义字符串资源)?

我会感谢每一条评论,非常感谢你的帮助。

更新:

我只是将“SlothCoding”(参见他的回答)的建议代码复制到我的语言选择片段中。因此,当用户通过 onClick 方法中的 ImageButton 选择德语语言时,应执行代码(因此应设置应用程序的当前语言)。不幸的是,编译器告诉我一个错误:“无法解析符号 'activity'”。也许错误发生是因为我使用了单activity-多片段方法?

这是更新后的 class:

package com.example.td.bapp;

import android.content.ContextWrapper;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.example.td.bapp.databinding.FragmentLanguageSelectionBinding;

import java.util.Locale;

/**

  Fragment for selecting the language of the app via ImageButtons
 *
 */

public class FR_LanguageSelection extends Fragment implements View.OnClickListener {



    /*
    String specifying the language of the App
     */
    public static String currentLanguageOfTheApp;
    public static final String LANGUAGE_GERMAN = "German";
    public static final String LANGUAGE_ENGLISH = "English";


    public FR_LanguageSelection() {
        // Required empty public constructor
    }


    public static FR_LanguageSelection newInstance(String param1, String param2) {
        FR_LanguageSelection fragment = new FR_LanguageSelection();

        return fragment;
    }

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

    }


    private FragmentLanguageSelectionBinding binding;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        binding = FragmentLanguageSelectionBinding.inflate(inflater, container, false);
        return binding.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        binding.imageButtonGermany.setOnClickListener(this);
        binding.imageButtonUK.setOnClickListener(this);
    }


    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    @Override
    public void onClick(View view) {

        if(view.getId() == R.id.imageButtonGermany) {
            this.currentLanguageOfTheApp = LANGUAGE_GERMAN;

            Locale locale;
            locale = new Locale("de", "DE");

            Configuration config = new Configuration(activity.getBaseContext().getResources().getConfiguration());
            Locale.setDefault(locale);
            config.setLocale(locale);

            activity.getBaseContext().getResources().updateConfiguration(config,
                    activity.getBaseContext().getResources().getDisplayMetrics());

            Navigation.findNavController(view).navigate
                    (FR_LanguageSelectionDirections.actionFRLanguageSelectionToFRMenu());


        }

        if(view.getId() == R.id.imageButtonUK) {
            this.currentLanguageOfTheApp = LANGUAGE_ENGLISH;
            Navigation.findNavController(view).navigate(FR_LanguageSelectionDirections.actionFRLanguageSelectionToFRMenu());
        }

        }




}

这不是执行此操作的最佳方法,因为 Android 应使用 phone 中的区域设置来为您的应用程序设置默认语言。但是一年前我不得不在我的应用程序中做同样的事情,我被迫在应用程序中使用语言环境配置。这是怎么回事。首先,我要求用户选择他想要的语言并将其保存在我的 SharedPreferences 中。我有一个我创建为 JSONObjects 的语言列表,其中有

  • language_name(例如英语)
  • language_code(例如 en)
  • language_script(美国除外)

我是这样做的:

language_object = new JSONObject();
language_object.put("language_name", "Deutsch");
language_object.put("language_code", "de");
language_object.put("language_script", "LT");
languages.put(language_object);

language_object = new JSONObject();
language_object.put("language_name", "English");
language_object.put("language_code", "en");
language_object.put("language_script", "US");
languages.put(language_object);

现在我使用 ListView 显示这些语言的列表并处理 LanguageListAdapter 中的点击。当用户 select 使用他想要的语言时,我会在 ListAdapter:

中执行此操作
@Override
public View getView(final int position, View convertView, ViewGroup parent) {

    if(convertView == null) {
        LayoutInflater layout_inflater = LayoutInflater.from(_context);
        convertView = layout_inflater.inflate(R.layout.item_language, parent, false);
    }

    try {

        JSONObject object = _data_set.getJSONObject(position);

        TextView lang_name = convertView.findViewById(R.id.language_name);
        lang_name.setText(object.getString("language_name"));

        convertView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {

                    SharedPreferences shared_preferences = activity.getBaseContext().getSharedPreferences("MY_PREFERENCES", MODE_PRIVATE);
                    SharedPreferences.Editor preferences_editor = shared_preferences.edit();
                    preferences_editor.putString("MY_LANGUAGE", object.toString());
                    preferences_editor.apply();

                    common.setLocale(activity);

                    Intent intent = new Intent(activity, LoadingActivity.class);
                    activity.startActivity(intent);
                    activity.finish();
                } catch (Exception e) {
                    e.getMessage();
                }

            }
        });
    } catch (Exception e) {
        e.getMessage();
    }
    return convertView;
}

我的函数 setLocale(Activity activity) 位于名为 common 的单独 class 中。我使用这个 class 来存储我在整个应用程序中经常使用的函数。它是这样的:

public static void setLocale(Activity activity){

    try {
        SharedPreferences shared_preferences = activity.getBaseContext().getSharedPreferences("MY_PREFERENCES", MODE_PRIVATE);
        String language = shared_preferences.getString("MY_LANGUAGE", "");

        JSONObject object = new JSONObject(language);

        Locale locale;
        locale = new Locale(object.getString("language_code"), object.getString("language_script"));
        Configuration config = new Configuration(activity.getBaseContext().getResources().getConfiguration());
        Locale.setDefault(locale);
        config.setLocale(locale);

        activity.getBaseContext().getResources().updateConfiguration(config,
                activity.getBaseContext().getResources().getDisplayMetrics());
    } catch (Exception e) {
        e.getMessage();
    }
}

这设置了我想要的语言并使用基于 language_codelanguage_scriptstrings.xml。所以在我的 res > values 中我有这个例如:

  • strings.xml - 默认语言
  • strings.xml(en-rUS) - 英语
  • strings.xml(de-rLT) - 自定义德语(自定义是指自定义代码脚本标签)

现在,每当用户 selects,比方说德语,我将“de-LT”保存为语言代码并在 setLocale 中使用它,以便应用程序可以读取来自 strings.xml 资源。


编辑: 如何为每种语言创建 strings.xml

因此,要执行此操作,您需要先转到 res > values 文件夹。在那里您将拥有默认的 strings.xml 文件。当您没有为用户的区域设置 phone 设置定义 strings.xml 时使用此选项。默认情况下,如果应用程序可以在国际上使用,这应该是英文的,原因很简单,你可能知道为什么。

在此 strings.xml 中,您可以定义您在应用程序中使用的所有字符串,并且您可以这样做:

<resources>
    <string name="app_name">TempProject</string>

    <string name="Size">Size</string>

</resources>

现在,您想要为德语和克罗地亚语创建翻译。你需要这样做。 右键单击值文件夹 > 新建 > 值资源文件

现在将打开一个新的 window,您需要这样填写:

所以,我们慢慢来吧,一场一场的。

  • 文件名:strings.xml - 与默认文件同名
  • 目录名:values-de-rDE - 这定义了带有德国国家代码的目录名,这就是编译器如何知道从哪个文件读取您定义的字符串。如果您不知道国家代码,您可以简单地使用 Available qualifiers 和 select Locale > 从 Language 部分选择“de: German” > From Specific Region Only 选择你想要的,在我的案例我使用了“DE: Germany”。这将自动生成与我上面相同的目录名称:

现在你有了新的 strings.xml 文件,但它在右边有一个像这样的标签 (de-rDE)。现在在该文件中定义与默认相同的字符串 strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="Size">Größe</string>

</resources>

现在你可以看到我没有在这个资源文件中为应用程序名称定义字符串,你会收到一个警告。当您进入默认 strings.xml 时,您会看到:

因此,请确保您在 strings.xml 个文件中的所有文件中定义了相同的字符串。

除了默认文件外,我还按照上面的所有内容添加了更多克罗地亚语和英语翻译。所以,现在我有了这个:

现在你可以使用我的函数 setLocale() 并在这一行中提供国家代码:

Locale locale;
locale = new Locale("de", "DE");
Configuration config = new Configuration(activity.getBaseContext().getResources().getConfiguration());
Locale.setDefault(locale);
config.setLocale(locale);

activity.getBaseContext().getResources().updateConfiguration(config,
            activity.getBaseContext().getResources().getDisplayMetrics());

你可以看到对于第二个参数我只输入了“DE”而不是“rDE”。这会将区域设置设置为德语,您的应用程序将使用 strings.xml (de-rDE) 文件作为字符串资源。