Skip to content

Commit

Permalink
feat: experimental auto declare/import translation functions (#2858)
Browse files Browse the repository at this point in the history
* feat: experimental auto import translation functions

* docs: add `autoImportTranslationFunctions` to experimental options description

* feat: add transformation to insert translation functions when needed

* refactor: replace auto import implementation using `globalThis`

* feat: auto import more translation functions

* fix: strip types before parsing script

* fix: add global types for inserted functions

* docs: change description for `autoImportTranslationFunctions`

* fix: only transform script setup sfc

* refactor: reorder definitions

* refactor: reorder imports

* fix: skip sourcemap when parsing for scriptSetup

* fix: type `te` should be `tm`

Co-authored-by: kazuya kawaguchi <kawakazu80@gmail.com>

* fix: reuse parsed information to replace script content

* chore: revert playground and fixture experimentation

* docs: add warning explaining auto-import requirement

* chore: revert playground and fixture experimentation

---------

Co-authored-by: kazuya kawaguchi <kawakazu80@gmail.com>
  • Loading branch information
BobbieGoede and kazupon authored Mar 20, 2024
1 parent f57e208 commit a586929
Show file tree
Hide file tree
Showing 11 changed files with 387 additions and 4 deletions.
6 changes: 5 additions & 1 deletion docs/content/docs/3.options/10.misc.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ description: Miscellaneous options.
## `experimental`

- type: `object`
- default: `{ localeDetector: '', switchLocalePathLinkSSR: false }`
- default: `{ localeDetector: '', switchLocalePathLinkSSR: false, autoImportTranslationFunctions: false }`

Supported properties:

Expand All @@ -15,6 +15,10 @@ Supported properties:
About how to define the locale detector, see the [`defineI18nLocaleDetector` API](/docs/api#definei18nlocaledetector)
::
- `switchLocalePathLinkSSR` (default: `false`) - Changes the way dynamic route parameters are tracked and updated internally, improving language switcher SSR when using the [`SwitchLocalePathLink`](/docs/api/components#switchlocalepathlink) component.
- `autoImportTranslationFunctions` (default: `false`) - Automatically imports/initializes `$t`, `$rt`, `$d`, `$n`, `$tm` and `$te` functions in `<script setup>` when used.
::callout{type="warning"}
This feature relies on [Nuxt's Auto-imports](https://nuxt.com/docs/guide/concepts/auto-imports) and will not work if this has been disabled.
::


## `customBlocks`
Expand Down
3 changes: 2 additions & 1 deletion playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ export default defineNuxtConfig({
i18n: {
experimental: {
localeDetector: './localeDetector.ts',
switchLocalePathLinkSSR: true
switchLocalePathLinkSSR: true,
autoImportTranslationFunctions: true
},
compilation: {
// jit: false,
Expand Down
37 changes: 37 additions & 0 deletions specs/experimental/auto_import_translation_functions.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { test, expect } from 'vitest'
import { fileURLToPath } from 'node:url'
import { setup } from '../utils'
import { gotoPath, renderPage } from '../helper'

await setup({
rootDir: fileURLToPath(new URL(`../fixtures/basic_usage`, import.meta.url)),
browser: true,
// prerender: true,
// overrides
nuxtConfig: {
runtimeConfig: {
public: {
i18n: {
baseUrl: ''
}
}
},
i18n: {
experimental: {
autoImportTranslationFunctions: true
}
}
}
})

describe('experimental.autoImportTranslationFunctions', async () => {
test('can use `$t` in `<template>`', async () => {
const { page, consoleLogs } = await renderPage('/')

await gotoPath(page, '/')

const logStrings = consoleLogs.map(x => x.text)
expect(logStrings).toContain('[autoImportTranslationFunctions][default]: Welcome')
expect(logStrings).toContain('[autoImportTranslationFunctions][fr]: Bienvenue')
})
})
5 changes: 5 additions & 0 deletions specs/fixtures/basic_usage/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ const switchLocalePath = useSwitchLocalePath()
const localeRoute = useLocaleRoute()
const appConfig = useAppConfig()
if (useNuxtApp().$config.public.i18n.experimental.autoImportTranslationFunctions) {
console.log(`[autoImportTranslationFunctions][default]: ${$t('welcome')}`)
console.log(`[autoImportTranslationFunctions][fr]: ${$t('welcome', 1, { locale: 'fr' })}`)
}
const category = ref({
title: 'Kirby',
slug: 'nintendo'
Expand Down
11 changes: 11 additions & 0 deletions src/bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import VueI18nWebpackPlugin from '@intlify/unplugin-vue-i18n/webpack'
import VueI18nVitePlugin from '@intlify/unplugin-vue-i18n/vite'
import { TransformMacroPlugin } from './transform/macros'
import { ResourcePlugin } from './transform/resource'
import { TransformI18nFunctionPlugin } from './transform/i18n-function-injection'
import { assign } from '@intlify/shared'
import { getLayerLangPaths } from './layers'

Expand All @@ -13,6 +14,7 @@ import type { PluginOptions } from '@intlify/unplugin-vue-i18n'
import type { NuxtI18nOptions } from './types'
import type { TransformMacroPluginOptions } from './transform/macros'
import type { ResourcePluginOptions } from './transform/resource'
import type { TransformI18nFunctionPluginOptions } from './transform/i18n-function-injection'
import { MetaDeprecationPlugin, type MetaDeprecationPluginOptions } from './transform/meta-deprecation'

const debug = createDebug('@nuxtjs/i18n:bundler')
Expand All @@ -37,6 +39,9 @@ export async function extendBundler(nuxt: Nuxt, nuxtOptions: Required<NuxtI18nOp
const metaDeprecationOptions: MetaDeprecationPluginOptions = {
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client
}
const i18nFunctionOptions: TransformI18nFunctionPluginOptions = {
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client
}

/**
* webpack plugin
Expand Down Expand Up @@ -65,6 +70,9 @@ export async function extendBundler(nuxt: Nuxt, nuxtOptions: Required<NuxtI18nOp
addWebpackPlugin(TransformMacroPlugin.webpack(macroOptions))
addWebpackPlugin(ResourcePlugin.webpack(resourceOptions))
addWebpackPlugin(MetaDeprecationPlugin.webpack(metaDeprecationOptions))
if (nuxtOptions.experimental.autoImportTranslationFunctions) {
addWebpackPlugin(TransformI18nFunctionPlugin.webpack(i18nFunctionOptions))
}

extendWebpackConfig(config => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- `config.plugins` is safe, so it's assigned with nuxt!
Expand Down Expand Up @@ -113,6 +121,9 @@ export async function extendBundler(nuxt: Nuxt, nuxtOptions: Required<NuxtI18nOp
addVitePlugin(TransformMacroPlugin.vite(macroOptions))
addVitePlugin(ResourcePlugin.vite(resourceOptions))
addVitePlugin(MetaDeprecationPlugin.vite(metaDeprecationOptions))
if (nuxtOptions.experimental.autoImportTranslationFunctions) {
addVitePlugin(TransformI18nFunctionPlugin.vite(i18nFunctionOptions))
}

extendViteConfig(config => {
if (config.define) {
Expand Down
3 changes: 2 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export const SWITCH_LOCALE_PATH_LINK_IDENTIFIER = 'nuxt-i18n-slp'
export const DEFAULT_OPTIONS = {
experimental: {
localeDetector: '',
switchLocalePathLinkSSR: false
switchLocalePathLinkSSR: false,
autoImportTranslationFunctions: false
},
bundle: {
compositionOnly: true,
Expand Down
16 changes: 15 additions & 1 deletion src/gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,18 @@ export function generateI18nTypes(nuxt: Nuxt, options: NuxtI18nOptions) {
const generatedLocales = simplifyLocaleOptions(nuxt, options)
const resolvedLocaleType = typeof generatedLocales === 'string' ? 'string[]' : 'LocaleObject[]'

const i18nType = `${vueI18nTypes.join(' & ')} & NuxtI18nRoutingCustomProperties<${resolvedLocaleType}>`

const globalTranslationTypes = `
declare global {
var $t: (${i18nType})['t']
var $rt: (${i18nType})['rt']
var $n: (${i18nType})['n']
var $d: (${i18nType})['d']
var $tm: (${i18nType})['tm']
var $te: (${i18nType})['te']
}`

// prettier-ignore
return `// Generated by @nuxtjs/i18n
import type { ${vueI18nTypes.join(', ')} } from 'vue-i18n'
Expand All @@ -189,10 +201,12 @@ declare module 'vue-i18n' {
declare module '#app' {
interface NuxtApp {
$i18n: ${vueI18nTypes.join(' & ')} & NuxtI18nRoutingCustomProperties<${resolvedLocaleType}>
$i18n: ${i18nType}
}
}
${options.experimental?.autoImportTranslationFunctions && globalTranslationTypes || ''}
export {}`
}

Expand Down
10 changes: 10 additions & 0 deletions src/internal-global-types.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable no-var */
import type { Composer, ExportedGlobalComposer, VueI18n } from 'vue-i18n'
import type { ComposerCustomProperties, NuxtI18nRoutingCustomProperties } from './runtime/types'

Expand All @@ -13,4 +14,13 @@ declare module '#app' {
}
}

declare global {
var $t: Composer['t']
var $rt: Composer['rt']
var $n: Composer['n']
var $d: Composer['d']
var $tm: Composer['tm']
var $te: Composer['te']
}

export {}
6 changes: 6 additions & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ export default defineNuxtModule<NuxtI18nOptions>({
)
}

if (options.experimental.autoImportTranslationFunctions && nuxt.options.imports.autoImport === false) {
logger.warn(
'Disabling `autoImports` in Nuxt is not compatible with `experimental.autoImportTranslationFunctions`, either enable `autoImports` or disable `experimental.autoImportTranslationFunctions`.'
)
}

/**
* nuxt layers handling ...
*/
Expand Down
Loading

0 comments on commit a586929

Please sign in to comment.