流行好用的软件应该能够适用于不同地区的市场。下面记录一些在项目中国际化的应用,有关图片和文本资源的自适应。
Android采用XML资源文件来管理所有字符串消息,如果系统设置的Custom Locale,没有对应的本地化资源文件,那么程序就会取默认的res\values\strings.xml。在此我们默认的strings为英文,如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">L10NTest</string>
<string name="action_settings">Settings</string>
<string name="hello_world">Hello world!</string>
<string name="I_love_you">I love you.</string>
</resources>
我们添加有关中文的字符串资源,这需要为values目录添加中文的语言国家版本。values文件夹的命名方式为:values-语言代码-r国家代码。
如果想要支持中文的话,则需要在res目录下添加values-zh-rCN。该文件夹中的strings如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">国际化测试</string>
<string name="action_settings">设置</string>
<string name="hello_world">世界,你好!</string>
<string name="I_love_you">我爱你.</string>
</resources>
如果希望应用的图片也能随语言环境改变,则需要为drawable目录添加其他的语言国家版本。drawable文件夹的命名方式为:drawable-语言代码-r国家代码。同样支持中文的话需要在res目录下添加drawable-zh-rCN。
如果还需要为drawable目录按照分辨率提供文件夹,可以在其后追加分辨率后缀,如:drawable-zh-rCN-mdpi、drawable-en-rUS-xhdpi等等。
主Activity中没有写任何东西,下面是它的XML布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/country"
tools:ignore="ContentDescription" />
<!-- tools:ignore="ContentDescription",屏蔽掉对View设置备注说明的提示 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/I_love_you"
android:textSize="20sp" />
</LinearLayout>
在android ADT 16.0以后的控件中,没有文本描述的view如果不加android:contentDescription="@string/……"的话就会有黄 色的下划线,提示“[Accessibility] Missing contentDescription attribute on p_w_picpath”。加“ tools:ignore="ContentDescription"“便是屏蔽掉该提示。这句代码不加也没什么影响,在界面上不会有其他效果,当然也可以写一些字符串数据做提示,方便他人阅读。或者直接写成:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/country" />
也可。
以下是应用的res结构:
我增加了”drawable-en-rUS“、”drawable-zh-rCN“和”values-zh-rCN“三个文件夹,其中前两个文件夹中放了名称都是”country“的图片,只是前者为美国国旗图片,后者为中国国旗图片。第三个文件夹中的strings文件上面已给出。
运行程序,如果设置系统的语言和输入为中文,效果图如下:
如果设置为英文,效果图如下:
我并没有增加有关英文的本地化资源文件,所以程序就会取res\values\下的strings.xml,当然也可以增加对应的文件夹为:values-en-rUS,其中放置相关文本资源。
如果是其他国家的语言环境,那文件夹该如何命名呢,以字符串资源为例,如下:
中文(中国):values-zh-rCN
中文(台湾):values-zh-rTW
中文(香港):values-zh-rHK
英语(美国):values-en-rUS
英语(英国):values-en-rGB
英文(澳大利亚):values-en-rAU
英文(加拿大):values-en-rCA
英文(爱尔兰):values-en-rIE
英文(印度):values-en-rIN
英文(新西兰):values-en-rNZ
英文(新加坡):values-en-rSG
英文(南非):values-en-rZA
阿拉伯文(埃及):values-ar-rEG
阿拉伯文(以色列):values-ar-rIL
保加利亚文: values-bg-rBG
加泰罗尼亚文:values-ca-rES
捷克文:values-cs-rCZ
丹麦文:values-da-rDK
德文(奥地利):values-de-rAT
德文(瑞士):values-de-rCH
德文(德国):values-de-rDE
德文(列支敦士登):values-de-rLI
希腊文:values-el-rGR
西班牙文(西班牙):values-es-rES
西班牙文(美国):values-es-rUS
芬兰文(芬兰):values-fi-rFI
法文(比利时):values-fr-rBE
法文(加拿大):values-fr-rCA
法文(瑞士):values-fr-rCH
法文(法国):values-fr-rFR
希伯来文:values-iw-rIL
印地文:values-hi-rIN
克罗里亚文:values-hr-rHR
匈牙利文:values-hu-rHU
印度尼西亚文:values-in-rID
意大利文(瑞士):values-it-rCH
意大利文(意大利):values-it-rIT
日文:values-ja-rJP
韩文:values-ko-rKR
立陶宛文:valueslt-rLT
拉脱维亚文:values-lv-rLV
挪威博克马尔文:values-nb-rNO
荷兰文(比利时):values-nl-BE
荷兰文(荷兰):values-nl-rNL
波兰文:values-pl-rPL
葡萄牙文(巴西):values-pt-rBR
葡萄牙文(葡萄牙):values-pt-rPT
罗马尼亚文:values-ro-rRO
俄文:values-ru-rRU
斯洛伐克文:values-sk-rSK
斯洛文尼亚文:values-sl-rSI
塞尔维亚文:values-sr-rRS
瑞典文:values-sv-rSE
泰文:values-th-rTH
塔加洛语:values-tl-rPH
土耳其文:values--r-rTR
乌克兰文:values-uk-rUA
越南文:values-vi-rVN
事实上可以通过程序获取JAVA所有支持的语言和国家的,程序代码如下:
import java.util.Locale;
public class Test {
public static void main(String[] args) {
//调用Locale类的getAvailableLocales方法获取,该方法返回一个数组,其中包含JAVA支持的语言和国家
Locale[] localeList = Locale.getAvailableLocales();
//依次获取所支持的国家和语言
for (int i = 0; i < localeList.length; i++) {
System.out.println(localeList[i].getDisplayCountry() + "="
+ localeList[i].getCountry() + ""
+ localeList[i].getDisplayLanguage() + "="
+ localeList[i].getLanguage());
}
}
}
输出结果如下(有些国家可能会使用多种语言):
马来西亚=MY马来文=ms
卡塔尔=QA阿拉伯文=ar
冰岛=IS冰岛文=is
芬兰=FI芬兰文=fi
=波兰文=pl
马耳他=MT英文=en
瑞士=CH意大利文=it
比利时=BE荷兰文=nl
沙特阿拉伯=SA阿拉伯文=ar
伊拉克=IQ阿拉伯文=ar
波多黎哥=PR西班牙文=es
智利=CL西班牙文=es
=芬兰文=fi
奥地利=AT德文=de
=丹麦文=da
英国=GB英文=en
巴拿马=PA西班牙文=es
=塞尔维亚文=sr
也门=YE阿拉伯文=ar
马其顿王国=MK马其顿文=mk
=马其顿文=mk
加拿大=CA英文=en
越南=VN越南文=vi
荷兰=NL荷兰文=nl
美国=US西班牙文=es
中国=CN中文=zh
洪都拉斯=HN西班牙文=es
美国=US英文=en
=法文=fr
=泰文=th
=阿拉伯文=ar
摩洛哥=MA阿拉伯文=ar
=拉托维亚文(列托)=lv
=德文=de
印度尼西亚=ID印度尼西亚文=in
=克罗地亚文=hr
南非=ZA英文=en
韩国=KR朝鲜文=ko
突尼斯=TN阿拉伯文=ar
=印度尼西亚文=in
=日文=ja
塞尔维亚=RS塞尔维亚文=sr
白俄罗斯=BY白俄罗斯文=be
台湾地区=TW中文=zh
苏丹=SD阿拉伯文=ar
=葡萄牙文=pt
=冰岛文=is
日本=JP日文=ja
玻利维亚=BO西班牙文=es
阿尔及利亚=DZ阿拉伯文=ar
=马来文=ms
阿根廷=AR西班牙文=es
阿拉伯联合酋长国=AE阿拉伯文=ar
加拿大=CA法文=fr
=斯洛文尼亚文=sl
=西班牙文=es
立陶宛=LT立陶宛文=lt
黑山=ME塞尔维亚文=sr
叙利亚=SY阿拉伯文=ar
俄罗斯=RU俄文=ru
比利时=BE法文=fr
西班牙=ES西班牙文=es
=保加利亚文=bg
以色列=IL希伯来文=iw
=瑞典文=sv
=英文=en
=希伯来文=iw
丹麦=DK丹麦文=da
哥斯达黎加=CR西班牙文=es
香港=HK中文=zh
=中文=zh
西班牙=ES加泰罗尼亚文=ca
泰国=TH泰文=th
乌克兰=UA乌克兰文=uk
多米尼加共和国=DO西班牙文=es
委内瑞拉=VE西班牙文=es
波兰=PL波兰文=pl
利比亚=LY阿拉伯文=ar
约旦=JO阿拉伯文=ar
=意大利文=it
=乌克兰文=uk
匈牙利=HU匈牙利文=hu
=爱尔兰文=ga
危地马拉=GT西班牙文=es
巴拉圭=PY西班牙文=es
保加利亚=BG保加利亚文=bg
克罗地亚=HR克罗地亚文=hr
波斯尼亚和黑山共和国=BA塞尔维亚文=sr
罗马尼亚=RO罗马尼亚文=ro
卢森堡=LU法文=fr
=挪威文=no
=立陶宛文=lt
新加坡=SG英文=en
厄瓜多尔=EC西班牙文=es
波斯尼亚和黑山共和国=BA塞尔维亚文=sr
尼加拉瓜=NI西班牙文=es
=斯洛伐克文=sk
=俄文=ru
=马耳他文=mt
萨尔瓦多=SV西班牙文=es
=荷兰文=nl
印度=IN印地文=hi
=爱沙尼亚文=et
希腊=GR希腊文=el
斯洛文尼亚=SI斯洛文尼亚文=sl
意大利=IT意大利文=it
日本=JP日文=ja
卢森堡=LU德文=de
瑞士=CH法文=fr
马耳他=MT马耳他文=mt
巴林=BH阿拉伯文=ar
=阿尔巴尼亚文=sq
=越南文=vi
黑山=ME塞尔维亚文=sr
巴西=BR葡萄牙文=pt
挪威=NO挪威文=no
=希腊文=el
瑞士=CH德文=de
新加坡=SG中文=zh
科威特=KW阿拉伯文=ar
埃及=EG阿拉伯文=ar
爱尔兰=IE爱尔兰文=ga
秘鲁=PE西班牙文=es
捷克共和国=CZ捷克文=cs
土耳其=TR土耳其文=tr
=捷克文=cs
乌拉圭=UY西班牙文=es
爱尔兰=IE英文=en
印度=IN英文=en
阿曼=OM阿拉伯文=ar
塞尔维亚及黑山=CS塞尔维亚文=sr
=加泰罗尼亚文=ca
=白俄罗斯文=be
=塞尔维亚文=sr
=朝鲜文=ko
阿尔巴尼亚=AL阿尔巴尼亚文=sq
葡萄牙=PT葡萄牙文=pt
拉脱维亚=LV拉托维亚文(列托)=lv
塞尔维亚=RS塞尔维亚文=sr
斯洛伐克=SK斯洛伐克文=sk
墨西哥=MX西班牙文=es
澳大利亚=AU英文=en
挪威=NO挪威文=no
新西兰=NZ英文=en
瑞典=SE瑞典文=sv
=罗马尼亚文=ro
黎巴嫩=LB阿拉伯文=ar
德国=DE德文=de
泰国=TH泰文=th
=土耳其文=tr
哥伦比亚=CO西班牙文=es
菲律宾=PH英文=en
爱沙尼亚=EE爱沙尼亚文=et
塞浦路斯=CY希腊文=el
=匈牙利文=hu
法国=FR法文=fr
在做项目时,我遇到这样一种情况,字符串资源需要JAVA代码来控制,而不是在XML文件中获取,下面提供该方法:
创建ConstantBase类来定义你需要的字符串,如下:
package com.example.l10ntest;
public class ConstantBase {
public String PUT_NAME;
public String SAY_LOVE;
public String NEXT;
public String Second_Page;
}
中英文的字符串类分别命名如下,中文:
package com.example.l10ntest;
public class ConstantCH extends ConstantBase {
public ConstantCH() {
//此处定义需要的中文字符串
PUT_NAME = "请输入你的名字。";
SAY_LOVE = "我爱你";
NEXT = "下一页";
Second_Page = "第二页";
}
}
英文:
package com.example.l10ntest;
public class ConstantEN extends ConstantBase {
public ConstantEN() {
// 此处定义需要的英文字符串
PUT_NAME = "Please put your name.";
SAY_LOVE = "I love you";
NEXT = "next";
Second_Page = "The second page";
}
}
我在新创建的Constant类中写了本地语言的判断方法,该类如下:
package com.example.l10ntest;
import java.util.Locale;
public class Constant {
public static ConstantBase string = produceConstant();
public static ConstantBase produceConstant() {
if (isLocalLanguageZH()) {
string = new ConstantCH();
} else {
string = new ConstantEN();
}
return string;
}
//判断本地语言是否是中文
public static boolean isLocalLanguageZH() {
if (Locale.getDefault().getLanguage().contains("zh")
&& Locale.getDefault().getCountry().contains("CN")) {
return true;
}
return false;
}
}
主类的XML文件做一下修改(暂时将ImageView屏蔽掉,后面增加了一个Button),如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical" >
<!-- <ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/country" /> -->
<!-- tools:ignore="ContentDescription",屏蔽掉对View设置备注说明的提示 -->
<TextView
android:id="@+id/putName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@null"
android:textSize="20sp" />
<TextView
android:id="@+id/iloveyou"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@null"
android:textSize="20sp" />
<Button
android:id="@+id/next"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
在主类中,写如下代码:
((TextView) findViewById(R.id.putName))
.setText(Constant.string.PUT_NAME);
((TextView) findViewById(R.id.iloveyou))
.setText(Constant.string.SAY_LOVE);
这样便可以通过JAVA代码来控制字符串的获取了。但是发现一个问题,就是当程序切换到后台时,你将手机终端的语言修改,比如当前中文修改为英文,再从后台将该程序拉起时,显示的文本信息并不会随着变换,如果以前是中文,修改系统语言后还是中文,国际化似乎不生效。最后发现只有将该程序进程杀死,再次启动,国际化才会生效。但是每次切换一下终端语言就人为杀进程重启应用,显然是不合理的。
事实上当locale信息改变之后,系统会发广播消息Intent.ACTION_LOCALE_CHANGED,我们可以写接收该广播的代码,并做一些操作。
不妨多写几个Activity页面做测试,我写了两个,MainActivity经过修改的XML前面已经给出了,MainActivity如下:
package com.example.l10ntest;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((TextView) findViewById(R.id.putName))
.setText(Constant.string.PUT_NAME);
((TextView) findViewById(R.id.iloveyou))
.setText(Constant.string.SAY_LOVE);
Button btn = (Button) findViewById(R.id.next);
btn.setText(Constant.string.NEXT);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this,SecondActivity.class));
}
});
}
}
第二个Activity(SecondActivity)我没有写XML布局,代码如下:
package com.example.l10ntest;
import android.os.Bundle;
import android.view.Gravity;
import android.widget.TextView;
public class SecondActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView textView = new TextView(this);
textView.setGravity(Gravity.CENTER);
textView.setText(Constant.string.Second_Page);
textView.setTextSize(30);
setContentView(textView);
}
}
事实上一个应用一般不会只有一个Activity的,我将两个Activity都继承了BaseActivity,在BaseActivity中对这两个Activity做统一管理,BaseActivity如下:
package com.example.l10ntest;
import android.app.Activity;
import android.os.Bundle;
public class BaseActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Utils.addToList(this);
}
}
只调用了Utils类里的一个方法,在Activity创建后,将它添加到List里面。Utils类代码如下:
package com.example.l10ntest;
import java.util.ArrayList;
import java.util.Iterator;
import android.app.Activity;
import android.util.Log;
public class Utils {
private static String TAG = "Utils";
// 创建存放Activity的集合
private static ArrayList<Activity> activityList = new ArrayList<Activity>();
public static void addToList(Activity activity) {
Log.i(TAG, "activity name:" + activity.getClass().getName());
activityList.add(activity);
}
public static void stopApp() {
Iterator<Activity> itr = activityList.iterator();
Activity a = null;
while (itr.hasNext()) {
a = itr.next();
a.finish();
}
Log.i(TAG, "stopApp end.");
System.exit(0);
}
}
这里面有两个方法,一个就是添加Activity做统一管理,第二个是停止程序的方法,先将List中的Activity依次杀死,再杀死进程退出程序。
下面就是比较重要的接收系统广播的类了,代码如下:
package com.example.l10ntest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class AppExitReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent == null || intent.getAction() == null)
return;
if (intent.getAction().equals(Intent.ACTION_LOCALE_CHANGED)) {
Utils.stopApp();
}
}
}
AndroidManifest.xml文件如下,配置了一个广播接收。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.l10ntest"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.l10ntest.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.example.l10ntest.SecondActivity"
android:label="@string/app_name" >
</activity>
<receiver android:name="com.example.l10ntest.AppExitReceiver" >
<intent-filter>
<action android:name="android.intent.action.LOCALE_CHANGED" />
</intent-filter>
</receiver>
</application>
</manifest>
整理一下思路:
写了两个Activity,这两个Activity都用到了文本资源,如“((TextView) findViewById(R.id.iloveyou)).setText(Constant.string.SAY_LOVE);”和“textView.setText(Constant.string.Second_Page);”MainActivity中增加了跳转到SecondActivity的按钮。两个Activity都继承了BaseActivity,当它们创建后,会通过BaseActivity中的“Utils.addToList(this)”将它们依次添加到Utils类中的activityList里。
在终端切换系统语言,发送Intent.ACTION_LOCALE_CHANGED广播后,我们创建AppExitReceiver类来接收该广播,并调用“Utils.stopApp()“来将创建的Activity依次关闭,并且退出应用程序,这样不管该程序从后台切回来还是点击该程序的图标,程序都会重新启动,显示的文本信息会随着系统语言的变换而变换。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。