标签模板函数基础
梗概
当你调用一个标签模板函数如 func\hello ${name} yes“ 时,函数实际接收的参数如下:
function func(strings, ...values) {
// strings: ["hello ", " yes"]
// values: [name]
}参数解析
- strings: 被插值分割的字符串数组,第一个和最后一个元素分别是第一个插值前和最后一个插值后的字符串
- values: 所有插值表达式的值数组,按出现顺序排列
基础示例
let person = 'Alice';
let age = 25;
function tag(strings, ...values) {
console.log('strings:', strings); // ['Hello ', ', you are ', ' years old']
console.log('values:', values); // ['Alice', 25]
// 重新组装字符串
let result = '';
for (let i = 0; i < strings.length; i++) {
result += strings[i];
if (i < values.length) {
result += values[i];
}
}
return result;
}
console.log(tag`Hello ${person}, you are ${age} years old`);
// 输出: "Hello Alice, you are 25 years old"标签模板在国际化(i18n)中的应用
实现原理
利用标签模板函数的特性,我们可以实现一个强大的国际化系统:
- 提取翻译键名: 从第一个字符串片段中提取
- 动态值替换: 将插值中的动态值替换到翻译模板中
- 语言切换: 根据当前语言选择对应的翻译字典
完整实现
1. 定义多语言字典
const i18nDict = {
en: {
welcome: "Welcome, {name}!",
messages: "You have {count} new messages",
lastLogin: "Last login: {date}",
greeting: (hour) => hour < 12 ? "Good morning" : "Good evening"
},
zh: {
welcome: "欢迎, {name}!",
messages: "您有 {count} 条新消息",
lastLogin: "上次登录时间: {date}",
greeting: (hour) => hour < 12 ? "早上好" : "晚上好"
}
};2. 创建标签模板函数
function i18n(strings, ...values) {
// 获取当前语言(这里简化为手动切换)
const lang = document.documentElement.lang || 'en';
const dict = i18nDict[lang];
// 提取翻译键名 (从第一个插值前的字符串)
const key = strings[0].trim();
// 获取翻译模板
const template = dict[key] || strings.join('');
// 替换占位符 {name} {count} 等
return template.replace(/{(\w+)}/g, (match, p1) => {
// 查找匹配的动态值 (按命名)
const index = values.findIndex(v => v && v.name === p1);
return index !== -1 ? values[index].value : match;
});
}3. 使用示例
// 情况1:普通文本替换
console.log(i18n`welcome ${{ name: 'name', value: 'Alice' }}`);
// en: "Welcome, Alice!"
// zh: "欢迎, Alice!"
// 情况2:多个参数
console.log(i18n`messages ${{ name: 'count', value: 5 }}`);
// en: "You have 5 new messages"
// zh: "您有 5 条新消息"
// 情况3:日期格式化
const now = new Date().toLocaleDateString();
console.log(i18n`lastLogin ${{ name: 'date', value: now }}`);
// en: "Last login: 6/24/2025"
// zh: "上次登录时间: 6/24/2025"与现有i18n库的比较
优势
- 原生语法: 不需要学习额外的API,使用模板字符串语法
- 类型安全: 在TypeScript中可以提供更好的类型检查
- 简洁性: 代码更加直观和简洁
- 性能: 编译时可以进行更多优化
劣势
- 功能限制: 相比成熟的i18n库功能较少
- 复数处理: 需要额外实现复数形式处理
- 上下文: 缺少上下文相关的翻译支持
- 工具链: 缺少提取翻译文本的自动化工具
实际项目建议
对于大型项目,建议结合使用:
- 使用标签模板函数作为语法糖
- 底层仍然使用成熟的i18n库如
react-i18next或vue-i18n - 通过构建工具自动提取翻译键和生成字典文件
// 混合使用示例
import { t } from 'i18next';
function i18n(strings, ...values) {
const key = strings[0].trim();
const params = {};
values.forEach(v => {
if (v && v.name) {
params[v.name] = v.value;
}
});
return t(key, params);
}