在《基于Modern工具包的本地化方式》一文中实现的本地化方式忽略了在切换语言后,原始的文本值已经改变,要想再切换回去,由于找不到对应的本地化值,最终切换不了,因而,必须在第一次切换的时候记录下原始文本值,这样才能保证每次切换的时候都能找到对应值。
在前文中还有一个bug是当本地化先于组件完成初始化时,就不会监听localizedready
事件。
本文还添加了语言切换按钮,用于切换语言。
以下是修正后的代码:Localized.js
:
Ext.define('CommonShared.service.Localized', {
alternateClassName: 'LocalizedService',
singleton: true,
config:{
currentLanguage: null
},
requires:[
'CommonShared.util.Url',
'CommonShared.service.OAuth',
],
isReady: false,
constructor(config){
const me = this;
me.initConfig(config)
me.initLanguages();
me.loadResources();
},
initLanguages(){
const me = this;
let current = StorageService.get('lang');
if(current) return;
current = AppConfig.lang === 'zh-CN' ? 'zh-Hans'
: AppConfig.lang === 'zh-TW' ? 'zh-Hant' : AppConfig.lang;
me.setCurrentLanguage(current);
StorageService.set('lang', current);
},
loadResources(){
const me= this;
me.isReady = false;
Ext.Ajax.request({
url: URI.get('Configuration', 'localization'),
headers: AuthService.getAuthorizationHeader(),
scope: me
}).then(me.loadSuccess, me.loadFailure, null, me);
},
loadSuccess(response){
const me = this,
obj = Ext.decode(response.responseText,true);
if(obj){
me.remoteRawValue = {};
Object.assign(me.remoteRawValue, obj);
me.doOverride();
}
me.isReady = true;
Ext.fireEvent('localizedready', me);
},
loadFailure(response){
let obj = Ext.decode(response.responseText, true),
error = 'Unknown Error!';
if(obj && obj.error) error = obj.error;
Ext.Msg.alert('Error', error);
},
get(key, resourceName){
const me = this,
defaultResourceName = me.remoteRawValue.defaultResourceName,
values = me.remoteRawValue.values;
return resourceName && values[resourceName] && values[resourceName][key]
|| ( values['ExtResource'] && values['ExtResource'][key] )
|| ( values[defaultResourceName] && values[defaultResourceName][key] )
|| key;
},
getLanguages(){
return this.remoteRawValue.languages;
},
getCurrentCulture(){
return this.remoteRawValue.currentCulture;
},
switchLanguages(value){
const me = this,
current = me.getCurrentLanguage();
if(current === value) return;
me.setCurrentLanguage(value);
StorageService.set('lang', value);
me.loadResources();
},
localized(cls, name, resourceName){
name = Ext.String.capitalize(name);
const originLocalized = cls.getOriginLocalized(),
get = cls[`get${name}`],
set = cls[`set${name}`];
let value = originLocalized[name];
//有原始值的,使用原始值返回
if(value){
if(set){
set.apply(cls, [LocalizedService.get(value,resourceName)]);
return;
}
return LocalizedService.get(value,resourceName);
}
//没有原始值的处理
if(!get || !set) {
//没有set或get方法的,直接返回
value = name;
originLocalized[name] = value;
return LocalizedService.get(value,resourceName);
};
value = get.apply(cls);
originLocalized[name] = value;
if(!value) return;
set.apply(cls, [LocalizedService.get(value,resourceName)]);
},
privates:{
remoteRawValue: {},
doOverride(){
const me = this,
values = me.remoteRawValue.values.ExtResource,
newMonthNames = [],
newDayNames = [],
am = values['AM'] || 'AM',
pm = values['PM'] || 'PM';
if(!Ext.Date.originMonthNames)Ext.Date.originMonthNames = [].concat(Ext.Date.monthNames);
Ext.Date.originMonthNames.forEach(month=>{
newMonthNames.push(values[month] || month);
});
Ext.Date.monthNames = newMonthNames;
if(!Ext.Date.originDayNames)Ext.Date.originDayNames = [].concat(Ext.Date.dayNames);
Ext.Date.originDayNames.forEach(day=>{
newDayNames.push(values[day] || day);
});
Ext.Date.dayNames = newDayNames;
//console.log(Ext.Date)
Ext.Date.formatCodes.a = `(this.getHours() < 12 ? '${am}' : '${pm}')`;
Ext.Date.formatCodes.A = `(this.getHours() < 12 ? '${am}' : '${pm}')`;
const parseCodes = {
g: 1,
c: "if (/(" + am + ")/i.test(results[{0}])) {\n" +
"if (!h || h == 12) { h = 0; }\n" +
"} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
s: `(${am}|${pm})`,
calcAtEnd: true
};
Ext.Date.parseCodes.a = Ext.Date.parseCodes.A = parseCodes;
},
}
})
Component.js
:
Ext.define('CommonOverrides.shared.Component',{
override: 'Ext.Component',
config:{
resourceName: null,
localized: [],
originLocalized: {}
},
initialize(){
const me = this;
me.callParent(arguments);
if(LocalizedService && LocalizedService.isReady){
me.onLocalized();
}
Ext.on('localizedready', me.onLocalized, me);
},
onLocalized(){
const me = this,
xtype = me.xtype,
resourceName = me.getResourceName(),
service = LocalizedService,
localized = me.getLocalized();
if(me.isButton || me.isMenuItem) {
service.localized(me, 'text');
return;
}
if(xtype === "loadmask"){
service.localized(me, 'message');
return;
};
if(me.isField){
service.localized(me,'requiredMessage');
service.localized(me,'validationMessage');
service.localized(me,'label', resourceName);
if(me.getPlaceholder) service.localized(me, 'placeholder', resourceName);
if(me.getBoxLabel) service.localized(me ,'boxLabel', resourceName);
if(xtype === 'datefield' || xtype === 'DatePicker'){
service.localized(me,'minDateMessage');
service.localized(me,'maxDateMessage');
}
if(xtype === 'numberfield'){
me.decimalsText = service.localized(me, 'decimalsText');
me.minValueText = service.localized(me, 'minValueText');
me.maxValueText = service.localized(me, 'maxValueText');
me.badFormatMessage = service.localized(me, 'badFormatMessage');
}
if(xtype === 'textfield'){
me.badFormatMessage = service.localized(me, 'badFormatMessage');
}
me.setError(null);
return;
}
if(me.isContainer){
if(me.isPanelTitle || me.isPanel) {
service.localized(me , 'title', resourceName);
if(xtype === 'tooltip'){
service.localized(me , 'html', resourceName);
}
const collapsible = me.getCollapsible && me.getCollapsible();
if(collapsible){
service.localized(collapsible, 'collapseToolText');
service.localized(collapsible, 'expandToolText');
}
me.doCustomLocalized(me, localized, resourceName);
return;
};
if(me.isGridColumn){
service.localized(me ,'text', resourceName);
return;
}
if(xtype === 'datepanel'){
service.localized(me, 'nextText');
service.localized(me, 'prevText');
return;
}
if(me.isDataView){
service.localized(me, 'loadingText');
service.localized(me, 'emptyText');
return;
}
// if(xtype === 'lockedgridregion'){
// service.localized(me, 'menuLabel');
// return;
// }
}
me.doCustomLocalized(me, localized, resourceName);
},
doCustomLocalized(me, localized, resourceName){
localized.forEach(key=>{
LocalizedService.localized(me, key, resourceName)
});
}
})
Abstract.js
:
Ext.define('CommonOverrides.shared.plugin.Abstract',{
override: 'Ext.plugin.Abstract',
config:{
originLocalized: {}
},
constructor: function(config) {
const me = this;
if (config) {
me.cmp = config.cmp;
me.pluginConfig = config;
me.initConfig(config);
}
if(LocalizedService && LocalizedService.isReady){
me.onLocalized();
}
Ext.on('localizedready', me.onLocalized, me);
},
onLocalized(){
const me = this,
type = me.type;
if(type === 'gridrowdragdrop'){
me.dragText = LocalizedService.localized(me , 'dragText');
return
};
if(type === 'listpaging'){
LocalizedService.localized(me , 'loadMoreText');
LocalizedService.localized(me , 'noMoreRecordsText');
}
},
});
Validator.js
:
Ext.define('CommonOverrides.shared.data.validator.Validator',{
override: 'Ext.data.validator.Validator',
config:{
originLocalized: {}
},
constructor: function(config) {
const me = this;
if (typeof config === 'function') {
me.fnOnly = true;
me.validate = config;
}
else {
me.initConfig(config);
}
if(LocalizedService && LocalizedService.isReady){
me.onLocalized();
}
Ext.on('localizedready', me.onLocalized, me);
},
onLocalized(){
const me = this,
type = me.type,
resourceName = 'ExtResource';
if(type === 'bound' || type === 'length' || type === 'range'){
if(type === 'bound') LocalizedService.localized(me ,'emptyMessage',resourceName);
if(type === 'range') LocalizedService.localized(me ,'nanMessage',resourceName);
LocalizedService.localized(me ,'minOnlyMessage', resourceName);
LocalizedService.localized(me ,'maxOnlyMessage', resourceName);
LocalizedService.localized(me ,'bothMessage', resourceName);
return;
}
LocalizedService.localized(me ,'message', resourceName);
},
})
语言切换按钮的代码:Language.js
Ext.define('CommonShared.ux.button.Language',{
extend: 'Ext.Button',
xtype: 'languagebutton',
ui: 'action',
onLocalized(){
const me = this,
originLocalized = me.getOriginLocalized(),
current = LocalizedService.getCurrentCulture(),
displayName = current.cultureName.includes('zh') ? current.displayName: current.englishName;
originLocalized['Text'] = displayName;
me.setText(displayName);
me.callParent();
if(me.getMenu()) return;
const menus = [];
LocalizedService.getLanguages().forEach(l=>{
menus.push({
value: l.cultureName ,
text: l.displayName,
handler: me.onSwitchLanguage,
});
});
me.setMenu(menus);
},
onSwitchLanguage(sender){
LocalizedService.switchLanguages(sender.value);
}
})
采用这种本地化方式的一个麻烦是,要修改按钮的文本显示,必须修改原始值,不然显示的会是原始值,代码中的originLocalized['Text'] = displayName;
就是用来修改原始值的,或者再想想有没有更好的办法。