Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(unplugin-vue-i18n): allow for a custom i18n block transform hook #387

Merged
merged 7 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}