国际化
支持不同语言,在不同的国家都可以使用。
本文将会介绍如下节点:
- 实现 string 资源的国际化
- 简繁体切换实现
- 在现有业务上,做国际化遇到的一些问题
string 资源的国际化实现
在 Android 开发规范中,我们一般不建议在代码或者布局文件中 hard code 我们 string 字符串,而是需要将这些字符串放在单独的 string.xml 文件。
- 错误示例
- 正确的姿势
2.实现步骤
2.1 创建语言区域目录
在 res/ 文件中创建对应国家或者地区资源文件,具体如下
- 创建语言区域目录
res/value-ISO语言代码
- res/value 默认的语言目录
- res/value-zh 表示中国
- res/value-zh-rHK 表示中国香港
- res/value-zh-rTW 表示中国台湾
- res/value-zh-rMO 表示中国澳门
- res/value-es 表示美国
- …
本文演示的是中文简体和繁体的国际化,所以下面展示的是中国台湾,香港繁体和中文简体。
2.2 创建语言区域目录对应的字符串文件
默认的 res/value/strings.xml
<resources>
<string name="text">纸短情长-默认</string>
</resources>
res/value-zh-rHK/strings.xml
<resources>
<string name="text">紙短情長-HK</string>
</resources>
res/value-zh-rTW/strings.xml
<resources>
<string name="text">紙短情長-TW</string>
</resources>
2.3 应用资源文件
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
2.4 测试
在这里调节不同的语言环境就可以看到加载不同的资源文件了。
3. 简繁体切换实现
本地的语言环境是简体,但是我们在需要在应用动态读取繁体资源。这种操作意味着去更新应用的 Configuration 配置信息。
3.1 操作步骤
- 获取一个需要转化的 Locale 对象
- 读取 Configuration 配置信息
- 修改locale为目标locale
- 更新configuration
- 重启Activity
**注意:**当修改 Configuration 之后,需要调用Activity#recreate() 方法重新创建 activity 。
3.2 简体转繁体
/**
* 简体转繁体
*
* @param view
*/
public void s2t(View view) {
// 本地语言设置
// 获取一个需要转化的 Locale 对象
Locale myLocale = Locale.TAIWAN;
Resources res = getResources();
DisplayMetrics dm = res.getDisplayMetrics();
//读取 Configuration 配置信息
Configuration conf = res.getConfiguration();
//修改locale为目标locale
conf.locale = myLocale;
//更新configuration
res.updateConfiguration(conf, dm);
//重启Activity
recreate();
}
3.3 繁体转简体
/**
* 繁体转简体
*
* @param view
*/
public void t2s(View view) {
// 本地语言设置
Locale myLocale = Locale.CHINESE;
Resources res = getResources();
DisplayMetrics dm = res.getDisplayMetrics();
Configuration conf = res.getConfiguration();
conf.locale = myLocale;
res.updateConfiguration(conf, dm);
//重启Activity
recreate();
}
4. 在现有业务上,做国际化遇到的一些问题
一般的应用在开发初期只会针对国内用户进行开发,也就是一般不会去做国际化,当我们应用发展到一定的规模之后就需要往海外市场拓展。
下面就会有一个这样需求,就是在当前的简体中文版基础上开发一个繁体中文版。需要国际化,就需要解决以下几个问题:
- 服务端接口是简体中文。
- …
4.1 服务端接口国际化
因为时间比较赶,无法让服务端去修改接口,所以只能在客户端获取到接口数据之后,在进行简繁体转换。
因为项目使用是 OKHTTP 网络请求框架,因此我们使用拦截器的方式拦截返回的响应 Response 对象,修改返回内容来实现接口数据国际化。
- 定义拦截器
在这里定义的拦截器中是 Application Interceptor 拦截器。放在 OKHTTP 拦截器自带拦截器之前,也就是说在这个拦截器得到的响应就是网络请求得到数据的响应,因此在这里修改响应内容即可。
注意在读取 final String content = response.body().string(); 之后,对应的流会被关闭,那么这时上层获取到 Response 之后,在调用 body().string()就会报错,因此我们在读取数据,并转化为繁体之后,需要构造一个新的 ResponseBody 给 Response 即可。
public class Simple2TraditionChineseInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
if (response.body() != null && response.body().contentType() != null) {
final MediaType mediaType = response.body().contentType();
final String content = response.body().string();
String convertResult = content;
JChineseConvertor jChineseConvertor = JChineseConvertor
.getInstance();
//将简体数据转化为繁体
try {
final String convertToTraditionalChinese = jChineseConvertor.s2t(content);
if (!TextUtils.isEmpty(convertToTraditionalChinese)) {
convertResult = convertToTraditionalChinese;
}
} catch (Exception e) {
//当转化异常时,使用默认返回的数据
Logger.e("接口返回数据转化失败");
e.printStackTrace();
}
//构造一个新的ResponseBody,因为 response.body().string() 调用之后会把流关闭,因此这里要新建。
ResponseBody responseBody = ResponseBody.create(mediaType, convertResult);
response = response.newBuilder().body(responseBody).build();
}
return response;
}
}
- 配置拦截器
public static OkHttpClient getClient() {
if (client == null) {
synchronized (HttpUtils.class){
if (client==null) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
builder.readTimeout(READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
builder.writeTimeout(WRITE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
builder.addInterceptor(new ReportInterceptor()); // 添加上报接口性能
//判断是否对接口进行简繁体转换
if (LiveCore.isShowTraditionalChinese()) {
builder.addInterceptor(new Simple2TraditionChineseInterceptor()); // 国际化
}
client = builder.build();
}
}
}
return client;
}