vue3项目使用【i18n-jsautotranslate】全网站一键翻译西班牙语、葡萄牙等(也适用vue2)

一、插件【vue-i18n
1、Vue I18n 进行国际化时,通常需要自己提供翻译文本(预定义翻译内容,即语言包)例如:en.json。
2、Vue I18n 本身并不提供自动翻译功能,需要结合第三方翻译API(如Google Cloud Translation API、Microsoft Translator API、DeepL API等)来实现自动翻译。
3、注册一个翻译API服务(如Google Cloud Translation)并获取API密钥
4、第三方翻译API:虽然免费,但是有额度限制,会涉及超额停用、收费、IP限制等问题
5、限额收费
(DeepL API:注册需填写境外的信用卡,每月最多可以翻译50万字符)
(Google Cloud:50万字符免费/每月;20美元(约140元)/百万字符)
6、参考文章1参考文章2

二、插件【translate.js
translate.js 作为轻量级解决方案完全能满足基本需求,作者也很友好!

三、最终选择:【i18n-jsautotranslate】translate.js配合i18n进行自动翻译
1、自动翻译可能不够准确,尤其是在特定领域或上下文语境中。因此,通常建议自动翻译仅作为初稿,然后由人工进行校对。
2、DOM 刷新问题:当 DOM 内容刷新时,translate.js 不会自动翻译,需手动调用 translate.execute()。
3、SEO 优化:虽然 translate.js 对 SEO 友好,但在中译英时,DOM 节点内容仍为中文,需结合 i18n 使用以优化 SEO。
4、性能优化:translate.js 使用缓存预加载,但频繁翻译可能影响性能,建议合理使用。
5、git地址

1、安装

npm install i18n-jsautotranslate --legacy-peer-deps

//查阅包的文档或源码(查看 node_modules/i18n-jsautotranslate 中的导出方式)以确定正确用法。
如果包使用默认导出:
import translate from ‘i18n-jsautotranslate’;
如果包使用命名导出:
import { translate } from ‘i18n-jsautotranslate’;

2、在 main.js 中引入并配置,参考文章

import translate from 'i18n-jsautotranslate';
 // console.log(translate);
 
// 设置使用 v2.x 版本
translate.setUseVersion2();
// 是否显示语言切换栏
translate.selectLanguageTag.show = false;
//设置使用的翻译服务
translate.service.use('client.edge');
//对整个页面进行整体翻译
translate.whole.enableAll();
//本地语种也进行强制翻译
translate.language.translateLocal = true;
// 监控页面动态渲染的文本进行自动翻译
translate.listener.start();


//app.config.globalProperties.$translate = translate;
app.use(translate);

app.vue(有些引入可以去掉)

<template>
  <!-- @change="languageChange" -->
  <select
    id="translate"
    v-model="selectValue"
    class="change-guest ignore"
    popper-class="ignore"
  >
    <option value="spanish">Español</option>
    <option value="portuguese">português</option>
    <option value="chinese_simplified">简体中文</option>
    <option value="english">English</option>
  </select>

  <a-config-provider :locale="locale">
  	<!-- key 路由变化 -->
    <router-view :key="$route.fullPath" />
    <global-setting />
  </a-config-provider>
</template>
<script lang="ts" setup>
  import { watch, computed, onMounted, ref, nextTick } from 'vue';
  import { useRoute } from 'vue-router';
  import enUS from '@arco-design/web-vue/es/locale/lang/en-us';
  import zhCN from '@arco-design/web-vue/es/locale/lang/zh-cn';
  import GlobalSetting from '@/components/global-setting/index.vue';
  import useLocale from '@/hooks/locale';
  import { TrayIcon } from '@tauri-apps/api/tray';
  import { Menu } from '@tauri-apps/api/menu';
  import { getCurrentWindow } from '@tauri-apps/api/window';
  import { exit } from '@tauri-apps/plugin-process';
  // 翻译-s
  import translate from 'i18n-jsautotranslate';

  const route = useRoute();
  const selectValue = ref(localStorage.getItem('to') || 'spanish');

  // 初始化翻译配置
  const initTranslate = () => {
    if (!translate) return;

    console.log(translate);
    console.log(translate.language.getLocal());

    // 设置当前语言(固定不能动!)selectValue.value
    translate.language.setLocal('chinese_simplified');
    console.log(translate.language.getLocal());

    // 配置翻译服务
    translate.service.use('client.edge');
    translate.whole.enableAll();
    translate.language.translateLocal = true;
    translate.language.setDefaultTo('spanish');

    // translate.language.setUrlParamControl();
    // 自动切换为用户所使用的语种
    // translate.setAutoDiscriminateLocalLanguage();
    // 翻译排队执行 默认true,禁用排队等待的能力,会立即翻译
    // translate.waitingExecute.use = false;

    console.log('翻译服务已初始化,当前语言:', translate.language.getCurrent());
  };

  // 执行翻译(优化版本)
  const isTranslating = ref(false);
  const hasPendingTranslation = ref(false);
  const doTranslate = async () => {
    if (!translate) return;

    // 如果正在翻译,则设置等待标志并返回
    if (isTranslating.value) {
      hasPendingTranslation.value = true;
      console.log('当前翻译还未完结,新的翻译任务已加入等待队列');
      return;
    }
    // 设置翻译状态为进行中
    isTranslating.value = true;

    try {
      // 等待 Vue 完成 DOM 更新
      await nextTick();

      translate.service.use('client.edge');
      translate.whole.enableAll();
      translate.language.translateLocal = true;
      translate.language.setDefaultTo('spanish');

      console.log('开始执行翻译...');
      // translate.execute();
      // 注意:可能是同步也可能是异步,根据库的文档,如果是异步则需要等待
      await new Promise<void>((resolve) => {
        // 假设是同步的,但为了通用性,我们可以使用Promise封装
        translate.execute();
        resolve();
        // 如果是异步方法,则应该在回调中调用resolve,例如:
        // translate.execute(() => resolve());
      });

      console.log('翻译完成');
    } catch (e) {
      console.error('翻译执行失败:', e);
    } finally {
      // 翻译结束,无论成功与否,都重置状态
      isTranslating.value = false;

      // 检查是否有等待的翻译任务
      if (hasPendingTranslation.value) {
        console.log('有待处理的翻译任务,开始执行...');
        hasPendingTranslation.value = false;
        // 递归执行下一个任务
        doTranslate();
      }
    }
  };

  // // 语言切换处理
  // const languageChange = (e: any) => {
  //   const lang = e.target.value;
  //   console.log('切换语言1:', lang);

  //   // 保存语言设置
  //   selectValue.value = lang;
  //   localStorage.setItem('to', lang);

  //   // 更新翻译语言:第一次切换语种时不会刷新页面,之后会刷新
  //   // translate.changeLanguage(lang);

  //   // 立即执行翻译
  //   doTranslate();
  // };

  const isWind = import.meta.env.VITE_IS_WIN !== '0';
  let trayWindow: any = null;
  const setupTray = async () => {
    if (!isWind) return;

    try {
      trayWindow = await getCurrentWindow();
      const trayMenu = await Menu.new({
        items: [
          {
            id: 'show',
            text: '显示窗口',
            action: async () => {
              await trayWindow.show();
              await trayWindow.setFocus();
            },
          },
          {
            id: 'quit',
            text: '退出程序',
            action: () => exit(1),
          },
        ],
      });

      await TrayIcon.new({
        icon: 'icons/icon.ico',
        menu: trayMenu,
        tooltip: '用心搜配件',
      });

      await trayWindow.onCloseRequested((e: any) => {
        e.preventDefault();
        trayWindow.hide();
      });
    } catch (error) {
      console.error('托盘初始化失败:', error);
    }
  };

  onMounted(async () => {
    await setupTray();

    // console.log('本地语言:');
    // console.log('chinese_simplified');
    // console.log('当前语言:');
    // console.log(selectValue.value);

    // 初始化翻译
    initTranslate();

    // 初始执行翻译
    setTimeout(doTranslate, 300);
  });

  // 监听路由变化
  watch(
    () => route.path,
    async (newPath) => {
      console.log('路由变化:', newPath);
      // 等待新路由组件渲染完成
      await nextTick();
      // 执行翻译
      doTranslate();
    }
  );

  // // 监听语言变化 - 确保同步
  watch(selectValue, (newLang) => {
    if (translate) {
      console.log('切换语言2:', newLang);

      // 更新翻译语言:第一次切换语种时不会刷新页面,之后会刷新
      translate.changeLanguage(newLang);

      // 立即执行翻译
      doTranslate();
    }
  });

  // 翻译-e

  const { currentLocale } = useLocale();
  const locale = computed(() => {
    switch (currentLocale.value) {
      case 'zh-CN':
        return zhCN;
      case 'en-US':
        return enUS;
      default:
        return enUS;
    }
  });
</script>
<style>
  .right-side {
    margin-right: 100px;
  }
  #translate {
    position: fixed;
    top: 15px;
    right: 10px;
    width: 114px;
    background-color: #fff;
    padding-inline: 0px;
    padding-block: 0px;
    height: 30px;
    line-height: 30px;
    padding: 0px 20px;
    font-size: 12px;
    border-radius: 4px;
    color: var(--color-text-1);
    margin-left: 10px;
    box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.1);
    border: 0;
    outline: 0;
    cursor: pointer;
  }
  #translateSelectLanguage {
    background-color: #fff;
    padding-inline: 0px;
    padding-block: 0px;
    height: 30px;
    line-height: 30px;
    padding: 0px 20px;
    font-size: 12px;
    border-radius: 4px;
    color: var(--color-text-1);
    margin-left: 10px;
    box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.1);
    border: 0;
    outline: 0;
    cursor: pointer;
  }
</style>

效果网址:搜配件-海外版
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值