intlify / intlify/bundle-tools

Messages containing @ character are silently dropped in production build with JIT compilation

Open
#533 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
270
Forks
46
PR merge metrics
No merged PRs in 30d

Description

### Reporting a bug?

### Bug Description

When using `vue-i18n` with Vite in production mode, messages containing the `@` character are **silently removed** (return empty string) during the build process. This only occurs in production builds (`vite build`), while development mode (`vite dev`) works correctly.

The issue appears to be in the JIT compilation phase where static string analysis misidentifies `@` as ICU MessageFormat syntax, causing the entire message to be discarded without warning.

**Steps:**
1. Create a Vue 3 + vue-i18n (v9) + Vite project
2. Add a locale message containing `@`:
```typescript
// src/locales/zh-CN.ts
export default {
ddns: {
placeholder: {
domainRr: '如: www (为空则使用@)',
ipAddress: '请输入IP地址'
}
}
}
```

3. Use in component:
```vue


import { useI18n } from 'vue-i18n'
const { t } = useI18n()

```

4. **Development** (`npm run dev`): Both placeholders display correctly
5. **Production** (`npm run build` + `npm run preview`):
- `domainRr` input has **empty placeholder** (returns empty string)
- `ipAddress` input displays correctly

### Expected Behavior

The `@` character should be treated as literal text in production builds, consistent with development behavior. The message should display: `如: www (为空则使用@)`

### Actual Behavior

- **Development:** `t('ddns.placeholder.domainRr')` returns `'如: www (为空则使用@)'` ✅
- **Production:** `t('ddns.placeholder.domainRr')` returns `''` (empty string) ❌
- **No build errors or warnings are emitted**

### Environment

```json
{
"vue": "^3.5.24",
"vue-i18n": "^9.14.4",
"vite": "^7.2.4",
"@vitejs/plugin-vue": "^6.0.1",
"naive-ui": "^2.43.2",
"typescript": "~5.9.3"
}
```

- **Build Tool:** Vite 7.2.4
- **Vue I18n Version:** 9.14.4
- **Node Version:** [e.g., 20.x]
- **OS:** [e.g., Windows/macOS/Linux]

### Root Cause Analysis

After extensive debugging, the issue appears to be in the **JIT compilation phase**:

1. **Static Analysis Phase:** The JIT compiler statically analyzes message strings during build time to pre-compile them.
2. **ICU Syntax Misidentification:** The `@` character is used in ICU MessageFormat for argument selection (plural rules, etc.). The parser likely encounters `@` and treats it as ICU syntax, fails to parse it correctly (since it's just literal text), and **silently discards** the entire message.
3. **Why Function Works:** Arrow functions `() => 'text@text'` cannot be statically analyzed (not executed at build time), so they bypass the check:
```typescript
// ❌ Static string - analyzed and removed
domainRr: '如: www (为空则使用@)'

// ✅ Arrow function - preserved for runtime
domainRr: () => '如: www (为空则使用@)'
```

### Workarounds (Verified)

**Workaround 1: Arrow Function (Recommended)**
```typescript
export default {
ddns: {
placeholder: {
domainRr: () => '如: www (为空则使用@)' // Bypasses static analysis
}
}
}
```
Then in component:
```typescript
const result = t('ddns.placeholder.domainRr')
const text = typeof result === 'function' ? result() : result
```

**Workaround 2: Array Join**
```typescript
domainRr: ['如: www (为空则使用', '@', ')'].join('')
```

**Workaround 3: Disable JIT (Last Resort)**
```typescript
// vite.config.ts
import vueI18n from '@intlify/vite-plugin-vue-i18n'

export default {
plugins: [
vueI18n({
jitCompilation: false // Falls back to runtime compilation
})
]
}
```

### Suggested Fix

The JIT compiler should:
1. **Distinguish ICU syntax from literal text:** `@` should only be treated as special syntax when it's part of a valid ICU structure (e.g., `{count, plural, one {# item} other {# items}}`), not when it's just a character in a sentence.
2. **Fail loudly:** If the parser encounters what it thinks is invalid ICU syntax, it should emit a warning/error during build rather than silently dropping the message.

Related git changes: https://github.com/ProgramCX/FoxNAS-Web/commit/f76943efb2ec075aae407a02bb8e6da69113c007

### Expected behavior

The `@` character should be treated as literal text in production builds, consistent with development behavior. The message should display: `如: www (为空则使用@)`

### Reproduction

not fixed: https://github.com/ProgramCX/FoxNAS-Web/tree/e4964780959cc37041292f9e08e683771921d206
fixed: https://github.com/ProgramCX/FoxNAS-Web/tree/f76943efb2ec075aae407a02bb8e6da69113c007

### Issue Package

vite-plugin-vue-i18n

### System Info

```shell
System:
OS: Windows 11 10.0.26200
CPU: (20) x64 13th Gen Intel(R) Core(TM) i7-13700H
Memory: 1.20 GB / 15.72 GB
Binaries:
Node: 20.19.0 - D:\Program Files\nodejs\node.EXE
Yarn: 1.22.22 - C:\Users\Progr\AppData\Roaming\npm\yarn.CMD
npm: 10.8.2 - D:\Program Files\nodejs\npm.CMD
Browsers:
Chrome: 144.0.7559.133
Edge: Chromium (131.0.2903.146)
Internet Explorer: 11.0.26100.7296
npmPackages:
vite: ^7.2.4 => 7.3.1
vue: ^3.5.24 => 3.5.27
vue-i18n: ^9.14.4 => 9.14.4
```

### Screenshot

Image

### Additional context

- **Unicode escape fails:** Using `\u0040` instead of `@` also gets removed, suggesting the parser resolves escapes before ICU analysis.
- **Hardcoded strings work:** `` displays correctly, confirming it's not a UI framework issue (Naive UI handles `@` fine).
- **Silent failure:** The most critical aspect is that this fails **silently** - no build error, no runtime warning, just empty strings in production.

### Validations

- [x] Read the [Contributing Guidelines](https://github.com/intlify/bundle-tools/blob/main/CONTRIBUTING.md).
- [x] Read the README
- [x] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [x] Check that this is a concrete bug. For Q&A open a [GitHub Discussion](https://github.com/intlify/bundle-tools/discussions).

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.