MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Svelte 组件国际化:支持多语言的最佳实践

2023-11-141.2k 阅读

一、Svelte 国际化基础概念

(一)为什么需要国际化

在全球化的互联网环境下,软件应用需要面向不同语言和地区的用户。前端应用作为与用户直接交互的部分,国际化(i18n)显得尤为重要。以一个电商网站为例,如果它仅支持英文,那么非英语国家的用户在使用时就会面临极大的不便,这可能导致用户流失。通过实现国际化,网站能够以用户熟悉的语言展示商品信息、操作提示等内容,大大提升用户体验,扩大用户群体。

(二)Svelte 国际化的特点

Svelte 是一种新型的前端框架,它采用了编译时优化,将组件编译成高效的 JavaScript 代码。在国际化方面,Svelte 的响应式系统可以很好地与国际化方案结合。与其他框架不同,Svelte 不需要复杂的虚拟 DOM 差异计算来更新视图,这使得在处理语言切换等动态操作时,性能表现更为出色。例如,当切换语言时,Svelte 可以精准地更新需要翻译的文本部分,而不会影响其他无关的 DOM 元素。

二、准备工作

(一)安装必要的依赖

在 Svelte 项目中实现国际化,我们可以使用 svelte-i18n 库,它是一个专门为 Svelte 打造的国际化解决方案。首先确保你已经创建了一个 Svelte 项目,如果你使用 create - svelte 工具创建项目,项目结构应该类似这样:

my - svelte - project
├── public
│   ├── favicon.ico
│   └── index.html
├── src
│   ├── App.svelte
│   ├── main.js
│   └── routes
├── .gitignore
├── package.json
├── README.md
└── rollup.config.js

接下来安装 svelte - i18n

npm install svelte - i18n

(二)目录结构规划

为了更好地管理多语言资源,我们可以在 src 目录下创建一个 locales 目录,用于存放不同语言的翻译文件。例如:

src
├── locales
│   ├── en.json
│   └── zh.json
├── App.svelte
├── main.js
└── routes

en.json 文件中,我们可以这样定义一些翻译内容:

{
    "welcome": "Welcome to our app",
    "about": "About us"
}

zh.json 文件中对应的翻译:

{
    "welcome": "欢迎来到我们的应用",
    "about": "关于我们"
}

三、初始化国际化配置

(一)在 main.js 中初始化

打开 main.js 文件,引入 svelte - i18n 并进行初始化配置:

import { init, register } from'svelte - i18n';
import en from './locales/en.json';
import zh from './locales/zh.json';

register('en', en);
register('zh', zh);

init({
    fallbackLocale: 'en',
    initialLocale: 'en'
});

在上述代码中,我们首先通过 register 函数注册了英文(en)和中文(zh)两种语言的翻译资源。然后使用 init 函数进行初始化配置,设置 fallbackLocaleen,表示如果在当前语言中没有找到对应的翻译,将使用英文作为备用语言。initialLocale 设置为 en,表示应用启动时默认使用英文。

(二)创建语言切换逻辑

为了方便用户切换语言,我们可以在 App.svelte 中添加一个语言切换按钮。在 App.svelte 中添加如下代码:

<script>
    import { setLocale } from'svelte - i18n';
    const languages = ['en', 'zh'];
    let currentLanguage = 'en';
    const changeLanguage = (lang) => {
        setLocale(lang);
        currentLanguage = lang;
    };
</script>

<button on:click={() => changeLanguage('en')}>{currentLanguage === 'en'? 'English' : '切换到英文'}</button>
<button on:click={() => changeLanguage('zh')}>{currentLanguage === 'zh'? '中文' : '切换到中文'}</button>

在这段代码中,我们引入了 setLocale 函数,它来自 svelte - i18n,用于设置当前应用的语言。languages 数组定义了支持的语言列表,currentLanguage 变量用于记录当前选中的语言。changeLanguage 函数在用户点击语言切换按钮时被调用,它通过 setLocale 函数设置新的语言,并更新 currentLanguage

四、在 Svelte 组件中使用翻译

(一)使用 t 函数进行简单翻译

在 Svelte 组件中,我们可以使用 svelte - i18n 提供的 t 函数来获取翻译后的文本。例如,在 App.svelte 中显示欢迎信息:

<script>
    import { t } from'svelte - i18n';
</script>

<p>{$t('welcome')}</p>

这里的 $t 是一个响应式变量,welcome 是在翻译文件(如 en.jsonzh.json)中定义的键。当语言切换时,$t('welcome') 的值会自动更新为对应语言的翻译文本。

(二)处理复数形式

在很多语言中,名词的复数形式需要根据数量进行变化。例如在英文中,“1 个苹果”是“1 apple”,“2 个苹果”是“2 apples”。在 svelte - i18n 中,我们可以通过以下方式处理复数形式。首先在翻译文件中定义复数形式的翻译,以英文为例,在 en.json 中:

{
    "apples": {
        "one": "{count} apple",
        "other": "{count} apples"
    }
}

在 Svelte 组件中使用:

<script>
    import { t } from'svelte - i18n';
    let appleCount = 3;
</script>

<p>{$t('apples', { count: appleCount })}</p>

这里 $t 函数的第一个参数是翻译键 apples,第二个参数是一个对象,包含了需要替换到翻译文本中的变量 countsvelte - i18n 会根据 count 的值自动选择合适的复数形式。

(三)嵌套翻译

有时候翻译内容可能存在嵌套结构,例如导航栏可能有不同层级的菜单翻译。在翻译文件中可以这样定义,以 en.json 为例:

{
    "nav": {
        "home": "Home",
        "products": {
            "title": "Products",
            "submenu": "View products"
        }
    }
}

在 Svelte 组件中获取嵌套翻译:

<script>
    import { t } from'svelte - i18n';
</script>

<p>{$t('nav.products.title')}</p>

通过点号(.)连接键名,就可以获取到嵌套的翻译文本。

五、处理日期和数字格式

(一)日期格式

不同语言和地区对日期的显示格式有不同的习惯。例如,美国习惯使用月/日/年(MM/dd/yyyy)的格式,而欧洲很多国家习惯使用日/月/年(dd/MM/yyyy)的格式。在 Svelte 国际化中,我们可以借助 Intl.DateTimeFormat 来处理日期格式。首先在组件中引入:

<script>
    import { getLocale } from'svelte - i18n';
    const today = new Date();
    const getFormattedDate = () => {
        const locale = $getLocale();
        return new Intl.DateTimeFormat(locale, {
            year: 'numeric',
            month: 'long',
            day: '2 - digit'
        }).format(today);
    };
</script>

<p>{getFormattedDate()}</p>

这里通过 getLocale 获取当前应用的语言环境,然后将其作为参数传递给 Intl.DateTimeFormat。根据不同的语言环境,日期会以相应的格式显示。

(二)数字格式

同样,数字的显示格式也因地区而异。例如,在美国数字 1000 显示为 1,000,而在德国则显示为 1.000。在 Svelte 组件中处理数字格式可以这样做:

<script>
    import { getLocale } from'svelte - i18n';
    const number = 1234.56;
    const getFormattedNumber = () => {
        const locale = $getLocale();
        return new Intl.NumberFormat(locale, {
            style: 'decimal',
            minimumFractionDigits: 2
        }).format(number);
    };
</script>

<p>{getFormattedNumber()}</p>

Intl.NumberFormat 根据当前语言环境对数字进行格式化,style 设置为 decimal 表示普通数字格式,minimumFractionDigits 设置小数部分的最少位数。

六、动态加载翻译资源

(一)为什么需要动态加载

在一些大型应用中,可能有大量的翻译资源,一次性加载所有语言的翻译文件会导致初始加载时间过长。动态加载翻译资源可以解决这个问题,只有在用户需要切换到特定语言时,才加载该语言的翻译文件。

(二)实现动态加载

我们可以利用 svelte - i18nregisterAsync 函数来实现动态加载。首先修改 main.js,将注册翻译资源的部分改为异步加载:

import { init, registerAsync } from'svelte - i18n';

const loadLocale = async (locale) => {
    const lang = await import(`./locales/${locale}.json`);
    return lang.default;
};

registerAsync('en', () => loadLocale('en'));
registerAsync('zh', () => loadLocale('zh'));

init({
    fallbackLocale: 'en',
    initialLocale: 'en'
});

loadLocale 函数中,我们使用 import() 动态导入翻译文件。registerAsync 函数接受两个参数,第一个是语言代码,第二个是一个返回 Promise 的函数,该 Promise 解析后返回翻译资源。

在语言切换按钮的逻辑中,当用户点击按钮切换到未加载过的语言时,翻译资源会被动态加载:

<script>
    import { setLocale } from'svelte - i18n';
    const languages = ['en', 'zh'];
    let currentLanguage = 'en';
    const changeLanguage = async (lang) => {
        await setLocale(lang);
        currentLanguage = lang;
    };
</script>

<button on:click={() => changeLanguage('en')}>{currentLanguage === 'en'? 'English' : '切换到英文'}</button>
<button on:click={() => changeLanguage('zh')}>{currentLanguage === 'zh'? '中文' : '切换到中文'}</button>

这里的 setLocale 函数在动态加载翻译资源后会设置新的语言环境,从而实现动态加载翻译资源并切换语言。

七、国际化与路由

(一)语言前缀路由

在多语言应用中,通常希望通过 URL 来反映当前使用的语言。例如,/en/home 表示英文的首页,/zh/home 表示中文的首页。我们可以结合 Svelte 的路由库(如 svelte - routing)来实现语言前缀路由。首先安装 svelte - routing

npm install svelte - routing

然后在 main.js 中配置路由:

import { Router, Route } from'svelte - routing';
import { init, register } from'svelte - i18n';
import en from './locales/en.json';
import zh from './locales/zh.json';

register('en', en);
register('zh', zh);

init({
    fallbackLocale: 'en',
    initialLocale: 'en'
});

const routes = [
    {
        path: '/:lang(en|zh)/home',
        component: () => import('./routes/Home.svelte')
    },
    {
        path: '/:lang(en|zh)/about',
        component: () => import('./routes/About.svelte')
    }
];

const app = new Router({
    target: document.body,
    routes
});

在上述路由配置中,/:lang(en|zh) 表示语言前缀,它可以是 enzh。在路由组件中,我们可以获取语言前缀并设置相应的语言环境:

<script>
    import { setLocale } from'svelte - i18n';
    import { onMount } from'svelte';
    export let lang;
    onMount(() => {
        setLocale(lang);
    });
</script>

这样,当用户访问不同语言前缀的 URL 时,应用会自动切换到对应的语言。

(二)根据语言切换路由

有时候我们可能希望根据当前语言环境自动切换到不同的路由。例如,在英文环境下访问 /about,在中文环境下访问 /关于我们(假设对应的路由)。我们可以在路由切换逻辑中根据当前语言进行判断:

<script>
    import { getLocale } from'svelte - i18n';
    import { goto } from'svelte - routing';
    const handleAboutClick = () => {
        const locale = $getLocale();
        if (locale === 'en') {
            goto('/en/about');
        } else {
            goto('/zh/关于我们');
        }
    };
</script>

<button on:click={handleAboutClick}>About / 关于我们</button>

通过这种方式,用户在不同语言环境下点击相同的按钮会被导航到适合该语言的路由页面。

八、优化与注意事项

(一)性能优化

虽然 Svelte 在处理响应式更新方面有不错的性能,但在国际化应用中,大量的翻译文本和频繁的语言切换仍可能影响性能。为了优化性能,尽量避免在频繁更新的组件中进行复杂的翻译操作。例如,如果一个组件每秒都会更新数据,而其中包含大量需要翻译的文本,每次更新都可能触发翻译计算,导致性能下降。可以将翻译操作放在相对稳定的父组件中,通过传递已翻译好的文本给子组件。

(二)翻译维护

随着项目的发展,翻译内容会不断增加和变化。为了便于维护,建议使用工具来管理翻译。例如,一些在线翻译管理平台可以让不同语言的翻译人员协同工作,并且可以自动生成翻译文件。另外,在项目中对翻译键进行合理的命名和分类也很重要,例如按照功能模块对翻译键进行分组,这样在查找和更新翻译时会更加方便。

(三)测试国际化

在项目开发过程中,要进行充分的国际化测试。除了手动切换语言测试文本翻译是否正确外,还需要测试日期、数字格式是否符合相应语言地区的习惯,以及语言切换时的动画效果、页面布局等是否正常。可以使用自动化测试工具来辅助国际化测试,例如使用 Cypress 等工具编写测试用例,模拟用户切换语言的操作,并验证页面上的翻译文本和格式是否正确。