{"id":51567628,"url":"https://github.com/beixiyo/vv-i18n.nvim","last_synced_at":"2026-07-10T16:01:44.519Z","repository":{"id":367026146,"uuid":"1278507410","full_name":"beixiyo/vv-i18n.nvim","owner":"beixiyo","description":"Inline i18n preview / jump / sync-edit / missing-key report for TS/JS, all tree-sitter. TS/JS i18n 行内预览 / 跳转 / 同步改值 / 补缺失，全 tree-sitter","archived":false,"fork":false,"pushed_at":"2026-06-24T07:56:14.000Z","size":54,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-24T10:32:43.305Z","etag":null,"topics":["i18n","i18next","internationalization","l10n","locale","lua","neovim","neovim-plugin","nvim","nvim-plugin","translation","tree-sitter"],"latest_commit_sha":null,"homepage":null,"language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/beixiyo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-23T21:29:11.000Z","updated_at":"2026-06-24T07:56:19.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/beixiyo/vv-i18n.nvim","commit_stats":null,"previous_names":["beixiyo/vv-i18n.nvim"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/beixiyo/vv-i18n.nvim","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beixiyo%2Fvv-i18n.nvim","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beixiyo%2Fvv-i18n.nvim/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beixiyo%2Fvv-i18n.nvim/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beixiyo%2Fvv-i18n.nvim/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/beixiyo","download_url":"https://codeload.github.com/beixiyo/vv-i18n.nvim/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beixiyo%2Fvv-i18n.nvim/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35335898,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-10T02:00:06.465Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["i18n","i18next","internationalization","l10n","locale","lua","neovim","neovim-plugin","nvim","nvim-plugin","translation","tree-sitter"],"created_at":"2026-07-10T16:01:28.778Z","updated_at":"2026-07-10T16:01:44.478Z","avatar_url":"https://github.com/beixiyo.png","language":"Lua","funding_links":[],"categories":[],"sub_categories":[],"readme":"# vv-i18n.nvim\n\nTS/TSX i18n 的**行内预览 / 跳转定义 / 同步改值 / 补缺失语言**，对标 _lokalise_ · _i18n-ally_\n\n面向通用工具够不着的项目：locale 是 TS/JS 模块具名导出（或 JSON），运行时把多个文件合并到\n命名空间根下、hook 注入前缀。**locale 来源 / 文件→语言 / 文件→命名空间 / 调用点→前缀\n均「字面量或函数」可配**，默认中性，内核全 tree-sitter\n\n## 先对号入座：你的项目是哪种布局？\n\n| 布局         | 命名空间在哪         | 例子                                    | 全键             | 配 `mount`   |\n| ------------ | -------------------- | --------------------------------------- | ---------------- | ------------ |\n| **top-key**  | 文件**内容**顶层 key | `Hero/zh-CN.ts` = `{ hero: { title } }` | `hero.title`     | `'top-key'`  |\n| **filename** | **文件名 / 路径**    | `en/common.json` = `{ ok }`             | `common.ok`      | `'filename'` |\n| **flat**     | 无                   | `zh-CN.ts` = `{ greeting: { hello } }`  | `greeting.hello` | `'flat'`     |\n\n## 安装（PackSpec / lazy.nvim）\n\n```lua\n{\n  url = 'beixiyo/vv-i18n.nvim',\n  main = 'vv-i18n',\n  dependencies = { 'beixiyo/vv-utils.nvim' },\n  ft = { 'typescript', 'typescriptreact', 'javascript', 'javascriptreact' },\n  opts = {\n    sources = { { prefix = '', discover = { 'src/locales' }, mount = 'flat', namespace = 'flat' } },\n  },\n}\n```\n\n\u003e `sources` 默认为空 → 不激活；按你的布局配好才生效，非匹配项目零噪声\n\n## 按布局对照配置（找到你的文件树，抄对应那段）\n\n\u003e locale 文件格式：**JS / TS 对象导出 + JSON**（`export const x = {...} (as const)` / `export default {...}` / `module.exports` / 裸 `{...}` / `.json`）。其它格式（YAML / PO 等）传 `parse` 自定义解析函数读入（见下「自定义解析」）\n\n### top-key —— 命名空间在文件**内容**顶层 key\n\n```\npackages/ui/src/\n  components/Hero/locales/\n    zh-CN.ts   = { hero: { title: '英雄' } }   → 全键  ui.hero.title\n    en-US.ts\n  i18n/common/{zh-CN,en-US}.ts                 → 全键  ui.common.*\n```\n\n```ts\nconst t = useUiT()                            // hook 注入前缀 ui\nt('hero.title')   // Hero/locales/zh-CN.ts  { hero: { title } }  →  ui.hero.title\n```\n\n```lua\n{\n  prefix    = 'ui',\n  root      = 'packages/ui/src',\n  discover  = { 'components/*/locales', 'i18n/common' },\n  mount     = 'top-key',\n  namespace = 'two-level',   -- useUiT() 注入前缀 ui\n  lang      = '{lang}.ts',\n  hooks     = { 'useUiT' },\n}\n```\n\n### filename —— 命名空间在**文件名 / 路径**（键在文件根，react-i18next 标准）\n\n```\nsrc/locales/\n  en-US/common.json = { ok: 'OK' }   → 全键  common.ok\n  en-US/home.json                    → 全键  home.*\n  zh-CN/common.json\n```\n\n```ts\nconst { t } = useTranslation('common')        // hook 参数 = 命名空间\nt('ok')   // locales/zh-CN/common.json  { ok }  →  common.ok\n```\n\n```lua\n{\n  prefix    = '',\n  root      = 'src/locales',\n  lang      = '{lang}/{ns}.json',   -- en-US/common.json → lang=en-US, ns=common\n  mount     = 'filename',\n  namespace = 'hook-arg',\n  hooks     = { 'useTranslation' },\n}\n```\n\n### flat —— **无**命名空间\n\n```\nlocales/\n  zh-CN.ts = { greeting: { hello: '你好' } }   → 全键  greeting.hello\n  en-US.ts\n```\n\n```ts\nconst { t } = useTranslation()\nt('greeting.hello')   // locales/zh-CN.ts  { greeting: { hello } }  →  greeting.hello\n```\n\n```lua\n{\n  prefix    = '',\n  discover  = { 'locales' },\n  mount     = 'flat',\n  namespace = 'flat',\n  lang      = '{lang}.ts',\n}\n```\n\n### mono-repo —— 一包一源，各自规则（混用上面任意布局）\n\n```lua\nsources = {\n  { prefix = 'web', root = 'apps/web',        discover = { 'src/locales' },         mount = 'flat',     namespace = 'flat' },\n  { prefix = 'ui',  root = 'packages/ui/src', discover = { 'components/*/locales' }, mount = 'top-key',  namespace = 'two-level', hooks = { 'useUiT' } },\n}\n```\n\n## 四个可配轴（每个都「字面量 | 函数」）\n\n| 轴          | 作用            | 字面量                                              | 函数                       |\n| ----------- | --------------- | --------------------------------------------------- | -------------------------- |\n| `discover`  | 找 locale 目录  | `{ 'components/*/locales' }`（glob，相对 `root`）   | `fn(root) -\u003e dirs`         |\n| `lang`      | 文件 → 语言码   | `'{lang}.ts'` / `'{lang}/{ns}.json'` / 数组         | `fn(path) -\u003e 'zh-CN'\\|nil` |\n| `mount`     | 文件 → 命名空间 | `'top-key'` / `'filename'` / `'flat'`               | `fn(ctx) -\u003e ns`            |\n| `namespace` | 调用点 → 前缀   | `'flat'` / `'hook-arg'` / `'fixed'` / `'two-level'` | `fn(ctx) -\u003e prefix\\|nil`   |\n\n`namespace` 字面量含义：\n\n- `flat`：`t('x')` → `x`\n- `hook-arg`：`useXxx('common')` → 前缀 `common`（`t('ok')` → `common.ok`）\n- `fixed`：固定用 source 的 `prefix`，忽略 hook 参数\n- `two-level`：`prefix[.\u003chook 参数\u003e]`\n\n\u003e `prefix` 只写在 source 上，索引侧与调用侧共用（单一真相）。`mount` 与 `namespace` 须配成对\n\u003e （上方每行都成对）\n\n## 全部配置\n\n```lua\nrequire('vv-i18n').setup({\n  root = nil,                          -- nil = 自动探测\n\n  -- 全局默认（每个 source 可覆盖同名字段）\n  hooks = { 'useTranslation' },\n  t = { 't' },\n  lang = { '{lang}.ts', '{lang}.json' },\n  mount = 'top-key',\n  namespace = 'hook-arg',\n\n  sources = { --[[ 见上 ]] },\n\n  -- 高级\n  namespace_separator = ':',           -- 绝对命名空间 ns\u003csep\u003ekey；'' 关闭\n  key_separator = '.',\n  quote_style = 'auto',                -- 写回引号 single|double|auto\n  indent = nil,                        -- 写回缩进，nil=推断\n  project_config = true,               -- 探测项目根 .vv-i18n.lua（见下）\n  parse = nil,                         -- 自定义读侧解析（非 JS/JSON 格式，见下）\n\n  display = {\n    enable = true,\n    preferred_langs = {},              -- 预览首选语言（空=字典序首个）\n    max_width = 40,\n    icon = '󰗊 ',                       -- 译文前缀图标\n    -- 样式：style 直接定义 { fg=, bg=, italic=, bold= }；不给则默认 注释色 + 斜体（随主题）\n    style = nil,                       -- 译文样式（覆盖 hl）\n    missing_style = nil,               -- 缺失样式\n    -- 也可只换高亮组：hl / missing_hl；或 lang（固定预览语言）/ missing_icon\n    render = nil,                      -- 函数完全自定义渲染（见下）\n  },\n})\n```\n\n\u003e 预览模式：**conceal 就地替换** —— 隐藏 `t('key')` 里的字符串、inline 插入「图标 + 译文」，\n\u003e **长度随译文动态变化**（代码会随之重排）；**光标落在该 `t()` 上自动还原**原文（token 级，同行多个互不干扰）\n\u003e 缺失键显示 `⚠ 键`。依赖 conceal：开启时给匹配窗口设 `conceallevel=2` / `concealcursor`，关闭时还原\n\n### 自定义渲染 `display.render`\n\n收上下文、返回字符串或 virt_text chunks（`nil` 落默认）：\n\n```lua\ndisplay = {\n  render = function(ctx)\n    -- ctx = { full_key, value, lang, kind, missing, per, literal, icon, hl, max_width }\n    if ctx.missing then return { { '✗ ' .. ctx.literal, 'Error' } } end\n    return { { ctx.icon, 'Comment' }, { ctx.value, 'String' } }  -- 图标与译文分色\n  end,\n}\n```\n\n## 项目级配置 `.vv-i18n.lua`\n\n项目根放一个 `.vv-i18n.lua`，**整份覆盖** nvim 里的配置（nvim setup 仅作默认/兜底）。这样一份 nvim 配置\n即可适配多个项目，各自带精确 source：\n\n```lua\n-- \u003c项目根\u003e/.vv-i18n.lua\nreturn {\n  sources = {\n    { prefix = '', discover = { 'src/locales' }, mount = 'filename', namespace = 'hook-arg' },\n  },\n}\n```\n\n- 从当前文件向上找 `.vv-i18n.lua`；找到则**全覆盖**，找不到回退 nvim 基线\n- **安全**：经 Neovim 内置 `vim.secure` —— 首次加载弹窗确认信任，内容改动后重新确认（信任记录在\n  `stdpath('state')/trust`，由 nvim 维护）\n- 切目录（`:cd`）自动重载；或随时 `:VVI18nReload`。关掉用 `project_config = false`\n\n## 自定义解析 `parse`（读侧，任意格式）\n\n默认解析器只认 JS/TS 对象 + JSON。其它格式（YAML / `.properties` / PO …）传一个 `parse` 函数（全局或 per-source）：收文件内容、返回叶子列表,即得**预览 / 跳转 / 完整度**\n\n```lua\n-- 例：key=value 行格式\nparse = function(content, path)\n  local leaves, top, row = {}, {}, 0\n  for line in (content .. '\\n'):gmatch('([^\\n]*)\\n') do\n    local k, v = line:match('^%s*([%w%.]+)%s*=%s*(.-)%s*$')\n    if k then\n      top[#top + 1] = k\n      leaves[#leaves + 1] = { path = { k }, dotted = k, kind = 'string', value = v, row = row, col = 0 }\n    end\n    row = row + 1\n  end\n  return { leaves = leaves, top_keys = top }\nend\n```\n\n- 返回 `{ leaves = VVI18nLeaf[], top_keys? }`;`leaf = { path=string[], dotted, kind='string', value, row, col }`(`row/col` 0-based,供跳转)\n- **仅读侧**。写回（`:VVI18nEdit` / `AddKey` / `SetValue`）仍是 tree-sitter 字节段引擎,对自定义格式不可用\n\n## 命令\n\n| 命令                               | 作用                           |\n| ---------------------------------- | ------------------------------ |\n| `:VVI18nKeys`                      | 键浏览 / 完整度 / 同步编辑面板 |\n| `:VVI18nMissing`                   | 仅缺失 key，按缺失语言分组     |\n| `:VVI18nEdit`                      | 光标处键的多语言同步编辑浮窗   |\n| `:VVI18nInfo`                      | 光标处键各语言译文             |\n| `:VVI18nJump`                      | 跳到 locale 定义               |\n| `:VVI18nSetValue`                  | 改某语言值（单语言快速）       |\n| `:VVI18nAddKey`                    | 补缺失语言                     |\n| `:VVI18nReload`                    | 重建索引                       |\n| `:VVI18n[Enable\\|Disable\\|Toggle]` | 行内预览开关                   |\n\n## 测试\n\n```bash\nbash tests/run.sh\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeixiyo%2Fvv-i18n.nvim","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbeixiyo%2Fvv-i18n.nvim","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeixiyo%2Fvv-i18n.nvim/lists"}