QML程序实现动态切换多语言

原文地址::https://zhuanlan.zhihu.com/p/40815590

 

Qt程序中实现多语言有Qt自己的一套机制,然而目前在5.9版本下该机制无法在程序运行期间动态切换语言。本文向大家介绍一种切实可行的walkaround方法,支持对普通文本以及在ListModel/ListElement中的文本的多语言处理。

回顾Qt多语言机制

Qt自身的多语言机制分下面几个步骤:

  1. 在源码中用QObject::tr()(C++)或者qsTr()(QML)将字符串包起来;
  2. 用Qt自带的lupdate生成.ts文件;
  3. 用linguist工具翻译并发布为.qm文件;
  4. 程序刚启动还未加载任何界面元素之前,根据某些规则加载对应的.qm文件即可实现显示对应的语言的效果。

之所以该机制不支持动态加载语言就在于第四步。该步骤核心代码是:

QTranslator translator;
translator.load("zh.qm");
app.installTranslator(&translator);

installTranslator函数执行完并不会重新加载或者刷新界面文本,所以新的多语言并不会起作用。 我们的解决办法也是设法能将改变程序语言的事件通知到界面。

动态加载处理方法

我们已经知道之所以界面文本没刷新是因为更换语言的事件没有通知到界面上各个qsTr()

解决办法首先是创建一个全局变量并加上:

  1. 一个属性,值是空的字符串,但是通过发送changed信号可以让和它绑定的属性(即我们的各种多语言文本)reevaluate一下,从而能够再次qsTr()获得新语言下的文本。
  2. 一个信号,用于程序中需要切换语言时发送。

例如我的程序中都有个AppGlobal的全局变量:

class AppGlobal : public QObject{
    Q_OBJECT
    QString m_langToken = "";
public:
    Q_PROPERTY(QString langToken READ langToken NOTIFY langTokenChanged)
    QString langToken() const{ return m_langToken;}
signals:
    void langTokenChanged(QString langToken);
    void changeLang(QString lang);
};

其中langToken就是触发reevaluate的属性,而changeLang则是切换语言时发送的信号。

一般逻辑是:

  1. 编写程序的时候,所有需要支持多语言的文本末尾都加上langToken,例如原来是qsTr("Hello World"),现在是 qsTr("Hello World") + $app.langToken;
  2. 用户通过选择语言触发changeLang信号,参数带上具体选择的语言;
  3. 事先连接的槽函数开始响应执行, 用QTranslator加载对应的.qm文件,然后 installTranslator装载;
  4. 最后发送langTokenChanged信号,触发所有和langToken属性绑定的文本刷新。

第3步的槽函数可以在任何C++部分编写连接,例如我们在main.cpp里写:

QGuiApplication app(argc, argv);

auto appGlobal = new AppGlobal;

QTranslator* translator = nullptr;
QObject::connect(appGlobal, &AppGlobal::changeLang, &app, [&app, translator](QString lang){
    if(translator)
        app.removeTranslator(translator);
    
    translator = new QTranslator(&app);
    if(lang == "en")
        translator->load("en.qm");
    else
        translator->load("zh.qm");
    
    app.installTranslator(translator);
    emit appGlobal->langTokenChanged(lang);
});

最后,在合适的位置触发更换语言的信号,例如我们在一个按钮下做下面的处理:

Button{
    id: chineseButton
    text: "中文"
    onClicked: $app.changeLang("zh");
}

该方法巧妙之处在于它不会改变界面的文本本身(因为langToken是个空字符串),但又会引起qsTr()重新执行(因为属性绑定、reevaluate机制)。

ListModel/ListElement多语言问题解决

用过ListModel的朋友应该都知道,ListElement的字段是无法进行属性绑定的。也就是说,它里面的文本无法用我们上面的机制进行刷新。那怎么办呢?Qt为我们准备了一个宏:QT_TR_NOOP,它可以解决这个问题。

首先在定义ListModel的时候,需要多语言支持的文本一律用该宏包裹:

ListModel{
    id: listModel
    ListElement{
        text: QT_TR_NOOP("First")
        isValid: true
    }
    ListElement{
        text: QT_TR_NOOP("Second")
        isValid: false
    }
    ListElement{
        text: QT_TR_NOOP("Third")
        isValid: true
    }
}

然后我们在使用该ListModeldelegate里,使用我们上面的绑定触发机制:

ListView{
    model: listModel
    delegate: Text{
        text: qsTr(text) + $app.langToken
        enabled: isValid
    }
}

这样,当$app.changeLang信号发出后,ListView的多语言也能够得到刷新。

总结

本文给大家介绍了如何克服Qt无法动态刷新多语言问题的方法。笔者的环境是Qt5.9.6。注意,在Qt5.10之后,qsTr()已经能够自动响应installTranslator事件了,也就是我们上面的langToken属性没必要加了。但是ListModel中的QT_TR_NOOP还是需要的。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值