一、插件【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>