Skip to content

Commit

Permalink
feat(unplugin-vue-i18n): support dynamic resource construction for js…
Browse files Browse the repository at this point in the history
… / ts formats (#241)

* feat(unplugin-vue-i18n): support dynamic resource construction for js / ts formats

* update docs
  • Loading branch information
kazupon committed Mar 13, 2023
1 parent 9b10508 commit fab2f86
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 12 deletions.
30 changes: 28 additions & 2 deletions packages/unplugin-vue-i18n/README.md
Expand Up @@ -277,14 +277,16 @@ This plugin will automatically select and bundle `petite-vue-i18n` build accordi
- ts
```

If nothing is specified for this option, i.e. `undefined`, nothing is done to the resource in the above format.

> ⚠️ NOTE:
`json` resources matches this option, it will be handled **before the internal json plugin of bundler, and will not be processed afterwards**, else the option doesn't match, the bundler side will handle.

> ⚠️ NOTE:
`yaml` resources don't support multi documentation with `|`, alias with `&` and `*`, tags with `! `, `@`, etc. Only simple data structures.

> ⚠️ NOTE:
`js` and `ts` resources are limited to **simple export (`export default`) as locale messages object only**, such as programmatically dynamic resource construction is not guaranteed to work currently.
`js` and `ts` resources are set **simple export (`export default`) as locale messages object, as default**.

```js
export default {
Expand All @@ -293,7 +295,31 @@ This plugin will automatically select and bundle `petite-vue-i18n` build accordi
}
```

If nothing is specified for this option, i.e. `undefined`, nothing is done to the resource in the above format.
If you need to use programmatically dynamic resource construction, you would be enable `allowDynamic` option. about details, see the section.

> ⚠️ NOTE:
If you use the `js` and `ts` resources formats, set the paths, so your application code is not targeted. We recommend that resources be isolated from the application code.

### `allowDynamic`

- **Type:** `boolean`
- **Default:** `false`

Whether or not programmatically dynamic resource construction for `js` or `ts` resource format.

In this case, you need to export the function with `export default` and construct the resource with the function:

```js
import resources from './locales/all.json'

export default async function loadResource(url) {
const res = await import(url).then(r => r.default || r)
return { ...resources, ...res }
}
```

If you fetch some resources from the backend, the data **must be pre-compiled** for production. exmaple is [here](https://github.com/intlify/vue-i18n-next/tree/master/examples/backend).


### `runtimeOnly`

Expand Down
2 changes: 1 addition & 1 deletion packages/unplugin-vue-i18n/package.json
Expand Up @@ -26,7 +26,7 @@
}
},
"dependencies": {
"@intlify/bundle-utils": "^5.0.0",
"@intlify/bundle-utils": "^5.1.2",
"@intlify/shared": "next",
"@rollup/pluginutils": "^5.0.2",
"@vue/compiler-sfc": "^3.2.47",
Expand Down
16 changes: 9 additions & 7 deletions packages/unplugin-vue-i18n/src/index.ts
Expand Up @@ -27,12 +27,7 @@ import { parseVueRequest, VueQuery } from './query'
import { createBridgeCodeGenerator } from './legacy'
import { getRaw, warn, error, raiseError } from './utils'

import type {
UnpluginContextMeta,
UnpluginOptions,
SourceMapCompact
} from 'unplugin'
import type { SourceMapInput } from 'rollup'
import type { UnpluginContextMeta, UnpluginOptions } from 'unplugin'
import type { PluginOptions } from './types'
import type { CodeGenOptions, DevEnv } from '@intlify/bundle-utils'

Expand Down Expand Up @@ -125,6 +120,8 @@ export const unplugin = createUnplugin<PluginOptions>((options = {}, meta) => {
const esm = isBoolean(options.esm) ? options.esm : true
debug('esm', esm)

const allowDynamic = !!options.allowDynamic

let isProduction = false
let sourceMap = false

Expand Down Expand Up @@ -296,6 +293,7 @@ export const unplugin = createUnplugin<PluginOptions>((options = {}, meta) => {
inSourceMap,
isGlobal: globalSFCScope,
useClassComponent,
allowDynamic,
bridge,
exportESM: esm,
forceStringify
Expand Down Expand Up @@ -501,6 +499,7 @@ export const unplugin = createUnplugin<PluginOptions>((options = {}, meta) => {
inSourceMap,
isGlobal: globalSFCScope,
useClassComponent,
allowDynamic,
bridge,
exportESM: esm,
forceStringify
Expand Down Expand Up @@ -743,14 +742,16 @@ function getOptions(
isGlobal = false,
bridge = false,
exportESM = true,
useClassComponent = false
useClassComponent = false,
allowDynamic = false
}: {
inSourceMap?: RawSourceMap
forceStringify?: boolean
isGlobal?: boolean
bridge?: boolean
exportESM?: boolean
useClassComponent?: boolean
allowDynamic?: boolean
}
): Record<string, unknown> {
const mode: DevEnv = isProduction ? 'production' : 'development'
Expand All @@ -761,6 +762,7 @@ function getOptions(
inSourceMap,
forceStringify,
useClassComponent,
allowDynamic,
bridge,
exportESM,
env: mode,
Expand Down
1 change: 1 addition & 0 deletions packages/unplugin-vue-i18n/src/types.ts
@@ -1,6 +1,7 @@
export type SFCLangFormat = 'json' | 'json5' | 'yml' | 'yaml'
export interface PluginOptions {
include?: string | string[]
allowDynamic?: boolean
runtimeOnly?: boolean
compositionOnly?: boolean
fullInstall?: boolean
Expand Down
1 change: 1 addition & 0 deletions packages/unplugin-vue-i18n/test/utils.ts
Expand Up @@ -153,6 +153,7 @@ export async function bundleAndRun(
options.sourcemap = isBoolean(options.sourcemap) || false
options.useClassComponent = isBoolean(options.useClassComponent) || false
options.bridge = isBoolean(options.bridge) || false
options.allowDynamic = isBoolean(options.allowDynamic) || false

const { code, map } = await bundler(fixture, options)

Expand Down
@@ -1,5 +1,6 @@
import { resolve } from 'pathe'
import { bundleVite, bundleAndRun } from '../utils'
import { isFunction } from '@intlify/shared'
import { createMessageContext } from '@intlify/core-base'

const options = {
Expand Down Expand Up @@ -48,3 +49,11 @@ test('ts resource', async () => {
// expect(fn.source).toEqual(`@.caml:{'no apples'} | {0} apple | {n} apples`)
expect(fn(createMessageContext({ named: { n: 3 } }))).toEqual(`3 apples`)
})

test('dynamical resource with js / ts', async () => {
const { module } = await bundleAndRun('ka-JP.ts', bundleVite, {
allowDynamic: true,
...options
})
expect(isFunction(module)).toBe(true)
})
4 changes: 2 additions & 2 deletions yarn.lock
Expand Up @@ -969,7 +969,7 @@ __metadata:
languageName: unknown
linkType: soft

"@intlify/bundle-utils@^5.0.0, @intlify/bundle-utils@workspace:packages/bundle-utils":
"@intlify/bundle-utils@^5.1.2, @intlify/bundle-utils@workspace:packages/bundle-utils":
version: 0.0.0-use.local
resolution: "@intlify/bundle-utils@workspace:packages/bundle-utils"
dependencies:
Expand Down Expand Up @@ -1094,7 +1094,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@intlify/unplugin-vue-i18n@workspace:packages/unplugin-vue-i18n"
dependencies:
"@intlify/bundle-utils": ^5.0.0
"@intlify/bundle-utils": ^5.1.2
"@intlify/shared": next
"@rollup/pluginutils": ^5.0.2
"@vue/compiler-sfc": ^3.2.47
Expand Down

0 comments on commit fab2f86

Please sign in to comment.