当前位置: 首页 > news >正文

快速上手Vue3国际化 (i18n)

在这里插入图片描述

文章目录

  • 一、背景介绍
  • 二、页面效果
  • 三、使用步骤
  • 四、代码
    • 1.src/App.vue
    • 2.src/main.js
    • 3.src/locales/index.js
    • 4.src/views/login/_request.js
    • 5.src/locales/en.json
    • 6.src/locales/zh.json
    • 7.SystemParam.vue
    • 8.I18NController.java
    • 9.DataServiceConfigValue.java
    • 10.ConfigValue.java
    • 11.application.properties
    • 12.SortedResourceBundle.java
    • 13.messages_zh_CN.properties
    • 14.messages_en_US.properties
    • 15.messages_ru_RU.properties
    • 16.LanguageTypeEnum
  • 五、注意点
  • 本人其他相关文章链接

一、背景介绍

项目前后端想支持统一的国际化词条,当前端修改语言类型选项时,页面自动刷新同时加载最新的语言词条效果。

二、页面效果

在这里插入图片描述
在这里插入图片描述

三、使用步骤

  1. 安装vue-i18n,在Vue 3项目中,使用npm或yarn安装vue-i18n插件。
# 使用 npm
npm install vue-i18n@next

# 使用 yarn
yarn add vue-i18n@next
  1. 安装完成后,需要在项目入口文件(通常是main.js或main.ts)中配置vue-i18n。
  2. 创建语言包
src/
├── locales/
│   ├── en.json
│   └── zh.json
│   └── index.js
  1. 整合语言包,在src/locales/index.js或src/locales/index.ts中整合所有语言包。
  2. 在组件中使用国际化
<div class="param" style="margin-top: 0">
  <a-spin style="width: 70%">
    <div class="param-title">
      <div class="param-title-text">{{ $t("LanguageSetting") }}:</div>
    </div>
    <div class="param-content">
      <param-item style="display: flex; align-items: center; gap: 20px">
        <template #label
          ><span class="label"
            >{{ $t("LanguageChoice") }}:</span
          ></template
        >
        <a-select
          v-model="addParam.languageChoice"
          style="width: 260px; margin-top: -6px"
          @change="changeLanguageChoice"
        >
          <a-option value="0" :label="$t('English')"></a-option>
          <a-option value="1" :label="$t('Chinese')"></a-option>
          <a-option value="2" :label="$t('Russian')"></a-option>
        </a-select>
      </param-item>
    </div>
  </a-spin>
</div>

四、代码

1.src/App.vue

<template>
  <router-view/>
</template>

<script setup>
import {reactive, provide} from "vue";
import {useI18n} from "vue-i18n";
import {useStore} from "@/stores";
import {getSystemLanguage} from "@/views/pages/system/system";
const userInfo = reactive({
  userId: '',
  token: ''
})
provide('t', useI18n().t)
provide('userInfo', userInfo)

const getSystemMode = () => {
  getSystemLanguage().then(response => {
    const mode = response.data
    if (mode) {
      localStorage.setItem('xnms-mode', mode)
      useStore().setMode(parseInt(mode))
    }
  })
}

getSystemMode()
</script>

<style>
</style>

2.src/main.js

import { createApp } from 'vue';
import App from './App.vue';
import { createPinia } from "pinia";
import router from '@/router'
import getI18n from '@/locales';
import SelfComponents from "@/views/pages/_common/selfComponents/index";
import ArcoVue, {Message} from '@arco-design/web-vue';
import ArcoVueIcon from '@arco-design/web-vue/es/icon';
import '@arco-design/web-vue/dist/arco.css';
import '@/assets/index.less';
window.Message = Message
if (process.env.NODE_ENV === 'development') {
  // import('@/mock')
}

const store = createPinia()

const promise = Promise.all([getI18n()])
const _beforeMount = await promise

const app = createApp(App)
app
  .use(_beforeMount[0])
  .use(router)
  .use(ArcoVue)
  .use(ArcoVueIcon)
  .use(store)
  .use(SelfComponents)
app.mount('#app')

3.src/locales/index.js

import { createI18n } from 'vue-i18n'
import {getI18nLanguagePkg, getNowLanguageType} from "@/views/login/_request";

const getNowLanguage = async () => {
  return new Promise((resolve, reject) => {
    getNowLanguageType().then(response => {
      if (response.code === 200) {
        resolve(response.data)
      } else {
        window.Message.warning(response.msg)
      }
    })
  })
}

const loadRemoteMessages = async (language) => {
  return new Promise((resolve, reject) => {
    getI18nLanguagePkg(language).then(response => {
      if (response.code === 200) {
        const messages = {}
        messages[languageEnum[language]] = response.data
        resolve(messages)
      } else {
        window.Message.warning(response.msg)
      }
    })
  })
}

const getI18n = async () => {
  const language = await getNowLanguage()
  localStorage.setItem('xnms-language', language)
  const remoteMessages = await loadRemoteMessages(language)
  const i18n = createI18n({
    legacy: false,
    locale: languageEnum[language],
    fallbackLocale: 'zh',
    messages: remoteMessages,
    globalInjection: true
  })
  return new Promise((resolve) => {
    resolve(i18n)
  })
}

const languageEnum = {
  0: 'en',
  1: 'zh',
  2: 'ru'
}

export default getI18n

4.src/views/login/_request.js

import {$http, Method} from "@/http";

export const getNowLanguageType = () => {
  return $http({
    url: `/api/i18n`,
    method: Method.GET,
  })
}

export const getI18nLanguagePkg = (language) => {
  return $http({
    url: `/api/i18n/${language}`,
    method: Method.GET,
  })
}

export const switchLanguage = (language) => {
  return $http({
    url: `/api/i18n/${language}`,
    method: Method.PUT,
  })
}

// 获取系统类型  0:常规系统  1:XPT系统
export const getSystemType = () => {
  return $http({
    url: `/api/config`,
    method: Method.GET,
  })
}

5.src/locales/en.json

{
  "menu_topology": "Topology View",
  "menu_alarm": "Monitoring Alarm",
  "menu_device": "Equipment Parameters",
  "menu_data": "Data Query",
  "menu_business": "Business Statistics",
}

6.src/locales/zh.json

{
  "menu_topology": "拓扑展示",
  "menu_alarm": "监控告警",
  "menu_device": "设备参数",
  "menu_data": "数据查询",
  "menu_business": "业务统计",
}

7.SystemParam.vue

<div class="param" style="margin-top: 0">
  <a-spin style="width: 70%">
     <div class="param-title">
       <div class="param-title-text">{{ $t("LanguageSetting") }}:</div>
     </div>
     <div class="param-content">
       <param-item style="display: flex; align-items: center; gap: 20px">
         <template #label
           ><span class="label"
             >{{ $t("LanguageChoice") }}:</span
           ></template
         >
         <a-select
           v-model="addParam.languageChoice"
           style="width: 260px; margin-top: -6px"
           @change="changeLanguageChoice"
         >
           <a-option value="0" :label="$t('English')"></a-option>
           <a-option value="1" :label="$t('Chinese')"></a-option>
           <a-option value="2" :label="$t('Russian')"></a-option>
         </a-select>
       </param-item>
  </a-spin>
</div>

8.I18NController.java

package com.xnms.client.service.controller.i18n;

import com.xnms.client.service.controller.common.ResponseModel;
import com.xnms.client.service.exception.XnmsException;
import com.xnms.data.contract.util.SortedResourceBundle;
import com.xnms.data.service.util.DataServiceConfigValue;
import com.xnms.service.center.utility.ConfigValue;
import io.swagger.annotations.Api;
import io.swagger.v3.oas.annotations.Operation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@Api(tags = "国际化")
@RestController
@RequestMapping("/api/i18n")
public class I18NController {

    private static final Logger logger = LoggerFactory.getLogger(I18NController.class);

    @Autowired
    private ConfigValue configValue;

    @Autowired
    private DataServiceConfigValue dataServiceConfigValue;

    /**
     * // 英文
     * ENGLISH,
     * // 中文
     * CHINESE
     * RUSSIAN;
     *
     * @return
     */
    @Operation(summary = "根据语言获取国际化列表,ENGLISH: 0,CHINESE:1,RUSSIAN:2")
    @GetMapping(value = "/{language}")
    public ResponseModel<Map<Object, Object>> getLanguageProperties(@PathVariable("language") Integer language) {
        Map<Object, Object> objectObjectMap = SortedResourceBundle.keyValueFromProperties(language);
        if (objectObjectMap == null) {
            throw new XnmsException("GetLanguageDataFailed");
        }
        return ResponseModel.ofSuccess(objectObjectMap);
    }


    @Operation(summary = "获取当前系统的语言")
    @GetMapping
    public ResponseModel<Integer> getSystemLanguage() {
        return ResponseModel.ofSuccess(Integer.parseInt(configValue.obtainConfigValue("xnms.client.Language")));
    }


    @Operation(summary = "切换系统的语言")
    @PutMapping(value = "/{language}")
    public ResponseModel<String> switchLanguage(@PathVariable("language") Integer language) {
        String res = configValue.updateConfig("xnms.client.Language", language);
        dataServiceConfigValue.writeConfig("xnms.client.Language", String.valueOf(language));
        return ResponseModel.ofSuccess(res);
    }
}

9.DataServiceConfigValue.java

package com.xnms.data.service.util;


import com.xnms.data.contract.Utility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;


@Component
public class DataServiceConfigValue {

    private static final Logger logger = LoggerFactory.getLogger(DataServiceConfigValue.class);

    //todo 标记一下,用这个和@value是不是都可以
    @Autowired
    private Environment env;

    @Autowired
    private ApplicationContext applicationContext;
   
    //todo 写入配置文件怎么生效在项目里
    /// 写入配置文件
    /// <param name="key">键</param>
    /// <param name="value">值</param>
    public void writeConfig(String key, String value) {
        String workMode = env.getProperty("xnms.service.center.WorkMode");
        // 获取配置文件路径
        Resource resource = applicationContext.getResource("classpath:application.properties");
        if (Objects.equals(workMode, "0")) {
            resource = applicationContext.getResource("classpath:application-cvt.properties");
        }
        if (Objects.equals(workMode, "1")) {
            resource = applicationContext.getResource("classpath:application-xpt.properties");
        }
        // 用于存储文件原始内容
        List<String> lines = new ArrayList<>();

        try (InputStream inputStream = resource.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
            String line;
            while ((line = reader.readLine()) != null) {
                lines.add(line);  // 保存每行内容
            }
        } catch (IOException e) {
            logger.error("ConfigService, writeConfig method, failed to read file:", e);
            return;
        }

        // 修改配置内容
        boolean keyFound = false;
        for (int i = 0; i < lines.size(); i++) {
            String line = lines.get(i).trim();

            // 跳过空行和注释行
            if (line.isEmpty() || line.startsWith("#")) {
                continue;
            }

            // 检查是否匹配到目标key
            if (line.startsWith(key + "=")) {
                lines.set(i, key + "=" + value);  // 修改指定键值对
                keyFound = true;
                break;
            }
        }

        if (keyFound) {
            try {
                // 获取文件的物理路径
                File file = resource.getFile();

                // 使用 FileWriter 覆盖原文件
                try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
                    for (String line : lines) {
                        writer.write(line);
                        writer.newLine();  // 保证每行后面换行
                    }
                    writer.flush();
                }

            } catch (IOException e) {
                logger.error("ConfigService, writeConfig method, failed to write file:", e);
            }
        } else {
            logger.warn("没有找到指定的key: {}", key);
        }
    }
    
	public String obtainConfigValue(String key) {
        String configValue = "";
        try {
            configValue = env.getProperty(key);
        } catch (Exception ex) {
            logger.error("[XNMSProxyService] ObtainConfigValue :" + ex.getMessage(), ex);
        }
        return configValue;
    }
}

10.ConfigValue.java

package com.xnms.service.center.utility;


import com.xnms.data.contract.Utility;
import com.xnms.data.contract.database.db.QuerySystemParamParams;
import com.xnms.data.contract.security.DecryptionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//todo 读取配置先这么写,打标记
@Component
public class ConfigValue {

    private static final Logger logger = LoggerFactory.getLogger(ConfigValue.class);

    private Map<String, Object> dynamicConfigMap = new HashMap<>();

    @Autowired
    private Environment env;

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private ConfigurableApplicationContext context;

    public String updateConfig(String key, Integer value) {
        // 获取 ConfigurableEnvironment
        ConfigurableEnvironment environment = (ConfigurableEnvironment) applicationContext.getEnvironment();
        // 更新动态配置的 Map
        dynamicConfigMap.put(key, value);
        // 使用自定义的 PropertySource 来存储动态配置
        PropertySource<?> propertySource = new org.springframework.core.env.MapPropertySource("dynamicConfig",
                dynamicConfigMap);
        environment.getPropertySources().remove("dynamicConfig");
        environment.getPropertySources().addFirst(propertySource);

        return "ok";
    }    
}

11.application.properties

xnms.client.Language=1

12.SortedResourceBundle.java

package com.xnms.data.contract.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SortedResourceBundle extends ListResourceBundle {

    private static final Logger logger = LoggerFactory.getLogger(SortedResourceBundle.class);

    private final List<Object[]> content;

    // 构造函数:使用 LinkedHashMap 来确保顺序
    public SortedResourceBundle(Map<String, String> properties) {
        content = new ArrayList<>();
        for (Map.Entry<String, String> entry : properties.entrySet()) {
            content.add(new Object[]{entry.getKey(), entry.getValue()});
        }
    }

    @Override
    protected Object[][] getContents() {
        return content.toArray(new Object[0][]);
    }

    // 使用 LinkedHashMap 读取 properties 文件并保持顺序
    public static SortedResourceBundle fromProperties(String fileName, boolean bool) throws IOException {
        Map<String, String> sortedProperties = new LinkedHashMap<>();
        try (InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)) {
            if (inputStream == null) {
                throw new FileNotFoundException("Resource file not found: " + fileName);
            }
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    // 跳过空行和注释
                    if (line.trim().isEmpty() || line.startsWith("#")) continue;

                    // 分割键值对
                    String[] keyValue = line.split("=", 2);
                    if (keyValue.length == 2) {
                        String v = null;
                        if (keyValue[1] != null) {
                            if (bool) {
                                v = unicodeToString(keyValue[1].trim());
                            } else {
                                v = keyValue[1].trim();
                            }
                        }
                        sortedProperties.put(keyValue[0].trim(), v);
                    }
                }
            }
        }
        return new SortedResourceBundle(sortedProperties);
    }

    public static Map<Object, Object> keyValueFromProperties(Integer language) {
        Map<Object, Object> KeyValue = new LinkedHashMap<>();
        try {
            SortedResourceBundle bundle = null;
            // 使用自定义的方法加载 properties 文件
            if (Objects.equals(LanguageTypeEnum.CHINESE.ordinal(), language)) {
                bundle = SortedResourceBundle.fromProperties("messages_zh_CN.properties", Boolean.TRUE);
            } else if (Objects.equals(LanguageTypeEnum.ENGLISH.ordinal(), language)) {
                bundle = SortedResourceBundle.fromProperties("messages_en_US.properties", Boolean.FALSE);
            } else if (Objects.equals(LanguageTypeEnum.RUSSIAN.ordinal(), language)) {
                bundle = SortedResourceBundle.fromProperties("messages_ru_RU.properties", Boolean.TRUE);
            } else {
                return null;
            }
            // 输出所有的键值对,确保顺序
            for (Object[] entry : bundle.getContents()) {
                KeyValue.put(entry[0], entry[1]);
            }
        } catch (Exception e) {
            logger.error("SortedResourceBundle类,KeyValueFromProperties方法:", e);
            return null;
        }
        return KeyValue;
    }

    public static String unicodeToString(String unicodeStr) {
        // 如果字符串中没有 "\\u" 转义编码,直接返回原字符串
        if (!unicodeStr.contains("\\u")) {
            return unicodeStr;
        }

        // 正则表达式,用于匹配 "\\u" 后面的 4 位十六进制数字
        String regex = "\\\\u([0-9a-fA-F]{4})";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(unicodeStr);

        StringBuffer result = new StringBuffer();

        // 循环匹配并替换
        while (matcher.find()) {
            // 获取匹配的 Unicode 码点并转换为字符
            String unicode = matcher.group(1);
            char character = (char) Integer.parseInt(unicode, 16);
            matcher.appendReplacement(result, String.valueOf(character));
        }
        matcher.appendTail(result);
        return result.toString();
    }
}

13.messages_zh_CN.properties

Title=XNMS客户端
XPTTitle=XNMS
Login=登  录
LoginCancel=取消登录
LoginCheckPassword=验证中…
...

14.messages_en_US.properties

Title=XNMS Client
XPTTitle=XNMS
Login=Login
LoginCancel=Cancel
LoginCheckPassword=Verifying...
...

15.messages_ru_RU.properties

Title=XNMS клиент
XPTTitle=XNMS
Login=Логин
LoginCancel=Отмена
LoginCheckPassword=Проверка...
...

16.LanguageTypeEnum

package com.xnms.data.contract.util;

public enum LanguageTypeEnum {

    // 英文
    ENGLISH,
    // 中文
    CHINESE,

    RUSSIAN;
}

五、注意点

注意点1:配置文件对应的值,【0:英文、1:中文、2:俄文】

注意点2:
梳理下国际化完整执行流程

  1. 配置文件存储初始化的语言类型,默认“中文”显示
  2. 首先执行 main.js 文件。这个文件通常负责创建 Vue 实例,并将根组件(通常是 App.vue)挂载到 DOM 中。
  3. 读取index.js文件文件,调用接口分别用于获取当前语言类型和加载语言包
  4. 创建 i18n 实例
    • getI18n: 这是主要的异步函数,用于创建 i18n 实例。
    • 它首先调用 getNowLanguage 来获取当前语言,并将其存储在 localStorage 中。
    • 然后,它调用 loadRemoteMessages 来加载该语言的消息数据。
    • 使用 createI18n 创建一个 i18n 实例,配置如下:
      • legacy: false: 表示使用 Composition API 的模式。
      • locale: 当前语言。
      • fallbackLocale: 设置回退语言为中文(zh)。
      • messages: 加载的语言包。
      • globalInjection: true: 允许在模板中全局使用 $t 和 $tc 方法。
    • 最后,返回一个解析为 i18n 实例的 Promise。
  5. 返回main.js中执行APP挂载,此刻初始化完成,展示所有菜单和词条中文效果。
  6. 如果此刻进入系统参数配置,更改语言类型,比如更选“英文”,优先调接口更新配置文件值
  7. 然后页面会强制大刷新一遍,然后从步骤2再重新执行一遍,此刻页面就变成了“英文”效果了。

注意点3:看过一次别人的博客,写法如下:

main.js

import { createApp } from 'vue';
import App from './App.vue';
import { createI18n } from 'vue-i18n';

// 导入语言包
import messages from './locales';

// 创建i18n实例
const i18n = createI18n({
  locale: 'en', // 默认语言
  fallbackLocale: 'en', // 回退语言
  messages, // 语言包
});

const app = createApp(App);

// 挂载i18n
app.use(i18n);

app.mount('#app');

index.js

import en from './en.json';
import zh from './zh.json';

export default {
  en,
  zh,
};

问题:他的写法和我的写法区别在哪?使用场景会有不同吗?

答案
您提到的两种实现方式都可以成功地将国际化(i18n)集成到 Vue 3 项目中,但它们在结构和实现细节上有所不同。以下是这两种方式的主要区别和各自的优缺点:

  1. 语言包的导入方式

    • 第一种方式
    import { createI18n } from 'vue-i18n';
    import messages from './locales';	
    

    在这个示例中,语言包 (messages) 是直接从一个文件导入的,通常这个文件会导出一个对象,包含所有语言的翻译文本。这种方式比较直接,适合简单的项目。

    • 第二种方式
    import getI18n from '@/locales';
    const promise = Promise.all([getI18n()])
    const _beforeMount = await promise
    

    在这个示例中,getI18n 是一个函数,它可能异步加载语言包。这种方式在处理大型项目或需要动态加载语言包的情况下非常有用,可以提高性能。

  2. i18n 实例的创建方式

    • 第一种方式
    const i18n = createI18n({
      locale: 'en',
      fallbackLocale: 'en',
      messages,
    });
    

    这里直接创建了一个 i18n 实例,并传入了语言设置和语言包。

    • 第二种方式
    const app = createApp(App)
    app.use(_beforeMount[0])
    

    在这种方式中,getI18n() 函数返回的 i18n 实例可能是在异步操作中创建的。通过 Promise.all 等待所有必要的异步操作完成后,再将 i18n 实例挂载到 Vue 应用中。

  3. 错误处理和可扩展性

  • 第一种方式:适合小型项目,结构简单,易于理解和维护。
  • 第二种方式:适合大型项目,可以更灵活地处理语言包的加载,支持异步加载和动态切换语言的需求。
  1. 性能
  • 第一种方式:所有语言包在应用启动时就加载到内存中,可能会导致初始加载时间较长,尤其是语言包比较大的时候。
  • 第二种方式:可以实现按需加载语言包,提升性能,尤其是在用户只需要某种语言的情况下。

总结
两种方式都可以正常工作,选择哪种方式取决于您的项目需求和复杂性。如果您的应用需要支持多语言且语言包较大,或者希望在不同条件下动态加载语言包,第二种方式可能更合适。如果您的应用相对简单,第一种方式可能更直接且易于实现。

注意点4:App.vue中的下方代码是为了给其他组件进行国际化解析的,无论是标签解析,还是方法中的词条解析。

import {useI18n} from "vue-i18n";
provide('t', useI18n().t)

注意点5:

  • 标签中解析使用:$t("LanguageChoice")
  • 方法中解析使用:t("invalidCerFileType")
<template #label><span class="label">{{ $t("LanguageChoice") }}:</span></template>
<a-option value="0" :label="$t('English')"></a-option>

const importLicenseFunction = (fileList) => {
	...
	if (!allowedExtensions.exec(lastFileName)) {
      Message.error(t("invalidCerFileType"));
      return;
    }
}

注意点6:App.vue中的下方代码只是在项目初始化加载时查询语言类型更新到相应缓存中而已,供后续其他组件从缓存直接获取使用。

const getSystemMode = () => {
  getSystemLanguage().then(response => {
    const mode = response.data
    if (mode) {
      localStorage.setItem('xnms-mode', mode)
      useStore().setMode(parseInt(mode))
    }
  })
}

注意点7:index.js中的代码写法,实际上是初始化空对象,给 messages 对象添加一个属性,属性名是 languageEnum[language] 的值,属性值是 response.data。

const messages = {}
messages[languageEnum[language]] = response.data

最后实际获取的messages 对象可能看起来像这样:

{
  'zh-CN': { /* response.data 的内容 */ }
}

注意点8:获取的语言包数据赋值给messages,这是为了将从远程获取的语言包数据传递给国际化(i18n)库,以便在页面中进行语言解析和显示。(即此刻页面所有{{ $t(“LicenseManagement”) }}实际是从i18n库中获取的词条内容)

const remoteMessages = await loadRemoteMessages(language)
...
messages: remoteMessages,

本人其他相关文章链接

1.vue3 开发电子地图功能
2.vue java 实现大地图切片上传
3.java导入excel更新设备经纬度度数或者度分秒

相关文章:

  • DeepSeek和文心一言的区别
  • 搭建hadoop集群模式并运行
  • react: styled-components实现原理 标签模版
  • Linux开发过程中常用命令整理
  • GO语言入门经典-反射3(Value 与对象的值)
  • 前端打印小票Lodop完整解决方案
  • Gnome | 在Linux中像MacOS一样点击空格预览文件
  • $_GET变量
  • [BreachCTF 2025]
  • Oracle AQ
  • python-leetcode 66.寻找旋转排序数组中的最小值
  • 快速记忆法,提高知识点背诵效率
  • MySQL学习笔记十
  • 【LangChain框架组成】 LangChain 技术栈的模块化架构解析
  • 安徽合肥天猫代入驻精细化运营实战
  • 4.8刷题记录(双指针)
  • 15-17手写持久层框架优化
  • 【Linux高级IO(三)】Reactor
  • Java基础编程练习第38题-除法器
  • C++隐式转换的机制、风险与消除方法
  • 单页式网站系统/关键词排名点击软件首页
  • 科技公司企业网站源码/百度收录提交入口网址
  • 海口网红美食餐厅/aso优化工具
  • 网站建设格式合同/seo词条
  • 做网站建设的销售怎么样/百度购物平台客服电话
  • 广州网站营销优化开发/百度权重等级