Skip to content

Commit

Permalink
feat(unplugin-vue-i18n): allow for a custom i18n block transform hook (
Browse files Browse the repository at this point in the history
…#387)

* implement webpack custom block transform

* remove debug code

* remove json generate modifications

* add vite example

* remove vestigial debug code and adjust types

* remove unused value from json.ts

* add readme documentation for transformI18nBlock
  • Loading branch information
dgautsch authored Jul 23, 2024
1 parent b576904 commit 3756bc9
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 8 deletions.
3 changes: 3 additions & 0 deletions examples/vite/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@
<p id="msg">{{ t('hello') }}</p>
<p id="custom-directive" v-t="'hi'"></p>
<Banana />
<Apple />
</template>

<script>
import { useI18n } from 'vue-i18n'
import Apple from './Apple.vue'
import Banana from './Banana.vue'
export default {
name: 'App',
components: {
Apple,
Banana
},
setup() {
Expand Down
26 changes: 26 additions & 0 deletions examples/vite/src/Apple.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<template>
<p>{{ t('language') }}</p>
<p>{{ t('hello') }}</p>
</template>

<script>
import { useI18n } from 'vue-i18n'
export default {
name: 'Apple',
setup() {
const { t } = useI18n({
inheritLocale: true,
useScope: 'local'
})
return { t }
}
}
</script>

<i18n>
[
"language",
"hello",
]
</i18n>
24 changes: 23 additions & 1 deletion examples/vite/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,27 @@ import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueI18n from '../../packages/unplugin-vue-i18n/src/vite'

function transformI18nBlock(source) {
const sourceCopy = source
const block = JSON.parse(
sourceCopy.replace(/[\n\s]/g, '').replace(/,\]$/, ']')
)
if (Array.isArray(block)) {
const transformedBlock = JSON.stringify({
en: {
language: 'Language',
hello: 'hello, world!'
},
ja: {
language: '言語',
hello: 'こんにちは、世界!'
}
})
return transformedBlock
}
return source
}

export default defineConfig({
resolve: {
alias: {
Expand All @@ -21,7 +42,8 @@ export default defineConfig({
vue(),
vueI18n({
include: path.resolve(__dirname, './src/locales/**'),
optimizeTranslationDirective: true
optimizeTranslationDirective: true,
transformI18nBlock: transformI18nBlock
})
]
})
3 changes: 3 additions & 0 deletions examples/webpack/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@
</form>
<p>{{ t('hello') }}</p>
<Banana />
<Apple />
</template>

<script>
import { useI18n } from 'vue-i18n'
import Apple from './Apple.vue'
import Banana from './Banana.vue'
export default {
name: 'App',
components: {
Apple,
Banana
},
setup() {
Expand Down
26 changes: 26 additions & 0 deletions examples/webpack/src/Apple.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<template>
<p>{{ t('language') }}</p>
<p>{{ t('hello') }}</p>
</template>

<script>
import { useI18n } from 'vue-i18n'
export default {
name: 'Apple',
setup() {
const { t } = useI18n({
inheritLocale: true,
useScope: 'local'
})
return { t }
}
}
</script>

<i18n>
[
"language",
"hello",
]
</i18n>
24 changes: 23 additions & 1 deletion examples/webpack/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@ const path = require('path')
const { VueLoaderPlugin } = require('vue-loader')
const VueI18nPlugin = require('../../packages/unplugin-vue-i18n/lib/webpack.cjs')

function transformI18nBlock(source) {
const sourceCopy = source
const block = JSON.parse(
sourceCopy.replace(/[\n\s]/g, '').replace(/,\]$/, ']')
)
if (Array.isArray(block)) {
const transformedBlock = JSON.stringify({
en: {
language: 'Language',
hello: 'hello, world!'
},
ja: {
language: '言語',
hello: 'こんにちは、世界!'
}
})
return transformedBlock
}
return source
}

module.exports = {
mode: 'development',
devtool: 'source-map',
Expand Down Expand Up @@ -39,7 +60,8 @@ module.exports = {
plugins: [
new VueLoaderPlugin(),
VueI18nPlugin({
include: [path.resolve(__dirname, './src/locales/**')]
include: [path.resolve(__dirname, './src/locales/**')],
transformI18nBlock: transformI18nBlock
})
]
}
1 change: 1 addition & 0 deletions packages/bundle-utils/src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export interface CodeGenOptions {
escapeHtml?: boolean
jit?: boolean
minify?: boolean
transformI18nBlock?: (source: string | Buffer) => string
onWarn?: (msg: string) => void
onError?: (
msg: string,
Expand Down
47 changes: 47 additions & 0 deletions packages/unplugin-vue-i18n/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,53 @@ If do you will use this option, you need to enable `jitCompilation` option.
> [!WARNING]
About for manually signature, see the details [vue-i18n-extensions API docs](https://github.com/intlify/vue-i18n-extensions/blob/next/docs/%40intlify/vue-i18n-extensions-api.md#translationsignatures) and [usecase from vue-i18n-extensions PR](https://github.com/intlify/vue-i18n-extensions/pull/217/files#diff-3fb9543f91e011d4b0dc9beff44082fe1a99c9eab70c1afab23c3c34352b7c38R121-R200)
### `transformI18nBlock`
- **Type**: `function`
- **Default:** `undefined`
This hook allows a user to modify the `<i18n>` block before the plugin generates the translations. The hook is passed the source of the `<ii8n>` block as a `string` after the SFC is read from disk.
**Plugin**
```javascript
function transformI18nBlock(source) {
// transformation logic
}
// Plugin
vueI18n({
transformI18nBlock
})
```
**Before**
```html
<i18n>
[
'slug-one',
'slug-two'
]
<i18n>
```
**After**
```html
<i18n>
{
'en': {
'slug-one': 'foo',
'slug-two': 'bar'
},
ja: {
'slug-one': 'foo',
'slug-two': 'bar'
}
}
</i18n>
```
> [!IMPORTANT]
The function **must** return a string or the build will fail.
## 📜 Changelog
Details changes for each release are documented in the [CHANGELOG.md](https://github.com/intlify/bundle-tools/blob/main/packages/unplugin-vue-i18n/CHANGELOG.md)
Expand Down
8 changes: 7 additions & 1 deletion packages/unplugin-vue-i18n/src/core/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ export function resolveOptions(
TranslationDirectiveResolveIndetifier
>()

const transformI18nBlock =
typeof options.transformI18nBlock === 'function'
? options.transformI18nBlock
: null

return {
include,
exclude,
Expand All @@ -93,7 +98,8 @@ export function resolveOptions(
strictMessage,
escapeHtml,
optimizeTranslationDirective,
translationIdentifiers
translationIdentifiers,
transformI18nBlock
}
}

Expand Down
27 changes: 22 additions & 5 deletions packages/unplugin-vue-i18n/src/core/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ export function resourcePlugin(
ssrBuild,
strictMessage,
allowDynamic,
escapeHtml
escapeHtml,
transformI18nBlock
}: ResolvedOptions,
meta: UnpluginContextMeta,
installedPkgInfo: InstalledPackageInfo
Expand Down Expand Up @@ -437,14 +438,24 @@ export function resourcePlugin(
) as CodeGenOptions
debug('parseOptions', parseOptions)

const source = await getCode(
let source = await getCode(
code,
filename,
sourceMap,
query,
getSfcParser(),
meta.framework
)

if (typeof transformI18nBlock === 'function') {
const modifiedSource = transformI18nBlock(source)
if (modifiedSource && typeof modifiedSource === 'string') {
source = modifiedSource
} else {
warn('transformI18nBlock should return a string')
}
}

const { code: generatedCode, map } = generate(source, parseOptions)
debug('generated code', generatedCode)
debug('sourcemap', map, sourceMap)
Expand Down Expand Up @@ -518,14 +529,16 @@ async function generateBundleResources(
onlyLocales = [],
strictMessage = true,
escapeHtml = false,
jit = true
jit = true,
transformI18nBlock = undefined
}: {
forceStringify?: boolean
isGlobal?: boolean
onlyLocales?: string[]
strictMessage?: boolean
escapeHtml?: boolean
jit?: boolean
transformI18nBlock?: PluginOptions['transformI18nBlock']
}
) {
const codes = []
Expand All @@ -542,7 +555,8 @@ async function generateBundleResources(
onlyLocales,
strictMessage,
escapeHtml,
forceStringify
forceStringify,
transformI18nBlock
}) as CodeGenOptions
parseOptions.type = 'bare'
const { code } = generate(source, parseOptions)
Expand Down Expand Up @@ -637,7 +651,8 @@ function getOptions(
allowDynamic = false,
strictMessage = true,
escapeHtml = false,
jit = true
jit = true,
transformI18nBlock = null
}: {
inSourceMap?: RawSourceMap
forceStringify?: boolean
Expand All @@ -647,6 +662,7 @@ function getOptions(
strictMessage?: boolean
escapeHtml?: boolean
jit?: boolean
transformI18nBlock?: PluginOptions['transformI18nBlock'] | null
}
): Record<string, unknown> {
const mode: DevEnv = isProduction ? 'production' : 'development'
Expand All @@ -662,6 +678,7 @@ function getOptions(
jit,
onlyLocales,
env: mode,
transformI18nBlock,
onWarn: (msg: string): void => {
warn(`${filename} ${msg}`)
},
Expand Down
1 change: 1 addition & 0 deletions packages/unplugin-vue-i18n/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ export interface PluginOptions {
strictMessage?: boolean
escapeHtml?: boolean
optimizeTranslationDirective?: boolean | string | string[]
transformI18nBlock?: (src: string | Buffer) => string
}

0 comments on commit 3756bc9

Please sign in to comment.