npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tofrankie/gettext

v0.0.2

Published

A lightweight runtime wrapper for node-gettext

Downloads

251

Readme

@tofrankie/gettext

npm version node version npm package license

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] gettextngettextpgettextnpgettextmsgid 参数仅支持字符串字面量,不支持动态模板字符串或者变量。

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-Hansen-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:初始化时的当前语言,不传则回落到 sourceLocale
  • domain:gettext domain,默认 messages
  • translationsMap:语言资源映射,形如 { 'zh-Hans': translations, 'en-us': translations },参考 zh-Hans.translations.jsonen-us.translations.json
  • onLocaleChange:语言切换后的回调,常用于缓存当前语言
  • onMissingTranslation:缺失翻译观测回调,用于日志或监控

2. isInitialized()

用途:判断运行时是否已经初始化。

签名:

isInitialized(): boolean

3. setLocale(locale, options?)

用途:切换当前语言。

签名:

setLocale(locale: string, options?: { translations?: GetTextTranslations }): void

参数说明:

  • locale:目标语言,如 zh-Hansen-us
  • options.translations:本次切换可直接携带该语言资源
    • 若未传,则尝试从 translationsMap[locale] 读取

4. getLocale()

用途:获取当前语言。

getLocale(): string

5. addTranslations(locale, domain, translations)

用途:动态添加某语言在某 domain 下的翻译资源。

签名:

addTranslations(locale: string, domain: string, translations: GetTextTranslations): void

参数说明:

  • locale:语言标识
  • domain:翻译域,通常用 messages
  • translationsGetTextTranslations 结构数据

6. setDomain(domain) / getDomain()

用途:设置/获取当前 domain。

setDomain(domain: string): void
getDomain(): string

7. getSourceLocale()

用途:获取当前 source locale。

getSourceLocale(): string

8. getState()

用途:获取当前状态快照(只读)。

getState(): {
  initialized: boolean
  sourceLocale: string
  locale: string
  domain: string
  availableLocales: string[]
}

9. 翻译函数

gettext(msgid, data?)

用途:普通翻译 + 插值。

gettext(msgid: string, data?: Record<string, unknown>): string

ngettext(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>): string

npgettext(msgctxt, msgid, msgidPlural, data?)

用途:上下文单复数翻译。

npgettext(
  msgctxt: string,
  msgid: string,
  msgidPlural: string,
  data?: { count?: number; [k: string]: unknown }
): string

FAQ

1. 关于正确使用 msgid 参数问题

gettextngettextpgettextnpgettextmsgid 参数仅支持字符串字面量,不支持动态模板字符串或者变量。

错误用法:

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. 开发、提取、翻译全流程

  1. 开发阶段使用 gettext / ngettext / pgettext / npgettext
  2. 使用 @tofrankie/gettext-extractor 提取 POT
  3. 在翻译平台完成翻译,得到 PO
  4. 使用 gettext-parser 解析 PO,产出 GetTextTranslations
  5. 将资源注入 translationsMap 或通过 addTranslations 动态添加

可以使用 @tofrankie/gettext-extractor + @tofrankie/gettext 完成翻译环节之外的大部分流程。

License

MIT License © Frankie