6.4 国际化之多语言

多语言是国际化中大家最常面对的问题,我们需要对应用的页面结构元素进行翻译,也需要对系统内容进行翻译比如:菜单、数据字典等,甚至还会业务数据进行翻译。但不管什么翻译需求,我们在实现上基本可以归类为前端翻译和后端翻译。前端翻译顾名思义是在前端根据用户选择语言对内容进行翻译,反之就是后端翻译。本文会带着大家了解oinone的前端翻译与后端翻译

准备工作

pamirs-demo-boot的pom文件中引入pamirs-translate包依赖

<dependency>
    <groupId>pro.shushi.pamirs.core</groupId>
    <artifactId>pamirs-translate</artifactId>
</dependency>

pamirs-demo-boot的application-dev.yml文件中增加配置pamirs.boot.modules增加translate,即在启动模块中增加translate模块

pamirs:
    boot:
    modules:
        - translate

后端翻译(使用)

这里通过对菜单的翻译来带大家了解翻译模块

Step1 新增翻译记录

切换应用到translate模块,点击新增翻译。

  1. 选择新增翻译生效模块

  2. 选择翻译的模型为:菜单模型

  3. 源语言选择中文,目标选择English

  4. 添加翻译项目:

    1. 源术语为:商店

    2. 翻译值为:shop

    3. 状态为:激活

image.png

Step2 查看效果

应用切换到Demo模块,在右上角切换语言至英语

image.png

后端翻译(自定义模型的翻译)

在前面菜单的翻译中,似乎我们什么都没做就可以正常通过翻译模块完成多语言的切换了。是不是真如我们想象的一样,当然不是。是因为Menu模型的displayName字段加上@Field(translate = true)注解。

Step1 为PetType模型的name字段增加翻译注解

package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.base.IdModel;

@Model.MultiTable(typeField = "kind")
@Model.model(PetType.MODEL_MODEL)
@Model(displayName="品种",labelFields = {"name"})
public class PetType extends IdModel {

    public static final String MODEL_MODEL="demo.PetType";

    @Field(displayName = "品种名" , translate = true)
    private String name;

    @Field(displayName = "宠物分类")
    private String kind;
}

Step2 重启应用查看效果

  1. 切换应用到translate模块,点击新增翻译

image.png

  1. 切换应用到Demo模块,切换中英文,查看效果

image.png

前端翻译

还记得我们前端第一个自定义动作吗?会弹出“oinone第一个自定义Action,啥也没干”,我们要对它进行翻译。

Step1 修改前端DoNothingActionWidget.ts

  1. import translateValueByKey

  2. 提示语用translateValueByKey加上翻译 const confirmRs = executeConfirm(translateValueByKey(\'oinone第一个自定义Action,啥也没干\')||\'oinone第一个自定义Action,啥也没干\');

    前端更多翻译工具请见前端高级特性-框架之翻译工具

import {
  Action,
  ActionContextType,
  ActionWidget,
  executeConfirm,
  IClientAction,
  SPI,
  ViewType,
  Widget,
  translateValueByKey
} from '@kunlun/dependencies';

@SPI.ClassFactory(ActionWidget.Token({ name: 'demo.doNothing' }))
export class DoNothingActionWidget extends ActionWidget {
  @Widget.Method()
  public async clickAction() {
        const confirmRs =  executeConfirm(translateValueByKey('oinone第一个自定义Action,啥也没干')||'oinone第一个自定义Action,啥也没干');
    }
}

//定义动作元数据
Action.registerAction('*', {
  displayName: '啥也没干',
  name: 'demo.doNothing',
  id: 'demo.doNothing',
  contextType: ActionContextType.ContextFree,
  bindingType: [ViewType.Table]
} as IClientAction);

Step2 新增翻译记录

前端翻译的翻译记录对应的模型可以随意找一个放。但要注意几点:

  1. 不要找有字读配置translate = true的模型,因为会影响后端翻译性能。

  2. 最好统一到一个模型中,便于后续管理。这里大家可以自定义一个无有业务访问且本身无需要翻译的模型来挂载,避免性能损失

image.png

Step3 刷新远程资源生成前端语言文件

image.png

Step4 新增或修改.env

前端在项目根目录下新增或修改.env,可以参考.env.example文件。通过.env文件为前端配置oss文件路径,针对I18N_OSS_URL配置项。真实前端访问翻译语言文件的路径规则为:http://bucket.downloadUrl/mainDir/租户/translate/模块/语言文件

  1. yaml文件中oss配置的文件路径:http://pamirs.oss-cn-hangzhou.aliyuncs.com/upload/demo/

  2. 租户/translate/模块/语言文件 前端会自动根据上下文组织

# 后端api配置
# API_BASE_URL=http://127.0.0.1:8090
# 下面是国际化语言的cdn配置,默认用当前请求链接下的路径: /pamirs/translate/${module}/i18n_${lang}.js
I18N_OSS_URL=http://pamirs.oss-cn-hangzhou.aliyuncs.com/upload/demo

Step5 重启前端应用看效果

对语言进行中英文切换,进入宠狗达人页面,点击【第一个自定义Action】,查看前端翻译效果

image.png

image.png

Oinone社区 作者:史, 昂原创文章,如若转载,请注明出处:https://doc.oinone.top/oio4/9328.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

(0)
史, 昂的头像史, 昂数式管理员
上一篇 2024年5月23日 am8:10
下一篇 2024年5月23日 am8:12

相关推荐

  • 4.2.3 框架之SPI机制

    SPI(Service Provider Interface)服务提供接口,是一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件,简单来说就是用来解耦,实现组件的自由插拔,这样我们就能在平台提供的基础组件外扩展新组件或覆盖平台组件。 目前定义SPI组件 ViewWidget 视图组件 FieldWidget 字段组件 ActionWidget 动作组件 表4-2-3-1 目前定义SPI组件 前提知识点 使用 TypeScript 装饰器(注解)装饰你的代码 1. 通过注解定义一种SPI接口(Interface) @SPI.Base<IViewFilterOptions, IView>('View', ['id', 'name', 'type', 'model', 'widget']) export abstract class ViewWidget<ViewData = any> extends DslNodeWidget { } 图4-2-3-1 代码示意 2. 通过注解注册提供View类型接口的一个或多个实现 @SPI.Base<IViewFilterOptions, IView>('View', ['id', 'name', 'type', 'model', 'widget']) export abstract class ViewWidget<ViewData = any> extends DslNodeWidget { } 图4-2-3-2 代码示意 3. 视图的xml内通过配置来调用已定义的一种SPI组件 <view widget="form" model="demo.shop"> <field name="id" /> </view> 图4-2-3-3 代码示意 图4-2-3-4 组件继承示意图 当有多个服务提供方时,按以下规则匹配出最符合条件的服务提供方 SPI匹配规则 SPI组件没有严格的按匹配选项属性限定,而是一个匹配规则 按widget最优先匹配,配置了widget等于是指定了需要调用哪个widget,此时其他属性直接忽略 按最大匹配原则(匹配到的属性越多优先级越高) 按后注册优先原则

    2024年5月23日
    1.3K00
  • 4.1.25 框架之搜索引擎

    一、使用场景 在碰到大数据量并且需要全文检索的场景,我们在分布式架构中基本会架设ElasticSearch来作为一个常规解决方案。在oinone体系中增强模型就是应对这类场景,其背后也是整合了ElasticSearch。 二、整体介绍 oinone与es整合设计图 图4-1-25-1 Oinone与es整合设计图 基础环境安装 Canal安装 详见4.1.10【函数之触发与定时】一文 修改Canal配置并重启 新增Canal的实例【destinaion: pamirs】,监听分表模型的binlog【filter: demo.demo_core_sharding_model……】用于增量同步 pamirs: middleware: data-source: jdbc-url: jdbc:mysql://localhost:3306/canal_tsdb?useUnicode=true&characterEncoding=utf-8&verifyServerCertificate=false&useSSL=false&requireSSL=false driver-class-name: com.mysql.cj.jdbc.Driver username: root password: oinone canal: ip: 127.0.0.1 port: 1111 metricsPort: 1112 zkClusters: – 127.0.0.1:2181 destinations: – destinaion: pamirschangedata name: pamirschangedata desc: pamirschangedata slaveId: 1235 filter: demo\.demo_core_pet_talent dbUserName: root dbPassword: oinone memoryStorageBufferSize: 65536 topic: CHANGE_DATA_EVENT_TOPIC dynamicTopic: false dbs: – { address: 127.0.0.1, port: 3306 } – destinaion: pamirs id: 1234 name: pamirs desc: pamirs slaveId: 1234 filter: demo\.demo_core_sharding_model_0,demo\.demo_core_sharding_model_1,demo\.demo_core_sharding_model_2,demo\.demo_core_sharding_model_3,demo\.demo_core_sharding_model_4,demo\.demo_core_sharding_model_5,demo\.demo_core_sharding_model_6,demo\.demo_core_sharding_model_7 dbUserName: root dbPassword: oinone memoryStorageBufferSize: 65536 topic: BINLOG_EVENT_TOPIC dynamicTopic: false dbs: – { address: 127.0.0.1, port: 3306 } tsdb: enable: true jdbcUrl: "jdbc:mysql://127.0.0.1:3306/canal_tsdb" userName: root password: oinone mq: rocketmq rocketmq: namesrv: 127.0.0.1:9876 retryTimesWhenSendFailed: 5 dubbo: application: name: canal-server version: 1.0.0 registry: address: zookeeper://127.0.0.1:2181 protocol: name: dubbo port: 20881 scan: base-packages: pro.shushi server: address: 0.0.0.0 port: 10010 sessionTimeout: 3600 图4-1-25-2 修改Canal配置并重启 ES安装 下载安装包官方下载地址,也可以直接下载elasticsearch-8.4.1-darwin-x86_64.tar.gz.txt(361.7 MB),下载后去除后缀.txt,然后解压文件 替换安装目录/config下的[elasticsearch.yml](elasticsearch)(1 KB),主要是文件中追加了三个配置 xpack.security.enabled: false xpack.security.http.ssl.enabled: false xpack.security.transport.ssl.enabled: false 图4-1-25-3 elasticsearc.yml追加三个配置 启动 a. 导入环境变量(ES运行时需要JDK18及以上版本JDK运行环境, ES安装包中包含了一个JDK18版本) # export JAVA_HOME=/Users/oinone/Documents/oinone/es/elasticsearch-8.4.1/jdk.app/Contents/Home/ export JAVA_HOME=ES解压安装目录/jdk.app/Contents/Home/ 图4-1-25-4 导入环境变量 b. 运行ES ## nohup /Users/oinone/Documents/oinone/es/elasticsearch-8.4.1/bin/elasticsearch >> $TMPDIR/elastic.log 2>&1 & nohup ES安装目录/bin/elasticsearch >> $TMPDIR/elastic.log 2>&1 & 图4-1-25-5 运行ES 停止ES lsof…

    2024年5月23日
    1.3K00
  • 4.1.21 框架之分布式消息

    消息中间件是在分布式开发中常见的一种技术手段,用于模块间的解耦、异步处理、数据最终一致等场景。 一、介绍 oinone对开源的RocketMQ进行了封装,是平台提供的一种较为简单的使用方式,并非是对RocketMQ进行的功能扩展。同时也伴随着两个非常至关重要的目的: 适配不同企业对RocketMQ的不同版本选择,不至于改上层业务代码。目前已经适配RocketMQ的开源版本和阿里云版本。 下个版本会对API进行升级支持不同类型MQ,以适配不同企业对MQ的不同要求,应对一些企业客户已经对MQ进行技术选择 对协议头进行扩展:如多租户的封装,saas模式中为了共用MQ基础资源,需要在消息头中加入必要租户信息。 二、使用准备 demo工程默认已经依赖消息,这里只是做介绍无需大家额外操作,大家可以用maven依赖树命令查看引用关系。 依赖包 增加对pamirs-connectors-event的依赖 <dependency> <groupId>pro.shushi.pamirs.framework</groupId> <artifactId>pamirs-connectors-event</artifactId> </dependency> 图4-1-21-1 分布式消息的依赖包 相关功能引入 增加模型、触发器都依赖MQ <!– 增强模型 –> <!– 增强模型 –> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-channel</artifactId> </dependency> <!– 触发器 api –> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-api</artifactId> </dependency> <!– 触发器 core –> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-core</artifactId> </dependency> 图4-1-21-2 增加模型、触发器都依赖MQ yml配置文件参考 详见4.1.1【模块之yml文件结构详解】的“pamirs.event”部分。 三、使用说明 发送消息(NotifyProducer) 概述 NotifyProducer是Pamirs Event中所有生产者的基本API,它仅仅定义了消息发送的基本行为,例如生产者自身的属性,启动和停止,当前状态,以及消息发送方法。它本身并不决定消息如何发送,而是根据具体的实现确定其功能。 目前仅实现了RocketMQProducer,你可以使用下面介绍的方法轻松使用这些功能。 使用方法 Notify注解方式 使用示例 @Component public class DemoProducer { @Notify(topic = "test", tags = "model") public DemoModel sendModel() { return new DemoModel(); } @Notify(topic = "test", tags = "dto") public DemoDTO sendDTO() { return new DemoDTO(); } } 图4-1-21-3 Notify注解方式使用示例 解释说明 使用Component注解方式注册Spring Bean。 Notify注解指定topic和tags。 topic和tags对应NotifyEvent中的topic和tags。 RocketMQProducer方法调用 使用示例 @Component public class SendRocketMQMessage { @Autowired private RocketMQProducer rocketMQProducer; /** * 发送普通消息 */ public void sendNormalMessage() { rocketMQProducer.send(new NotifyEvent("test", "model", new DemoModel())); rocketMQProducer.send(new NotifyEvent("test", "dto", new DemoDTO())); } /** * 发送有序消息 */ public void sendOrderlyMessage() { DemoModel data = new DemoModel(); data.setAge(10); rocketMQProducer.send(new NotifyEvent("test", "model", data) .setQueueSelector((queueSize, event) -> { DemoModel body = (DemoModel) event.getBody(); return body.getAge() % queueSize; })); } /** * 发送事务消息 */ public void sendTransactionMessage() { rocketMQProducer.send(new NotifyEvent("test", "model", new DemoModel()) .setIsTransaction(true) .setGroup("demoTransactionListener")); } } 图4-1-21-4 RocketMQProducer方法调用…

    2024年5月23日
    1.0K00
  • 翻译

    翻译应用是管理翻译规则的应用,以模型为基础、维护字段的翻译值,支持导入、导出 1. 操作步骤 Step1:导出所有翻译项; Step2:线下翻译; Step3:导入翻译项; Step4:刷新远程资源; Step5:页面右上角可切换语言,查看翻译效果。 2. 新增翻译 翻译是具体到模型字段,其中需要区分出是否字典; 源语言、目标语言,是在资源中维护的语言,可在资源中维护需要翻译的语言; 翻译项则是模型字段,默认翻译项为激活状态,关闭后维护的翻译项无效。 3. 导出、导入 不勾选导出:导出所有需要翻译的翻译项,包括模块、字段,源术语、翻译值等,其中如果已经翻译过的内容,会体现在翻译值中; 勾选导出:导出勾选模型的翻译项。 导入:导入翻译项,平台会根据模型拆分为多条数据。 4. 刷新远程资源 导入翻译项后,点击“刷新远程资源”按钮。 5. 查看翻译内容 页面右上角切换语言,查看翻译效果。

    2024年6月20日
    1.0K00
  • 4.2.2 框架之MessageHub

    一、MessageHub 请求出现异常时,提供”点对点“的通讯能力 二、何时使用 错误提示是用户体验中特别重要的组成部分,大部分的错误体现在整页级别,字段级别,按钮级别。友好的错误提示应该是怎么样的呢?我们假设他是这样的 与用户操作精密契合 当字段输入异常时,错误展示在错误框底部 按钮触发服务时异常,错误展示在按钮底部 区分不同的类型 错误 成功 警告 提示 调试 简洁易懂的错误信息 在oinone平台中,我们怎么做到友好的错误提示呢?接下来介绍我们的MessageHub,它为自定义错误提示提供无限的可能。 三、如何使用 订阅 import { useMessageHub, ILevel } from "@kunlun/dependencies" const messageHub = useMessageHub('当前视图的唯一标识'); /* 订阅错误信息 */ messageHub.subscribe((errorResult) => { console.log(errorResult) }) /* 订阅成功信息 */ messageHub.subscribe((errorResult) => { console.log(errorResult) }, ILevel.SUCCESS) 图4-2-2-1 订阅的示例代码 销毁 /** * 在适当的时机销毁它 * 如果页面逻辑运行时都不需要销毁,在页面destroyed是一定要销毁,重要!!! */ messageHub.unsubscribe() 图4-2-2-2 销毁的示例代码 四、实战 让我们把3.5.7.5【自定义视图-表单】一文中的自定义表单进行改造,加入我们的messageHub,模拟在表单提交时,后端报错信息在字段下方给予提示。 Step1 (后端)重写PetType的创建函数 重写PetType的创建函数,在创建逻辑中通过MessageHub返回错误信息,返回错误信息的同时要设置paths信息方便前端处理 @Action.Advanced(name = FunctionConstants.create, managed = true) @Action(displayName = "确定", summary = "创建", bindingType = ViewTypeEnum.FORM) @Function(name = FunctionConstants.create) @Function.fun(FunctionConstants.create) public PetType create(PetType data){ List<Object> paths = new ArrayList<>(); paths.add("demo.PetType"); paths.add("kind"); PamirsSession.getMessageHub().msg(new Message().msg("kind error").setPath(paths).setLevel(InformationLevelEnum.ERROR).setErrorType(ErrorTypeEnum.BIZ_ERROR)); List<Object> paths2 = new ArrayList<>(); paths2.add("demo.PetType"); paths2.add("name"); PamirsSession.getMessageHub().msg(new Message().msg("name error").setPath(paths2).setLevel(InformationLevelEnum.ERROR).setErrorType(ErrorTypeEnum.BIZ_ERROR)); // data.create(); return data; } 图4-2-2-3 (后端)重写PetType的创建函数 Step 2 修改PetForm.vue <template> <div class="petFormWrapper"> <form :model="formState" @finish="onFinish"> <a-form-item label="品种种类" id="name" name="kind" :rules="[{ required: true, message: '请输入品种种类!', trigger: 'focus' }]"> <a-input v-model:value="formState.kind" @input="(e) => onNameChange(e, 'kind')" /> <span style="color: red">{{ getServiceError('kind') }}</span> </a-form-item> <a-form-item label="品种名" id="name" name="name" :rules="[{ required: true, message: '请输入品种名!', trigger: 'focus' }]"> <a-input v-model:value="formState.name" @input="(e) => onNameChange(e, 'name')" /> <span style="color: red">{{ getServiceError('name') }}</span> </a-form-item> </form> </div> </template>- <script lang="ts"> import { defineComponent, reactive…

    2024年5月23日
    1.3K00

Leave a Reply

登录后才能评论

评论列表(2条)

  • xiao3的头像
    xiao3 2024年11月25日 am10:43

    只支持英语吗?如何添加其他语种?

    • shao的头像
      shao 2024年11月25日 pm5:24

      @xiao3支持其他的国家的语言,需要在资源里面创建,如果有其他的疑问,也可以群内联系我们哈