@tofrankie/gettext
v0.0.2
Published
A lightweight runtime wrapper for node-gettext
Downloads
251
Maintainers
Readme
@tofrankie/gettext
English | 中文
针对 node-gettext 的轻量封装,目标是让前端接入更直接、更易维护。
[!IMPORTANT] 1.0.0 之前的版本可能包含破坏性变更,升级前请务必阅读 CHANGELOG.md。
特性
- 良好的 TypeScript 类型支持
- 可与任意前端框架配合使用
- 提供
init初始化能力,可设置:- 源语言(
sourceLocale) - 初始语言(
initialLocale) - 语言资源映射(
translationsMap) - 语言切换回调(
onLocaleChange) - 缺失翻译观测回调(
onMissingTranslation)
- 源语言(
- 提供 4 个常用翻译函数,并支持插值:
gettext:普通翻译ngettext:单复数翻译pgettext:上下文翻译npgettext:上下文单复数翻译
- 提供
setLocale/getLocale切换/获取当前语言 - 提供
getState获取当前运行时状态 - 提供
addTranslations动态添加翻译资源 - 可与
@tofrankie/gettext-extractor无缝配合
安装
pnpm add @tofrankie/gettext快速开始
建议在应用入口完成初始化。
import { init } from '@tofrankie/gettext'
const LOCALE_KEY = 'app_locale'
const translationsMap = {
'zh-Hans': zhTranslations, // 参考 ./examples/zh-Hans.translations.json
'en-us': enTranslations, // 参考 ./examples/en-us.translations.json
}
init({
sourceLocale: 'zh-Hans',
initialLocale: localStorage.getItem(LOCALE_KEY) || 'zh-Hans',
onLocaleChange(nextLocale) {
localStorage.setItem(LOCALE_KEY, nextLocale)
},
translationsMap,
})切换语言:
import { getLocale, setLocale } from '@tofrankie/gettext'
function toggleLocale() {
const nextLocale = getLocale() === 'zh-Hans' ? 'en-us' : 'zh-Hans'
setLocale(nextLocale)
}使用示例
[!NOTE]
gettext、ngettext、pgettext、npgettext的msgid参数仅支持字符串字面量,不支持动态模板字符串或者变量。
1. gettext:普通文本 + 插值
import { gettext } from '@tofrankie/gettext'
const title = gettext('欢迎回来,{nickname}', { nickname: '小林' })
const placeholder = gettext('搜索商品、品牌或店铺')对应 en-us 翻译示例:
msgid "欢迎回来,{nickname}"
msgstr "Welcome back, {nickname}"
msgid "搜索商品、品牌或店铺"
msgstr "Search products, brands, or stores"2. ngettext:单数/复数
import { ngettext } from '@tofrankie/gettext'
const tip1 = ngettext('你有 1 条新消息', '你有 {count} 条新消息', { count: 1 }) // 单数
const tip2 = ngettext('你有 1 条新消息', '你有 {count} 条新消息', { count: 2 }) // 复数对应 en-us 翻译示例(重点看复数差异):
msgid "你有 1 条新消息"
msgid_plural "你有 {count} 条新消息"
msgstr[0] "You have a new message"
msgstr[1] "You have {count} new messages"3. pgettext:同词在不同 UI 布局下使用短版/完整版翻译
import { pgettext } from '@tofrankie/gettext'
// 顶部导航标签(空间窄):使用更短翻译
const navLabel = pgettext('窄布局导航标签', '商品详情')
// 页面主标题(空间宽):使用更完整翻译
const pageTitle = pgettext('宽布局页面标题', '商品详情')对应 en-us 翻译示例:
msgctxt "窄布局导航标签"
msgid "商品详情"
msgstr "Details"
msgctxt "宽布局页面标题"
msgid "商品详情"
msgstr "Product Details"4. npgettext:上下文 + 单复数组合
import { npgettext } from '@tofrankie/gettext'
const text = npgettext('购物车角标', '1 件待结算商品', '{count} 件待结算商品', {
count: 3,
})对应 en-us 翻译示例:
msgctxt "购物车角标"
msgid "1 件待结算商品"
msgid_plural "{count} 件待结算商品"
msgstr[0] "There is an item pending checkout"
msgstr[1] "There are {count} items pending checkout"5. 在 React/Vue 组件里直接使用
React 示例:
import { gettext, ngettext } from '@tofrankie/gettext'
function CartSummary({ nickname, count }: { nickname: string; count: number }) {
return (
<>
<h2>{gettext('你好,{nickname}', { nickname })}</h2>
<p>{ngettext('购物车里有 1 件商品', '购物车里有 {count} 件商品', { count })}</p>
<button>{gettext('去结算')}</button>
</>
)
}Vue 示例:
<script setup>
import { gettext, npgettext } from '@tofrankie/gettext'
const count = 2
</script>
<template>
<h2>{{ gettext('我的订单') }}</h2>
<p>{{ npgettext('订单页商品数', '共 1 件', '共 {count} 件', { count }) }}</p>
<button>{{ gettext('查看物流') }}</button>
</template>对应 en-us 翻译示例:
msgid "购物车里有 1 件商品"
msgid_plural "购物车里有 {count} 件商品"
msgstr[0] "There is an item in your cart"
msgstr[1] "There are {count} items in your cart"
msgctxt "订单页商品数"
msgid "共 1 件"
msgid_plural "共 {count} 件"
msgstr[0] "an item in total"
msgstr[1] "{count} items in total"进阶用法
1. 插值语法
node-gettext 原生不支持插值语法,本包在翻译结果上做了轻量替换,使用 {key} 包裹插值参数。其中 {count} 较为特殊,它既可以作为插值参数使用,也可以作为复数数量使用。
import { gettext } from '@tofrankie/gettext'
gettext('距活动结束还有 {hours} 小时', { hours: 6 })
gettext('订单 {orderNo} 已发货,请留意签收', { orderNo: 'NO20260517001' })对应 en-us 翻译示例:
msgid "距活动结束还有 {hours} 小时"
msgstr "{hours} hours left before the campaign ends"
msgid "订单 {orderNo} 已发货,请留意签收"
msgstr "Order {orderNo} has been shipped. Please watch for delivery"注意:翻译文本中请保留占位符,例如 Order {orderNo} has been shipped. Please watch for delivery 中的 {orderNo}。
2. 单复数翻译
ngettext / npgettext 使用 data.count 作为数量。
- 未传
count时按1处理 count同时可作为插值参数使用
import { ngettext } from '@tofrankie/gettext'
ngettext('你有 1 条未读消息', '你有 {count} 条未读消息', { count: 1 })
ngettext('你有 1 条未读消息', '你有 {count} 条未读消息', { count: 12 })对应 en-us 翻译示例:
msgid "你有 1 条未读消息"
msgid_plural "你有 {count} 条未读消息"
msgstr[0] "You have an unread message"
msgstr[1] "You have {count} unread messages"3. 上下文翻译
同一文案在不同 UI 场景下可能需要不同翻译,可使用 pgettext / npgettext 区分上下文。
import { pgettext } from '@tofrankie/gettext'
// 导航标签:短文案
pgettext('导航标签', '首页')
// 新手引导:更完整描述
pgettext('新手引导标题', '首页')对应 en-us 翻译示例:
msgctxt "导航标签"
msgid "首页"
msgstr "Home"
msgctxt "新手引导标题"
msgid "首页"
msgstr "Go to Home"4. 按需加载语言资源
适合大多数前端项目的实践:
- 初始化先放入核心语言(如
zh-Hans、en-us) - 某些低频语言在用户切换时再动态加载
import { addTranslations, init, setLocale } from '@tofrankie/gettext'
init({
sourceLocale: 'zh-Hans',
initialLocale: 'zh-Hans',
translationsMap: {
'zh-Hans': zhTranslations,
'en-us': enTranslations,
},
})后续按需补充:
async function loadLocale(locale: string) {
const translations = await fetch(`/locales/${locale}.json`).then(r => r.json())
addTranslations(locale, 'messages', translations)
setLocale(locale)
}也可以在切换时直接传入当前语言资源:
import { setLocale } from '@tofrankie/gettext'
async function loadLocale(locale: string) {
const translations = await fetch(`/locales/${locale}.json`).then(r => r.json())
setLocale(locale, { translations })
}API 说明
API 清单:
init(options)isInitialized()setLocale(locale, options?)getLocale()addTranslations(locale, domain, translations)setDomain(domain)getDomain()getSourceLocale()getState()gettext(msgid, data?)ngettext(msgid, msgidPlural, data?)pgettext(msgctxt, msgid, data?)npgettext(msgctxt, msgid, msgidPlural, data?)
1. init(options)
用途:初始化运行时(通常在应用入口调用一次)。
签名:
init(options: {
sourceLocale: string
initialLocale?: string
domain?: string
translationsMap?: Record<string, GetTextTranslations>
onLocaleChange?: (nextLocale: string, prevLocale: string, context: { reason: 'init' | 'set-locale' }) => void | Promise<void>
onMissingTranslation?: (info: {
locale: string
domain: string
kind: 'gettext' | 'ngettext' | 'pgettext' | 'npgettext'
msgid: string
msgidPlural?: string
msgctxt?: string
}) => void
}): Promise<void>参数说明:
sourceLocale:源语言(代码中 msgid 使用的语言)initialLocale:初始化时的当前语言,不传则回落到sourceLocaledomain:gettext domain,默认messagestranslationsMap:语言资源映射,形如{ 'zh-Hans': translations, 'en-us': translations },参考 zh-Hans.translations.json 和 en-us.translations.jsononLocaleChange:语言切换后的回调,常用于缓存当前语言onMissingTranslation:缺失翻译观测回调,用于日志或监控
2. isInitialized()
用途:判断运行时是否已经初始化。
签名:
isInitialized(): boolean3. setLocale(locale, options?)
用途:切换当前语言。
签名:
setLocale(locale: string, options?: { translations?: GetTextTranslations }): void参数说明:
locale:目标语言,如zh-Hans、en-usoptions.translations:本次切换可直接携带该语言资源- 若未传,则尝试从
translationsMap[locale]读取
- 若未传,则尝试从
4. getLocale()
用途:获取当前语言。
getLocale(): string5. addTranslations(locale, domain, translations)
用途:动态添加某语言在某 domain 下的翻译资源。
签名:
addTranslations(locale: string, domain: string, translations: GetTextTranslations): void参数说明:
locale:语言标识domain:翻译域,通常用messagestranslations:GetTextTranslations结构数据
6. setDomain(domain) / getDomain()
用途:设置/获取当前 domain。
setDomain(domain: string): void
getDomain(): string7. getSourceLocale()
用途:获取当前 source locale。
getSourceLocale(): string8. getState()
用途:获取当前状态快照(只读)。
getState(): {
initialized: boolean
sourceLocale: string
locale: string
domain: string
availableLocales: string[]
}9. 翻译函数
gettext(msgid, data?)
用途:普通翻译 + 插值。
gettext(msgid: string, data?: Record<string, unknown>): stringngettext(msgid, msgidPlural, data?)
用途:单复数翻译。
ngettext(msgid: string, msgidPlural: string, data?: { count?: number; [k: string]: unknown }): string参数说明:
data.count:复数数量,不传默认按1处理
pgettext(msgctxt, msgid, data?)
用途:上下文翻译。
pgettext(msgctxt: string, msgid: string, data?: Record<string, unknown>): stringnpgettext(msgctxt, msgid, msgidPlural, data?)
用途:上下文单复数翻译。
npgettext(
msgctxt: string,
msgid: string,
msgidPlural: string,
data?: { count?: number; [k: string]: unknown }
): stringFAQ
1. 关于正确使用 msgid 参数问题
gettext、ngettext、pgettext、npgettext 的 msgid 参数仅支持字符串字面量,不支持动态模板字符串或者变量。
错误用法:
const msgid1 = 'Hello {name}'
gettext(msgid1, { name: 'Frankie' }) // ❌
const name = 'Frankie'
const msgid2 = `Hello ${name}`
gettext(msgid2) // ❌正确用法:
gettext('Hello {name}', { name: 'Frankie' }) // ✅
const name = 'Frankie'
gettext('Hello {name}', { name }) // ✅
gettext('Hello {name}', { name: 'Mandy' }) // ✅2. 开发、提取、翻译全流程
- 开发阶段使用
gettext/ngettext/pgettext/npgettext - 使用
@tofrankie/gettext-extractor提取 POT - 在翻译平台完成翻译,得到 PO
- 使用
gettext-parser解析 PO,产出GetTextTranslations - 将资源注入
translationsMap或通过addTranslations动态添加
可以使用 @tofrankie/gettext-extractor + @tofrankie/gettext 完成翻译环节之外的大部分流程。
License
MIT License © Frankie
