Skip to content
This repository was archived by the owner on Jul 4, 2021. It is now read-only.

Commit 69ca128

Browse files
authored
feat: static bundle importing (#50)
1 parent f863c1f commit 69ca128

File tree

9 files changed

+164
-25
lines changed

9 files changed

+164
-25
lines changed

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,56 @@ ja:
125125
</i18n>
126126
```
127127

128+
### Static bundle importing
129+
130+
vite-plugin-vue-i18n allows you to statically bundle i18n resources such as `json` or `yaml` specified by the [`include` option](#include) of the plugin described below as locale messages with the `import` syntax.
131+
132+
In this case, only one i18n resource can be statically bundled at a time with `import` syntax, so the these code will be redundant for multiple locales.
133+
134+
```js
135+
import { createApp } from 'vue'
136+
import { createI18n } from 'vue-i18n'
137+
/*
138+
* The i18n resources in the path specified in the plugin `include` option can be read
139+
* as vue-i18n optimized locale messages using the import syntax
140+
*/
141+
import en from './src/locales/en.json'
142+
import ja from './src/locales/ja.yaml'
143+
import fr from './src/locales/fr.json5'
144+
145+
const i18n = createI18n({
146+
locale: 'en',
147+
messages: {
148+
en,
149+
ja,
150+
fr
151+
}
152+
})
153+
154+
const app = createApp()
155+
app.use(i18n).mount('#app)
156+
```
157+
158+
vite-plugin-vue-i18n can use the vite (rollup) mechanism to import all locales at once, using the special identifier `@intlify/vite-plugin-vue-i18n/messages`, as the bellow:
159+
160+
```js
161+
import { createApp } from 'vue'
162+
import { createI18n } from 'vue-i18n'
163+
/*
164+
* All i18n resources specified in the plugin `include` option can be loaded
165+
* at once using the import syntax
166+
*/
167+
import messages from '@intlify/vite-plugin-vue-i18n/messages'
168+
169+
const i18n = createI18n({
170+
locale: 'en',
171+
messages
172+
})
173+
174+
const app = createApp()
175+
app.use(i18n).mount('#app)
176+
```
177+
128178
### Bundle optimizations
129179

130180
vite-plugin-vue-i18n allows you to support bundle size optimization provided by vue-i18n.

examples/composition/main.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
import { createApp } from 'vue'
22
import { createI18n } from 'vue-i18n'
33
import App from './App.vue'
4-
5-
import ja from './locales/ja.json5'
6-
import en from './locales/en.yaml'
4+
import messages from '@intlify/vite-plugin-vue-i18n/messages'
75

86
const i18n = createI18n({
97
legacy: false,
108
locale: 'ja',
11-
messages: {
12-
en,
13-
ja
14-
}
9+
messages
1510
})
1611

1712
const app = createApp(App)

examples/legacy/main.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
import { createApp } from 'vue'
22
import { createI18n } from 'vue-i18n'
33
import App from './App.vue'
4-
5-
import ja from './locales/ja.json'
6-
import en from './locales/en.yaml'
4+
import messages from '@intlify/vite-plugin-vue-i18n/messages'
75

86
const i18n = createI18n({
97
legacy: true,
108
locale: 'ja',
11-
messages: {
12-
en,
13-
ja
14-
}
9+
messages
1510
})
1611

1712
const app = createApp(App)

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
}
2525
},
2626
"dependencies": {
27-
"@intlify/cli": "^0.1.2",
27+
"@intlify/cli": "^0.2.0",
2828
"@intlify/shared": "^9.0.0-beta.16",
2929
"@rollup/pluginutils": "^4.1.0"
3030
},
@@ -43,6 +43,7 @@
4343
"eslint-plugin-prettier": "^3.3.0",
4444
"eslint-plugin-vue": "^7.3.0",
4545
"eslint-plugin-vue-libs": "^4.0.0",
46+
"fast-glob": "^3.2.5",
4647
"jest": "^26.6.3",
4748
"jest-puppeteer": "^4.4.0",
4849
"jest-watch-typeahead": "^0.6.1",

src/index.ts

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import path from 'path'
1313
import { isArray, isBoolean, isEmptyObject, isString } from '@intlify/shared'
1414
import { createFilter } from '@rollup/pluginutils'
1515
import { generateJSON, generateYAML } from '@intlify/cli'
16+
import fg from 'fast-glob'
1617
import { debug as Debug } from 'debug'
1718
import { parseVueRequest } from './query'
1819
import { normalizePath } from 'vite'
@@ -23,6 +24,8 @@ import type { VitePluginVueI18nOptions } from './options'
2324

2425
const debug = Debug('vite-plugin-vue-i18n')
2526

27+
const INTLIFY_BUNDLE_IMPORT_ID = '@intlify/vite-plugin-vue-i18n/messages'
28+
2629
function pluginI18n(
2730
options: VitePluginVueI18nOptions = { forceStringify: false }
2831
): Plugin {
@@ -100,6 +103,30 @@ function pluginI18n(
100103
}
101104
},
102105

106+
resolveId(id: string) {
107+
if (id === INTLIFY_BUNDLE_IMPORT_ID) {
108+
return id
109+
}
110+
},
111+
112+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
113+
async load(id: string, ssr: boolean) {
114+
if (id === INTLIFY_BUNDLE_IMPORT_ID && include) {
115+
let resourcePaths = [] as string[]
116+
const includePaths = isArray(include) ? include : [include]
117+
for (const inc of includePaths) {
118+
resourcePaths = [...(await fg(inc))]
119+
}
120+
// TODO: source-map
121+
const code = await generateBundleResources(
122+
resourcePaths,
123+
config != null ? config.isProduction : false,
124+
options.forceStringify!
125+
)
126+
return Promise.resolve(code)
127+
}
128+
},
129+
103130
async transform(code: string, id: string) {
104131
const { filename, query } = parseVueRequest(id)
105132
debug('transform', id, JSON.stringify(query))
@@ -120,7 +147,7 @@ function pluginI18n(
120147
// `.json` is handled default in vite, and it's transformed to JS object.
121148
let _source = code
122149
if (langInfo === '.json') {
123-
_source = await getRawJSON(id)
150+
_source = await getRaw(id)
124151
}
125152
const generate = /\.?json5?/.test(langInfo)
126153
? generateJSON
@@ -159,7 +186,7 @@ function pluginI18n(
159186
}
160187
}
161188

162-
async function getRawJSON(path: string): Promise<string> {
189+
async function getRaw(path: string): Promise<string> {
163190
return fs.readFile(path, { encoding: 'utf-8' })
164191
}
165192

@@ -208,6 +235,35 @@ function getOptions(
208235
}
209236
}
210237

238+
async function generateBundleResources(
239+
resources: string[],
240+
isProduction: boolean,
241+
forceStringify: boolean
242+
) {
243+
const codes = []
244+
for (const res of resources) {
245+
debug(`${res} bundle loading ...`)
246+
if (/\.(json5?|ya?ml)$/.test(res)) {
247+
const { ext, name } = path.parse(res)
248+
const source = await getRaw(res)
249+
const generate = /json5?/.test(ext) ? generateJSON : generateYAML
250+
const parseOptions = getOptions(
251+
res,
252+
isProduction,
253+
{},
254+
forceStringify
255+
) as CodeGenOptions
256+
parseOptions.type = 'bare'
257+
const { code } = generate(source, parseOptions)
258+
debug('generated code', code)
259+
codes.push(`${JSON.stringify(name)}: ${code}`)
260+
}
261+
}
262+
return `export default {
263+
${codes.join(`,\n`)}
264+
}`
265+
}
266+
211267
// overwrite for cjs require('...')() usage
212268
export default pluginI18n
213269
export const vueI18n = pluginI18n

test/bundle-import.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import path from 'path'
2+
import { bundleAndRun } from './utils'
3+
import { createMessageContext } from '@intlify/runtime'
4+
5+
const options = {
6+
input: './fixtures/bundle.ts',
7+
include: [path.resolve(__dirname, './fixtures/locales/**')]
8+
}
9+
10+
test('import', async () => {
11+
const { exports: messages } = await bundleAndRun(
12+
'@intlify/vite-plugin-vue-i18n/messages',
13+
options
14+
)
15+
;['en', 'fr', 'ja', 'ko'].forEach(locale => {
16+
const fn = messages[locale].message
17+
expect(fn(createMessageContext({ named: { n: 3 } }))).toEqual(`3 apples`)
18+
})
19+
})

test/fixtures/bundle.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import messages from '@intlify/vite-plugin-vue-i18n/messages'
2+
3+
if (typeof window !== 'undefined') {
4+
window.module = messages
5+
window.exports = messages
6+
}

test/utils.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,17 @@ async function bundle(fixture: string, options: Record<string, unknown> = {}) {
1616
? 'info'
1717
: 'silent'
1818
: 'silent'
19+
20+
const alias: Record<string, string> = {
21+
vue: 'vue/dist/vue.runtime.esm-browser.js'
22+
}
23+
if (!fixture.startsWith('@')) {
24+
alias['~target'] = path.resolve(__dirname, target, fixture)
25+
}
26+
1927
const result = await build({
2028
logLevel: silent,
21-
alias: {
22-
'~target': path.resolve(__dirname, target, fixture),
23-
vue: 'vue/dist/vue.runtime.esm-browser.js'
24-
},
29+
alias,
2530
plugins: [vue(), vueI18n({ include })],
2631
build: {
2732
write: false,

yarn.lock

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -333,10 +333,10 @@
333333
dependencies:
334334
"@hapi/hoek" "^8.3.0"
335335

336-
"@intlify/cli@^0.1.2":
337-
version "0.1.2"
338-
resolved "https://registry.yarnpkg.com/@intlify/cli/-/cli-0.1.2.tgz#28097f12451491ec8797d2c09390293000c9eddb"
339-
integrity sha512-MMi/wUEoTE0oBtTkaospJ2TGPMl0TPKmuN4L6KFhLkBM9Yw0tCN0pDHknlX78tyiI55wYbutddbxkEVHZT1RIw==
336+
"@intlify/cli@^0.2.0":
337+
version "0.2.0"
338+
resolved "https://registry.yarnpkg.com/@intlify/cli/-/cli-0.2.0.tgz#631f4ab2e97cfe2848e2e96aa38e6681316265ed"
339+
integrity sha512-KIB6KggamrUoQJXa2CZad9D0dIH6FijPpRv020VtXvPFphprJKipG19GHndPgKRPK70t6Aj7zsJEqt5GvljKXg==
340340
dependencies:
341341
"@intlify/core" "^9.0.0-beta.16"
342342
"@intlify/core-base" "^9.0.0-beta.16"
@@ -2796,6 +2796,18 @@ fast-glob@^3.0.3, fast-glob@^3.1.1:
27962796
micromatch "^4.0.2"
27972797
picomatch "^2.2.1"
27982798

2799+
fast-glob@^3.2.5:
2800+
version "3.2.5"
2801+
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661"
2802+
integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==
2803+
dependencies:
2804+
"@nodelib/fs.stat" "^2.0.2"
2805+
"@nodelib/fs.walk" "^1.2.3"
2806+
glob-parent "^5.1.0"
2807+
merge2 "^1.3.0"
2808+
micromatch "^4.0.2"
2809+
picomatch "^2.2.1"
2810+
27992811
[email protected], fast-json-stable-stringify@^2.0.0:
28002812
version "2.1.0"
28012813
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"

0 commit comments

Comments
 (0)