yapyak

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
yapyak compiles
t('Save changes')
into the module that renders it. The source string is the key. On save, the yapyak plugin extracts every
t()
call, writes an empty stub to each locale file, and fills the stub through a translator when one is configured.
Read the reference for the project's framework before writing setup or component code:
FrameworkReference
React with Vitereferences/react.md
React Routerreferences/react-router.md
TanStack Startreferences/tanstack-start.md
Vue with Vitereferences/vue.md
Nuxtreferences/nuxt.md
Svelte with Vitereferences/svelte.md
SvelteKitreferences/sveltekit.md
Astroreferences/astro.md
Every
YAPnnnn
diagnostic, its cause, and its fix: references/diagnostics.md
yapyak 会将
t('Save changes')
编译到渲染它的模块中。源字符串就是键。保存时,yapyak 插件会提取所有
t()
调用,在每个 locale 文件中写入空桩,并在配置了翻译器时通过翻译器填充该桩。
在编写配置或组件代码前,请阅读项目所用框架的参考文档:
框架参考文档
使用 Vite 的 Reactreferences/react.md
React Routerreferences/react-router.md
TanStack Startreferences/tanstack-start.md
使用 Vite 的 Vuereferences/vue.md
Nuxtreferences/nuxt.md
使用 Vite 的 Sveltereferences/svelte.md
SvelteKitreferences/sveltekit.md
Astroreferences/astro.md
所有
YAPnnnn
诊断、原因和修复方法:references/diagnostics.md

Install

安装

Requirements: Node.js 22.22+, Vite 8+. Frameworks: React 19+, Vue 3.4+, Svelte 5+, Astro 7+, Nuxt 4.1+, React Router 7.9+, SvelteKit 3+, TanStack Start 1.168+.
FrameworkInstallBuild configProcessor
React
npm install yapyak @yapyak/vite @yapyak/react
plugins: [react(), yapyak()]
react()
from
@yapyak/react/processor
React Routeradd
@yapyak/react-router
plugins: [reactRouter(), yapyak()]
react()
TanStack Startadd
@yapyak/tanstack-start
plugins: [tanstackStart(), react(), yapyak()]
react()
Vue
npm install yapyak @yapyak/vite @yapyak/vue
plugins: [vue(), yapyak()]
vue()
from
@yapyak/vue/processor
Nuxt
npx nuxi module add yapyak
modules: ['@yapyak/nuxt']
nuxt()
from
@yapyak/nuxt/processor
Svelte
npm install yapyak @yapyak/vite @yapyak/svelte
plugins: [svelte(), yapyak()]
svelte()
from
@yapyak/svelte/processor
SvelteKitadd
@yapyak/sveltekit
plugins: [sveltekit(), yapyak()]
svelte()
Astro
npm install yapyak @yapyak/astro
integrations: [yapyak()]
from
@yapyak/astro/integration
astro()
from
@yapyak/astro/processor
Steps, in order:
  1. Install the packages. Add a translator package when the project translates with a model:
    @yapyak/anthropic
    ,
    @yapyak/openai
    ,
    @yapyak/gemini
    ,
    @yapyak/ollama
    , or
    @yapyak/claude-code
    .
  2. Register the plugin in the build config, after the framework plugin.
    yapyak
    is a named export of
    @yapyak/vite
    .
  3. Create
    yapyak.config.ts
    at the project root with the processor for the framework:
ts
import { defineConfig } from 'yapyak/config';
import { react } from '@yapyak/react/processor';

export default defineConfig({
  processors: [react()]
});
  1. Add
    .yapyak/types.d.ts
    and
    yapyak.config.ts
    to
    include
    in
    tsconfig.json
    . Nuxt adds the types on its own.
  2. Add the first target locale:
    npx yapyak add sv
    . This creates
    locales/sv.json
    and regenerates the
    Locale
    type.
  3. Add
    .yapyak/
    to
    .gitignore
    . It holds the orphan cache; the locale files are the committed source of truth.
Register one processor per framework in the project. The built-in parser reads
.ts
,
.tsx
,
.js
,
.jsx
,
.mts
,
.mjs
,
.cts
, and
.cjs
without one.
要求:Node.js 22.22+、Vite 8+。框架:React 19+、Vue 3.4+、Svelte 5+、Astro 7+、Nuxt 4.1+、React Router 7.9+、SvelteKit 3+、TanStack Start 1.168+。
框架安装构建配置处理器
React
npm install yapyak @yapyak/vite @yapyak/react
plugins: [react(), yapyak()]
react()
from
@yapyak/react/processor
React Router添加
@yapyak/react-router
plugins: [reactRouter(), yapyak()]
react()
TanStack Start添加
@yapyak/tanstack-start
plugins: [tanstackStart(), react(), yapyak()]
react()
Vue
npm install yapyak @yapyak/vite @yapyak/vue
plugins: [vue(), yapyak()]
vue()
from
@yapyak/vue/processor
Nuxt
npx nuxi module add yapyak
modules: ['@yapyak/nuxt']
nuxt()
from
@yapyak/nuxt/processor
Svelte
npm install yapyak @yapyak/vite @yapyak/svelte
plugins: [svelte(), yapyak()]
svelte()
from
@yapyak/svelte/processor
SvelteKit添加
@yapyak/sveltekit
plugins: [sveltekit(), yapyak()]
svelte()
Astro
npm install yapyak @yapyak/astro
integrations: [yapyak()]
from
@yapyak/astro/integration
astro()
from
@yapyak/astro/processor
按顺序执行以下步骤:
  1. 安装软件包。如果项目使用模型进行翻译,请添加翻译器软件包:
    @yapyak/anthropic
    、
    @yapyak/openai
    、
    @yapyak/gemini
    、
    @yapyak/ollama
    或
    @yapyak/claude-code
    。
  2. 在构建配置中注册插件,并将其放在框架插件之后。
    yapyak
    是
    @yapyak/vite
    的具名导出。
  3. 在项目根目录创建
    yapyak.config.ts
    ,并为框架设置处理器:
ts
import { defineConfig } from 'yapyak/config';
import { react } from '@yapyak/react/processor';

export default defineConfig({
  processors: [react()]
});
  1. 在
    tsconfig.json
    的
    include
    中添加
    .yapyak/types.d.ts
    和
    yapyak.config.ts
    。Nuxt 会自动添加这些类型。
  2. 添加第一个目标 locale:
    npx yapyak add sv
    。这会创建
    locales/sv.json
    并重新生成
    Locale
    类型。
  3. 将
    .yapyak/
    添加到
    .gitignore
    。该目录保存孤儿缓存;locale 文件才是应提交到版本控制的事实来源。
在项目中为每个框架注册一个处理器。内置解析器无需处理器即可读取
.ts
、
.tsx
、
.js
、
.jsx
、
.mts
、
.mjs
、
.cts
和
.cjs
。

Writing text

编写文本

Wrap every user-facing string in
t()
from
yapyak
. Write the sentence in the source language exactly as the reader sees it; it is the key.
ts
// ✓ The sentence is the key
t('Save changes');

// ✗ A synthetic key has no source string to translate
t('common.buttons.save');
Call
t()
inside the component or function that renders the text. A module-scope call evaluates once and never follows the active locale (
YAP0055
).
ts
// ✓ Reads the active locale on every render
function SaveButton() {
  return t('Save changes');
}

// ✗ Evaluates once when the module loads
const label = t('Save changes');
Write the source as a static string literal and the params as an inline object literal. The compiler reads neither from a variable.
ts
// ✓ Static source, inline params
t('Hi {name}', { name });

// ✗ Template literal (YAP0002)
t(`Hi ${name}`);

// ✗ Variable source
t(label);

// ✗ Params in a variable (YAP0006)
t('Hi {name}', params);
Never concatenate around
t()
→ put the moving part in a placeholder.
ts
// ✓ One message, one placeholder
t('{count} items in your cart', { count });

// ✗ Word order differs per language
t('You have') + ' ' + count + ' ' + t('items');
Call
t()
in attribute positions too:
aria-label={t('Pause')}
,
:title="t('Settings')"
,
placeholder={t('Search')}
. The attribute name is part of the call-site context the translator receives.
使用
yapyak
中的
t()
包裹所有面向用户的字符串。请按照读者看到的样子,在源语言中准确书写该句子;它就是键。
ts
// ✓ The sentence is the key
t('Save changes');

// ✗ A synthetic key has no source string to translate
t('common.buttons.save');
请在渲染文本的组件或函数内部调用
t()
。模块作用域中的调用只求值一次,且不会跟随当前 locale 变化(
YAP0055
)。
ts
// ✓ Reads the active locale on every render
function SaveButton() {
  return t('Save changes');
}

// ✗ Evaluates once when the module loads
const label = t('Save changes');
源文本必须写成静态字符串字面量,参数必须写成内联对象字面量。编译器无法从变量中读取这两者。
ts
// ✓ Static source, inline params
t('Hi {name}', { name });

// ✗ Template literal (YAP0002)
t(`Hi ${name}`);

// ✗ Variable source
t(label);

// ✗ Params in a variable (YAP0006)
t('Hi {name}', params);
切勿在
t()
周围拼接字符串 → 请将变化的部分放入占位符。
ts
// ✓ One message, one placeholder
t('{count} items in your cart', { count });

// ✗ Word order differs per language
t('You have') + ' ' + count + ' ' + t('items');
属性位置也应调用
t()
:
aria-label={t('Pause')}
、
:title="t('Settings')"
、
placeholder={t('Search')}
。属性名也是翻译器接收到的调用位置上下文的一部分。

Placeholders

占位符

Write
{name}
in the source and pass the value under the same name. TypeScript derives the required keys from the source string.
FormExampleValue type
Plain
t('Hi {name}', { name })
string | number
, rendered as its string form
Number
t('Total {n, number, integer}', { n })
number
; styles
decimal
,
integer
,
percent
,
currency <ISO 4217>
Date
t('Updated {when, date, long}', { when })
Date
or millisecond timestamp; styles
short
,
medium
,
long
,
full
Time
t('Doors open at {when, time, short}', { when })
Date
or millisecond timestamp; styles
short
,
medium
,
long
,
full
Plural
t('{count, plural, one {# item} other {# items}}', { count })
number
Ordinal
t('{place, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}', { place })
number
Select
t('{role, select, admin {Admin panel} other {Reader view}}', { role })
string
; a non-string is coerced with
String()
Name a placeholder with letters, digits, and underscores, starting with a letter or underscore (
YAP0052
). Non-ASCII letters are allowed.
Rules for
plural
,
selectordinal
, and
select
:
  • Write an
    other
    branch (
    YAP0008
    ).
  • Write
    #
    inside a plural branch for the formatted count.
  • Write
    =0
    ,
    =1
    for an exact value; exact matches are tried before categories.
  • Name plural branches with the CLDR keywords
    zero
    ,
    one
    ,
    two
    ,
    few
    ,
    many
    ,
    other
    (
    YAP0046
    ).
  • Write only the branches the source language uses,
    one
    and
    other
    for English; the translator adds the branches each target locale needs.
  • Nest freely: a branch is a full message and holds placeholders, plurals, and selects.
  • Split a message that has become unreadable into two
    t()
    calls and let the component pick.
  • Type a
    select
    value as a union literal so every legal value has a branch.
Format a list with
format.list()
outside the message and pass the result in as a placeholder. Format relative time with
format.relativeTime()
outside the message.
在源文本中写入
{name}
,并使用同名参数传入值。TypeScript 会从源字符串推导出必需的键。
形式示例值类型
普通
t('Hi {name}', { name })
string | number
,按字符串形式渲染
数字
t('Total {n, number, integer}', { n })
number
;样式包括
decimal
、
integer
、
percent
、
currency <ISO 4217>
日期
t('Updated {when, date, long}', { when })
Date
或毫秒时间戳;样式包括
short
、
medium
、
long
、
full
时间
t('Doors open at {when, time, short}', { when })
Date
或毫秒时间戳;样式包括
short
、
medium
、
long
、
full
复数
t('{count, plural, one {# item} other {# items}}', { count })
number
序数
t('{place, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}', { place })
number
选择
t('{role, select, admin {Admin panel} other {Reader view}}', { role })
string
;非字符串值会通过
String()
转换
占位符名称可包含字母、数字和下划线,且必须以字母或下划线开头(
YAP0052
)。允许使用非 ASCII 字母。
plural
、
selectordinal
和
select
的规则:
  • 必须编写
    other
    分支(
    YAP0008
    )。
  • 在复数分支中使用
    #
    表示格式化后的计数。
  • 使用
    =0
    、
    =1
    表示精确值;系统会先尝试精确匹配,再尝试类别匹配。
  • 复数分支必须使用 CLDR 关键词
    zero
    、
    one
    、
    two
    、
    few
    、
    many
    、
    other
    (
    YAP0046
    )。
  • 仅编写源语言使用的分支;英语只需
    one
    和
    other
    ,翻译器会为每个目标 locale 添加所需的分支。
  • 可以自由嵌套;一个分支是一条完整消息,可以包含占位符、复数和 select。
  • 如果一条消息变得难以阅读,请将其拆成两个
    t()
    调用,再由组件选择使用哪一条。
  • 将 select 值声明为联合字面量类型,确保每个合法值都有对应分支。
请在消息外部使用
format.list()
格式化列表,再将结果作为占位符传入。相对时间也应使用
format.relativeTime()
在消息外部格式化。

Homonyms

同源异义

Split two calls with the same source and different meanings with
t.as(context, source)
:
ts
t.as('action', 'Open');

t.as('status', 'Open');
  • Write the context as a short literal label (
    action
    ,
    status
    ,
    noun
    ,
    verb
    ), never a variable (
    YAP0017
    ).
  • Use
    t.as()
    for every occurrence of that source in the source file. Mixing
    t('Open')
    and
    t.as('action', 'Open')
    in one file is an error (
    YAP0018
    ).
  • Use plain
    t()
    when the source has one meaning. A lone context is an error (
    YAP0019
    ).
如果同一源字符串对应不同含义,请使用
t.as(context, source)
区分两次调用:
ts
t.as('action', 'Open');

t.as('status', 'Open');
  • 上下文必须写成简短的字面量标签(
    action
    、
    status
    、
    noun
    、
    verb
    ),不能使用变量(
    YAP0017
    )。
  • 源文件中的每次该源字符串调用都要使用
    t.as()
    。在同一文件中混用
    t('Open')
    和
    t.as('action', 'Open')
    会报错(
    YAP0018
    )。
  • 当源字符串只有一种含义时,使用普通的
    t()
    。仅使用上下文会报错(
    YAP0019
    )。

Forced locale

强制 locale

  • Write
    t.in(locale, source)
    for the translation in a forced locale regardless of the active one. The locale argument is typed against
    Locale
    .
  • Chain
    t.in('sv').as('action', 'Open')
    or
    t.as('action').in('sv', 'Open')
    when both apply.
  • Complete the chain in one expression. A chain stored in a variable is an error (
    YAP0020
    ).
  • 使用
    t.in(locale, source)
    获取指定 locale 的翻译,而不考虑当前 locale。locale 参数的类型限定为
    Locale
    。
  • 当两种行为同时适用时,可以链接调用
    t.in('sv').as('action', 'Open')
    或
    t.as('action').in('sv', 'Open')
    。
  • 必须在一个表达式中完成整个链式调用。将链式调用存储到变量中会报错(
    YAP0020
    )。

Rich text

富文本

Write tags inside the source and bind each tag to a handler in
<RichText>
from the framework's adapter package.
ts
t('Read our <link>privacy policy</link> for details.');

t('Line one<br/>line two');
TagFormHandler
Pair
<link>…</link>
Receives the wrapped children
Void
<br/>
Receives nothing
  • Start a tag name with a letter and continue with letters or digits.
  • Name a tag by meaning (
    link
    ,
    strong
    ,
    price
    ), never by HTML element; the element belongs in the handler.
  • Bind a handler for every tag in the source.
  • Never write attributes on a tag → put the
    href
    in the handler.
  • Walk the structure with
    parseRichText(value)
    from
    yapyak
    , which returns
    RichTextNode[]
    , when
    <RichText>
    does not fit.
Take the handler shape from the framework's reference file.
在源文本中写入标签,并通过框架适配器包中的
<RichText>
为每个标签绑定处理器。
ts
t('Read our <link>privacy policy</link> for details.');

t('Line one<br/>line two');
标签形式处理器
成对标签
<link>…</link>
接收被包裹的子节点
空元素标签
<br/>
不接收内容
  • 标签名必须以字母开头,后续只能包含字母或数字。
  • 按含义命名标签(
    link
    、
    strong
    、
    price
    ),不要按 HTML 元素命名;具体元素应由处理器决定。
  • 源文本中的每个标签都必须绑定处理器。
  • 切勿在标签上编写属性 → 请将
    href
    等内容放入处理器。
  • 如果
    <RichText>
    不适合当前场景,可以使用
    yapyak
    中的
    parseRichText(value)
    遍历结构,该函数返回
    RichTextNode[]
    。
请根据框架参考文件确定处理器结构。

Formatting outside messages

消息外格式化

Format values outside a message with
format
from
yapyak
. Every call reads the active locale.
MethodBacked byNote
format.number(value, options)
Intl.NumberFormat
style: 'currency'
requires
currency
;
style: 'unit'
requires
unit
format.dateTime(value, options)
Intl.DateTimeFormat
dateStyle
and
timeStyle
presets, or field options
format.list(items, options)
Intl.ListFormat
Run each item through
t()
first
format.relativeTime(value, unit, options)
Intl.RelativeTimeFormat
Negative is past;
numeric: 'auto'
gives
yesterday
format.displayName(code, type, options)
Intl.DisplayNames
type
is
language
,
region
,
script
,
currency
,
calendar
, or
dateTimeField
format.in(locale)
Returns a
format
scoped to a forced locale
  • Use
    format.*
    for a value outside a message and an ICU sub-format for a value inside one.
  • Type a currency code as
    Currency
    from
    yapyak
    ; narrow user input with
    isCurrency(value)
    .
  • Label a locale switcher with
    format.in(value).displayName(value, 'language')
    , never with a
    t()
    call per language.
  • An unsupported currency, unit, time zone, or display-name code renders a fallback instead of throwing and reports
    YAP0035
    ,
    YAP0036
    ,
    YAP0037
    , or
    YAP0056
    .
使用
yapyak
中的
format
在消息外部格式化值。每次调用都会读取当前 locale。
方法底层实现说明
format.number(value, options)
Intl.NumberFormat
style: 'currency'
必须提供
currency
;
style: 'unit'
必须提供
unit
format.dateTime(value, options)
Intl.DateTimeFormat
支持
dateStyle
和
timeStyle
预设,或各字段选项
format.list(items, options)
Intl.ListFormat
先将每个条目传入
t()
处理
format.relativeTime(value, unit, options)
Intl.RelativeTimeFormat
负数表示过去;
numeric: 'auto'
会得到
yesterday
format.displayName(code, type, options)
Intl.DisplayNames
type
可为
language
、
region
、
script
、
currency
、
calendar
或
dateTimeField
format.in(locale)
返回限定到强制 locale 的
format
  • 消息外部的值使用
    format.*
    ,消息内部的值使用 ICU 子格式。
  • 使用
    yapyak
    中的
    Currency
    类型约束货币代码;使用
    isCurrency(value)
    收窄用户输入。
  • 使用
    format.in(value).displayName(value, 'language')
    为 locale 切换器添加标签,不要为每种语言分别调用
    t()
    。
  • 不支持的货币、单位、时区或显示名称代码不会抛出异常,而是渲染回退内容,并报告
    YAP0035
    、
    YAP0036
    、
    YAP0037
    或
    YAP0056
    。

The active locale

当前 locale

A locale is the BCP 47 tag of a file in
localesDir
:
sv
for
locales/sv.json
,
en-GB
for
locales/en-GB.json
. Write tags as
lowercase-Titlecase-UPPERCASE
(
zh-Hant-TW
). The
Locale
type is the union of the files on disk and refreshes on
yapyak add
.
The active locale resolves in this order, first value wins: a persisted choice, a detected locale when
detectUserLocale
is on, then
defaultLocale
.
Export from
yapyak
Use
getLocale()
The active locale, outside components
setLocale(locale)
Switch and persist; ignored with a warning for an unknown value (
YAP0028
)
locales
Every added locale, for a switcher
defaultLocale
The locale when no persisted or detected locale matches
isLocale(value)
Type guard for a canonical string
parseLocale(value)
Normalizes casing, returns
Locale
or
undefined
getLocaleFallbackChain(locale)
['zh-Hant-TW', 'zh-Hant', 'zh']
;
defaultLocale
is not appended
getTextDirection(locale)
'rtl'
or
'ltr'
for
<html dir>
Read and switch the locale inside a component through the framework binding; never call
getLocale()
or
setLocale()
there.
FrameworkBinding
React
const [locale, setLocale] = useLocale()
;
useTextDirection()
Vue, Nuxt
locale
ref (
locale.value
, or
locale
in a template);
textDirection
ref
Svelte, SvelteKit
locale.current
;
textDirection.current
Astro
getLocale()
in frontmatter; the middleware binds it per request
Narrow a string from a URL, a form, or a cookie with
isLocale()
or
parseLocale()
before
setLocale()
.
locale 是
localesDir
中某个文件的 BCP 47 标签,例如
locales/sv.json
对应
sv
,
locales/en-GB.json
对应
en-GB
。标签应写成
lowercase-Titlecase-UPPERCASE
形式(
zh-Hant-TW
)。
Locale
类型是磁盘上所有 locale 文件的联合类型,并在执行
yapyak add
时刷新。
当前 locale 按以下顺序解析,第一个匹配值生效:已持久化的选择、启用
detectUserLocale
时检测到的 locale,最后是
defaultLocale
。
从
yapyak
导出
用途
getLocale()
在组件外部获取当前 locale
setLocale(locale)
切换并持久化 locale;传入未知值时会忽略并发出警告(
YAP0028
)
locales
获取所有已添加的 locale,用于构建切换器
defaultLocale
当持久化或检测到的 locale 均不匹配时使用的 locale
isLocale(value)
判断字符串是否为规范形式的类型守卫
parseLocale(value)
规范化大小写,返回
Locale
或
undefined
getLocaleFallbackChain(locale)
返回
['zh-Hant-TW', 'zh-Hant', 'zh']
;不会附加
defaultLocale
getTextDirection(locale)
返回
<html dir>
使用的
'rtl'
或
'ltr'
请通过框架绑定在组件内部读取和切换 locale;切勿在其中调用
getLocale()
或
setLocale()
。
框架绑定方式
React
const [locale, setLocale] = useLocale()
;
useTextDirection()
Vue、Nuxt
locale
ref(
locale.value
,或在模板中使用
locale
);
textDirection
ref
Svelte、SvelteKit
locale.current
;
textDirection.current
Astro在 frontmatter 中使用
getLocale()
;middleware 会按请求绑定 locale
对来自 URL、表单或 cookie 的字符串,请先使用
isLocale()
或
parseLocale()
收窄类型,再调用
setLocale()
。

yapyak.config.ts

yapyak.config.ts

Every field is optional. Export the result of
defineConfig
from
yapyak/config
.
FieldDefaultSets
sourceLocale
defaultLocale
The language the source strings are written in. Set it only when it differs from
defaultLocale
defaultLocale
'en'
The locale when no persisted or detected locale matches
localesDir
'locales'
The directory of locale files, relative to the project root
include
['.']
Directories, globs, or
RegExp
to scan; replaces the default
exclude
tests,
*.d.ts
,
node_modules
,
dist
,
build
,
coverage
,
yapyak.config.*
Files to skip; replaces the default. Extend with
[...DEFAULT_EXCLUDE, '**/*.vendor.ts']
processors
[]
One per framework
translator
noneThe translator that fills stubs
autoTranslateThreshold
20
New strings in one save above which the save loop writes stubs but skips the translator.
0
turns auto-translation off
preserveTranslationsOnSourceEdit
true
without a translator,
false
with one
Keep the translation when a source string is edited in place
persistence
'none'
Where the active locale is stored between page loads
detectUserLocale
false
Match
Accept-Language
on the server and
navigator.languages
in the browser on a first visit
syncHtmlAttributes
false
Keep
<html lang>
and
<html dir>
in step with the active locale on every change
ProcessorOptionUse
react({ rsc: true })
rsc
React Server Components: only
'use client'
files subscribe to the locale
nuxt({ explicitImports: true })
explicitImports
Another global
t
exists in the project
vue()
,
svelte()
,
astro()
none
每个字段都是可选的。请从
yapyak/config
导出
defineConfig
的结果。
字段默认值作用
sourceLocale
defaultLocale
编写源字符串所使用的语言。仅当它与
defaultLocale
不同时才需要设置
defaultLocale
'en'
当持久化或检测到的 locale 均不匹配时使用的 locale
localesDir
'locales'
相对于项目根目录的 locale 文件目录
include
['.']
要扫描的目录、glob 或
RegExp
;会替换默认配置
exclude
tests、
*.d.ts
、
node_modules
、
dist
、
build
、
coverage
、
yapyak.config.*
要跳过的文件;会替换默认配置。可通过
[...DEFAULT_EXCLUDE, '**/*.vendor.ts']
扩展
processors
[]
每个框架配置一个处理器
translator
无用于填充桩的翻译器
autoTranslateThreshold
20
单次保存新增的字符串超过此值时,保存循环会写入桩但跳过翻译器。
0
关闭自动翻译
preserveTranslationsOnSourceEdit
未配置翻译器时为
true
,配置翻译器时为
false
原地编辑源字符串时是否保留翻译
persistence
'none'
页面加载之间当前 locale 的存储位置
detectUserLocale
false
首次访问时,在服务器端匹配
Accept-Language
,在浏览器中匹配
navigator.languages
syncHtmlAttributes
false
每次变化时,使
<html lang>
和
<html dir>
与当前 locale 保持同步
处理器选项用途
react({ rsc: true })
rsc
React Server Components:只有
'use client'
文件订阅 locale 变化
nuxt({ explicitImports: true })
explicitImports
项目中已存在另一个全局
t
vue()
、
svelte()
、
astro()
无

Persistence

持久化

ValueOptionsFits
'none'
Kiosk, development build, locale carried by another system. A reload returns to
defaultLocale
'cookie'
{ type: 'cookie', name: 'locale', secure: false }
SSR apps. The adapter reads the cookie per request;
setLocale()
on the server buffers a
Set-Cookie
'local-storage'
{ type: 'local-storage', key: 'locale' }
Client-only apps. Under SSR the first render uses
defaultLocale
and flips after hydration
'url'
{ type: 'url', match: /[?&]lang=(?<locale>[^&]+)/ }
Locale in the path (
/sv/settings
, the default reads the first segment) or in a query string
  • Under
    'url'
    , switch by navigating.
    setLocale()
    is a no-op and reports
    YAP0026
    .
  • Set
    secure: true
    on the cookie in production only; on plain HTTP the browser refuses the write.
值选项适用场景
'none'
信息亭、开发构建,或 locale 由其他系统携带。重新加载后会回到
defaultLocale
'cookie'
{ type: 'cookie', name: 'locale', secure: false }
SSR 应用。适配器会按请求读取 cookie;在服务器端调用
setLocale()
会缓冲
Set-Cookie
'local-storage'
{ type: 'local-storage', key: 'locale' }
仅客户端应用。在 SSR 下,首次渲染使用
defaultLocale
,hydration 后切换
'url'
{ type: 'url', match: /[?&]lang=(?<locale>[^&]+)/ }
locale 位于路径(
/sv/settings
,默认读取第一个路径段)或查询字符串中
  • 在
    'url'
    模式下,通过导航切换 locale。
    setLocale()
    不执行任何操作,并报告
    YAP0026
    。
  • 仅在生产环境中将 cookie 的
    secure
    设置为
    true
    ;通过纯 HTTP 访问时,浏览器会拒绝写入。

Fixed-locale builds

固定 locale 构建

  • Set
    fixedLocale
    in the build config, never in
    yapyak.config.ts
    . Every
    t()
    compiles to the literal of that locale and the runtime disappears from the bundle.
  • Drive a CI matrix from an environment variable. With the variable unset, the build is a normal multi-locale build.
  • Pass an added locale; the build throws otherwise.
FrameworkWhere
Vite (React, Vue, Svelte, React Router, SvelteKit, TanStack Start)
yapyak({ fixedLocale: process.env.YAPYAK_LOCALE })
in
vite.config.ts
Astro
yapyak({ fixedLocale: process.env.YAPYAK_LOCALE })
in
astro.config.ts
Nuxt
yapyak: { fixedLocale: process.env.YAPYAK_LOCALE }
in
nuxt.config.ts
  • 在构建配置中设置
    fixedLocale
    ,不要在
    yapyak.config.ts
    中设置。每个
    t()
    都会编译为该 locale 的字面量,运行时也会从 bundle 中移除。
  • 通过环境变量驱动 CI 矩阵。未设置该变量时,构建仍会生成常规的多 locale 版本。
  • 必须传入已添加的 locale,否则构建会抛出异常。
框架配置位置
Vite(React、Vue、Svelte、React Router、SvelteKit、TanStack Start)在
vite.config.ts
中使用
yapyak({ fixedLocale: process.env.YAPYAK_LOCALE })
Astro在
astro.config.ts
中使用
yapyak({ fixedLocale: process.env.YAPYAK_LOCALE })
Nuxt在
nuxt.config.ts
中使用
yapyak: { fixedLocale: process.env.YAPYAK_LOCALE }

Locale files

Locale 文件

One JSON file per target locale in
localesDir
. The source language has no file. Entries nest under the source file's path, then the source string:
json
{
  "src/components/save-button.tsx": {
    "Save changes": "Spara ändringar"
  }
}
A homonym nests its contexts under the source:
json
{
  "src/components/dialog.tsx": {
    "Open": {
      "action": "Öppna",
      "status": "Öppen"
    }
  }
}
  • An empty string is an empty stub. The runtime renders the source string for it.
  • Hand-edit a value to correct or replace a translation. The running app updates through HMR.
  • Keep every placeholder, tag, and ICU structure of the source in the translation (
    YAP0010
    ,
    YAP0011
    ,
    YAP0012
    ,
    YAP0038
    ,
    YAP0045
    ,
    YAP0050
    ,
    YAP0051
    ). Placeholder names stay fixed; their position in the sentence is free.
  • Never edit a key → change the source string in the component; the compiler rewrites the key.
  • Never add an entry by hand → write the
    t()
    call; the save loop or
    yapyak translate
    creates the stub.
  • Renames and moves of source files restore translations from the orphan cache in
    .yapyak/orphans.json
    on the next save. Deleting
    .yapyak/
    loses nothing committed.
  • Orphans stay in the locale file until
    yapyak clean --write
    removes them.
Fill empty stubs by hand only when the project has no translator or the user asks for it.
在
localesDir
中为每个目标 locale 创建一个 JSON 文件。源语言没有对应文件。条目先按源文件路径嵌套,再按源字符串嵌套:
json
{
  "src/components/save-button.tsx": {
    "Save changes": "Spara ändringar"
  }
}
同源异义会将不同上下文嵌套在源字符串下:
json
{
  "src/components/dialog.tsx": {
    "Open": {
      "action": "Öppna",
      "status": "Öppen"
    }
  }
}
  • 空字符串表示空桩,运行时会为其渲染源字符串。
  • 可以手动编辑某个值来修正或替换翻译,运行中的应用会通过 HMR 更新。
  • 翻译必须保留源文本中的所有占位符、标签和 ICU 结构(
    YAP0010
    、
    YAP0011
    、
    YAP0012
    、
    YAP0038
    、
    YAP0045
    、
    YAP0050
    、
    YAP0051
    )。占位符名称必须保持不变,但可以在句子中调整位置。
  • 切勿编辑键 → 请修改组件中的源字符串,编译器会重写键。
  • 切勿手动添加条目 → 请编写
    t()
    调用,保存循环或
    yapyak translate
    会创建桩。
  • 重命名或移动源文件后,下次保存时会从
    .yapyak/orphans.json
    中的孤儿缓存恢复翻译。删除
    .yapyak/
    不会丢失任何已提交的内容。
  • 孤儿条目会保留在 locale 文件中,直到执行
    yapyak clean --write
    将其移除。
仅当项目没有翻译器或用户明确要求时,才手动填充空桩。

Translators

翻译器

A translator fills empty stubs on save and in
yapyak translate
. Requests go from the developer's machine to the provider. Set one in
yapyak.config.ts
:
ts
import { defineConfig } from 'yapyak/config';
import { anthropic } from '@yapyak/anthropic';

export default defineConfig({
  translator: anthropic({
    apiKey: process.env.ANTHROPIC_API_KEY,
    glossary: { cart: { sv: 'kundvagn' } },
    voice: 'Concise and friendly'
  })
});
PackageFactoryKeyDefaults that differ
@yapyak/anthropic
anthropic({ apiKey })
ANTHROPIC_API_KEY
@yapyak/openai
openai({ apiKey })
OPENAI_API_KEY
Extras
seed
,
organization
,
user
; other OpenAI-compatible endpoints through
endpoint
and
headers
@yapyak/gemini
gemini({ apiKey })
GEMINI_API_KEY
batchSize: 15
@yapyak/ollama
ollama()
none
endpoint: 'http://localhost:11434/api/generate'
,
model: 'llama3.1'
,
timeout: 120_000
,
maxRetries: 1
,
batchSize: 8
@yapyak/claude-code
claudeCode()
the signed-in
claude
CLI
concurrency: 2
,
timeout: 120_000
; no HTTP options;
model
falls back to the CLI's
Shared optionDefaultSets
model
per providerModel identifier
voice
noneOne or two sentences of tone guidance
glossary
{}
Record<source term, Record<locale, translation>>
; pinned brand names, domain terms, and words the model gets wrong
context
'minimal'
'none'
sends the source string only;
'rich'
adds the surrounding code
examples
5
,
0
under
context: 'none'
Existing translations sent per request as style reference
temperature
0.2
Sampling temperature
maxTokens
per providerOutput token cap
timeout
30_000
Per-request timeout in milliseconds
maxRetries
2
Retries on 408, 429, 5xx, and network failures
batchSize
25
Source strings per request
concurrency
5
Parallel requests
headers
{}
Extra HTTP headers
endpoint
per providerAPI URL
  • The
    t.as()
    context is sent at every
    context
    level.
  • A voice or glossary change affects new translations only. Run
    yapyak translate --force
    or
    yapyak retranslate "<source>"
    to propagate it.
  • Pick the translator by environment when CI has no signed-in CLI:
    process.env.CI ? anthropic({ apiKey: process.env.ANTHROPIC_API_KEY }) : claudeCode()
    .
A custom translator is
createTranslator({ id, translate })
from
yapyak/translator
:
  • translate({ items, signal, sourceLocale, targetLocales })
    returns one object per item, in item order, keyed by every target locale.
  • Forward
    signal
    to the request.
  • Throw
    TranslatorAuthError
    ,
    TranslatorRateLimitError
    ,
    TranslatorTimeoutError
    ,
    TranslatorNetworkError
    ,
    TranslatorSafetyError
    ,
    TranslatorInvalidResponseError
    , or
    TranslatorTruncatedError
    from
    yapyak/translator
    .
  • Retry inside the callback. yapyak runs it once.
翻译器会在保存时和执行
yapyak translate
时填充空桩。请求会从开发者机器发送到提供商。在
yapyak.config.ts
中设置翻译器:
ts
import { defineConfig } from 'yapyak/config';
import { anthropic } from '@yapyak/anthropic';

export default defineConfig({
  translator: anthropic({
    apiKey: process.env.ANTHROPIC_API_KEY,
    glossary: { cart: { sv: 'kundvagn' } },
    voice: 'Concise and friendly'
  })
});
软件包工厂函数密钥不同的默认值
@yapyak/anthropic
anthropic({ apiKey })
ANTHROPIC_API_KEY
@yapyak/openai
openai({ apiKey })
OPENAI_API_KEY
额外提供
seed
、
organization
、
user
;可通过
endpoint
和
headers
使用其他兼容 OpenAI 的端点
@yapyak/gemini
gemini({ apiKey })
GEMINI_API_KEY
batchSize: 15
@yapyak/ollama
ollama()
无
endpoint: 'http://localhost:11434/api/generate'
、
model: 'llama3.1'
、
timeout: 120_000
、
maxRetries: 1
、
batchSize: 8
@yapyak/claude-code
claudeCode()
已登录的
claude
CLI
concurrency: 2
、
timeout: 120_000
;不支持 HTTP 选项;
model
会回退到 CLI 的设置
通用选项默认值作用
model
由提供商决定模型标识符
voice
无一到两句语气指导
glossary
{}
Record<source term, Record<locale, translation>>
;用于固定品牌名称、领域术语以及模型容易翻译错误的词
context
'minimal'
'none'
仅发送源字符串;
'rich'
会附加周围的代码
examples
5
,在
context: 'none'
时为
0
每次请求发送的现有翻译,用作风格参考
temperature
0.2
采样温度
maxTokens
由提供商决定输出 token 上限
timeout
30_000
单次请求超时时间,单位为毫秒
maxRetries
2
遇到 408、429、5xx 和网络故障时重试
batchSize
25
每次请求中的源字符串数量
concurrency
5
并行请求数量
headers
{}
额外的 HTTP 标头
endpoint
由提供商决定API URL
  • 无论
    context
    级别如何,都会发送
    t.as()
    的上下文。
  • 语气或术语表的变更仅影响新翻译。运行
    yapyak translate --force
    或
    yapyak retranslate "<source>"
    可传播变更。
  • 当 CI 中没有已登录的 CLI 时,可根据环境选择翻译器:
    process.env.CI ? anthropic({ apiKey: process.env.ANTHROPIC_API_KEY }) : claudeCode()
    。
自定义翻译器使用
yapyak/translator
中的
createTranslator({ id, translate })
:
  • translate({ items, signal, sourceLocale, targetLocales })
    需按项目顺序为每个条目返回一个对象,并以每个目标 locale 为键。
  • 将
    signal
    传递给请求。
  • 从
    yapyak/translator
    抛出
    TranslatorAuthError
    、
    TranslatorRateLimitError
    、
    TranslatorTimeoutError
    、
    TranslatorNetworkError
    、
    TranslatorSafetyError
    、
    TranslatorInvalidResponseError
    或
    TranslatorTruncatedError
    。
  • 在回调内部重试,yapyak 只会调用该回调一次。

CLI

CLI

Run through the package manager:
npx yapyak <command>
,
pnpm yapyak <command>
,
bunx yapyak <command>
. Every command except
info
walks the source files first, so a
t()
call added without the dev server running is still found.
CommandDoesExit code
yapyak add <locale> [<locale>...]
Creates the locale file, fills it through the translator, regenerates the
Locale
type. Several tags in one run share requests
non-zero on an unrecognized tag
yapyak translate [<locale>] [--force]
Fills empty stubs.
--force
re-translates every entry and overwrites hand-edits
1
without a translator
yapyak retranslate "<source>" [--locale <code>] [--as <ctx>] [--file <path>]
Re-translates one source string; the flags compose
0
when nothing matches
yapyak status [--json]
Coverage per locale
1
when a translation is missing
yapyak check
Missing translations, compile-time diagnostics, ICU mismatches
1
on any finding
yapyak clean [--write]
Lists orphans;
--write
deletes them
yapyak export [<locale>...] [--out <path>] [--split]
Snapshot keyed by locale, then path, then sourcenon-zero on an unknown tag
yapyak info
Versions for a bug report
0
Boolean flags accept
=
:
--write=false
.
NO_COLOR
and
CI
strip color.
CI order:
yapyak translate
with the provider key in the environment, then
yapyak check
, then the build.
check
never calls the translator.
通过包管理器运行:
npx yapyak <command>
、
pnpm yapyak <command>
、
bunx yapyak <command>
。除
info
外的每个命令都会先遍历源文件,因此即使未运行开发服务器,也能找到新增的
t()
调用。
命令作用退出码
yapyak add <locale> [<locale>...]
创建 locale 文件,通过翻译器填充内容,并重新生成
Locale
类型。一次运行中的多个标签会共用请求
遇到无法识别的标签时返回非零值
yapyak translate [<locale>] [--force]
填充空桩。
--force
会重新翻译所有条目并覆盖手动编辑
未配置翻译器时返回
1
yapyak retranslate "<source>" [--locale <code>] [--as <ctx>] [--file <path>]
重新翻译一条源字符串;各参数可组合使用没有匹配项时返回
0
yapyak status [--json]
显示各 locale 的覆盖率缺少翻译时返回
1
yapyak check
检查缺失的翻译、编译时诊断和 ICU 不匹配发现任何问题时返回
1
yapyak clean [--write]
列出孤儿条目;
--write
会将其删除
yapyak export [<locale>...] [--out <path>] [--split]
导出快照,依次按 locale、路径和源字符串作为键遇到未知标签时返回非零值
yapyak info
输出用于问题报告的版本信息
0
布尔参数支持使用
=
:例如
--write=false
。
NO_COLOR
和
CI
会禁用颜色输出。
CI 中的执行顺序:先运行
yapyak translate
并在环境中提供提供商密钥,然后运行
yapyak check
,最后执行构建。
check
不会调用翻译器。

Working in a project

在项目中工作

  1. Write the
    t()
    call in the component. With the dev server running, the save writes the stub and the translator fills it. Without it, the CLI finds the call.
  2. Run
    npx yapyak check
    before finishing. Fix every reported
    YAPnnnn
    with references/diagnostics.md.
  3. Run
    npx yapyak translate
    when
    check
    reports missing translations and the project has a translator. Fill the stubs by hand when it has none.
  4. Commit the locale files with the code. They are the source of truth;
    .yapyak/
    is a cache.
  5. Run
    npx yapyak clean
    after removing components, and
    --write
    once the list is right.
Diagnostics an agent most often triggers:
CodeCauseFix
YAP0002
Template literal source
t('Hi {name}', { name })
YAP0004
,
YAP0005
,
YAP0049
Params do not match the placeholdersMatch the keys to the
{names}
in the source
YAP0006
Params in a variableInline object literal
YAP0008
No
other
branch
Add
other {...}
YAP0018
t()
and
t.as()
for one source in one file
t.as()
on every occurrence
YAP0020
Chain stored in a variableComplete
t.in()
or
t.as()
inline
YAP0046
Branch name is not a CLDR keyword
zero
,
one
,
two
,
few
,
many
,
other
, or
=N
YAP0055
t()
at module scope
Move the call into the function that renders it
YAP0022
getLocale()
on the server without an adapter
Register the SSR adapter from the framework reference
  1. 在组件中编写
    t()
    调用。开发服务器正在运行时,保存操作会写入桩,并由翻译器填充;开发服务器未运行时,CLI 会找到该调用。
  2. 完成前运行
    npx yapyak check
    。根据 references/diagnostics.md 修复所有报告的
    YAPnnnn
    问题。
  3. 当
    check
    报告缺少翻译且项目配置了翻译器时,运行
    npx yapyak translate
    ;没有翻译器时,请手动填充桩。
  4. 将 locale 文件与代码一起提交。locale 文件是事实来源,
    .yapyak/
    只是缓存。
  5. 删除组件后运行
    npx yapyak clean
    ,确认列表无误后再执行
    --write
    。
Agent 最常触发的诊断:
代码原因修复方法
YAP0002
源文本使用模板字符串
t('Hi {name}', { name })
YAP0004
、
YAP0005
、
YAP0049
参数与占位符不匹配使键与源文本中的
{names}
保持一致
YAP0006
参数位于变量中改用内联对象字面量
YAP0008
缺少
other
分支
添加
other {...}
YAP0018
同一文件中的同一源文本同时使用
t()
和
t.as()
每次出现都使用
t.as()
YAP0020
链式调用存储在变量中内联完成
t.in()
或
t.as()
链式调用
YAP0046
分支名称不是 CLDR 关键词使用
zero
、
one
、
two
、
few
、
many
、
other
或
=N
YAP0055
在模块作用域中调用
t()
将调用移入渲染文本的函数中
YAP0022
服务器端调用
getLocale()
,但未配置适配器
根据框架参考文档注册 SSR 适配器