diff --git a/CHANGELOG.md b/CHANGELOG.md index 43aa4a05d..85a37a7ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,7 +31,6 @@ This changelog is generated by [GitHub Releases](https://github.com/nuxt-modules #####     [View changes on GitHub](https://github.com/nuxt-modules/i18n/compare/v8.0.0-beta.12...v8.0.0-beta.13) - # v8.0.0-beta.12 (2023-05-11T14:39:12Z) This changelog is generated by [GitHub Releases](https://github.com/nuxt-modules/i18n/releases/tag/v8.0.0-beta.12) diff --git a/specs/fixtures/issues/2220/app.vue b/specs/fixtures/issues/2220/app.vue new file mode 100644 index 000000000..f9a1e0b2a --- /dev/null +++ b/specs/fixtures/issues/2220/app.vue @@ -0,0 +1,19 @@ + + + diff --git a/specs/fixtures/issues/2220/locales.ts b/specs/fixtures/issues/2220/locales.ts new file mode 100644 index 000000000..69524861c --- /dev/null +++ b/specs/fixtures/issues/2220/locales.ts @@ -0,0 +1,10 @@ +export default [ + { + code: 'en', + name: 'English' + }, + { + code: 'ru', + name: 'Русский' + } +].map(lang => ({ file: lang.code + '.json', ...lang })) diff --git a/specs/fixtures/issues/2220/locales/en.json b/specs/fixtures/issues/2220/locales/en.json new file mode 100644 index 000000000..2b349be02 --- /dev/null +++ b/specs/fixtures/issues/2220/locales/en.json @@ -0,0 +1,3 @@ +{ + "test": "Test" +} diff --git a/specs/fixtures/issues/2220/locales/ru.json b/specs/fixtures/issues/2220/locales/ru.json new file mode 100644 index 000000000..b05ddd228 --- /dev/null +++ b/specs/fixtures/issues/2220/locales/ru.json @@ -0,0 +1,3 @@ +{ + "test": "Тест" +} diff --git a/specs/fixtures/issues/2220/nuxt.config.ts b/specs/fixtures/issues/2220/nuxt.config.ts new file mode 100644 index 000000000..2d2e9f574 --- /dev/null +++ b/specs/fixtures/issues/2220/nuxt.config.ts @@ -0,0 +1,13 @@ +import locales from './locales' + +export default defineNuxtConfig({ + modules: ['@nuxtjs/i18n'], + i18n: { + lazy: true, + langDir: 'locales', + locales, + defaultLocale: 'en', + detectBrowserLanguage: false, + strategy: 'no_prefix' + } +}) diff --git a/specs/fixtures/issues/2220/package.json b/specs/fixtures/issues/2220/package.json new file mode 100644 index 000000000..048d0dc7d --- /dev/null +++ b/specs/fixtures/issues/2220/package.json @@ -0,0 +1,14 @@ +{ + "name": "nuxt3-test-issues-2220", + "private": true, + "scripts": { + "build": "nuxt build", + "dev": "nuxt dev", + "generate": "nuxt generate", + "preview": "nuxt preview" + }, + "devDependencies": { + "@nuxtjs/i18n": "latest", + "nuxt": "latest" + } +} diff --git a/specs/fixtures/issues/2220/server/api/foo.ts b/specs/fixtures/issues/2220/server/api/foo.ts new file mode 100644 index 000000000..852003f0f --- /dev/null +++ b/specs/fixtures/issues/2220/server/api/foo.ts @@ -0,0 +1,3 @@ +export default defineEventHandler(e => { + return [e.context.$t('test')] +}) diff --git a/specs/fixtures/issues/2220/server/middleware/i18n.ts b/specs/fixtures/issues/2220/server/middleware/i18n.ts new file mode 100644 index 000000000..f4fd5f3e0 --- /dev/null +++ b/specs/fixtures/issues/2220/server/middleware/i18n.ts @@ -0,0 +1,28 @@ +import { defineEventHandler, getCookie } from 'h3' +import { createI18n } from 'vue-i18n' +import locales from '../../locales' +import en from '../../locales/en.json' +import ru from '../../locales/ru.json' + +const resources = { + en, + ru +} + +const i18n = createI18n({ + fallbackLocale: 'en' +}).global + +for (const { code } of locales) { + i18n.setLocaleMessage(code, resources[code]) +} + +export default defineEventHandler(e => { + e.context.$t = (key: string) => i18n.t(key, getCookie(e, 'lang') || i18n.fallbackLocale.toString()) +}) + +declare module 'h3' { + interface H3EventContext { + $t: typeof i18n.t + } +} diff --git a/specs/issues/2220.spec.ts b/specs/issues/2220.spec.ts new file mode 100644 index 000000000..aa8883257 --- /dev/null +++ b/specs/issues/2220.spec.ts @@ -0,0 +1,19 @@ +import { test, expect, describe } from 'vitest' +import { fileURLToPath } from 'node:url' +import { setup, createPage, url } from '../utils' +import { getText } from '../helper' + +describe('#2220', async () => { + await setup({ + rootDir: fileURLToPath(new URL(`../fixtures/issues/2220`, import.meta.url)) + }) + + test('message-compiler work on server-side', async () => { + const home = url('/') + const page = await createPage() + await page.goto(home) + + expect(await getText(page, '#app')).include('PROD [ "Test" ]') + expect(await getText(page, '#app')).include(`yeah! it's finally working in prod too`) + }) +}) diff --git a/src/alias.ts b/src/alias.ts index cea88b8eb..23faf060c 100644 --- a/src/alias.ts +++ b/src/alias.ts @@ -1,8 +1,14 @@ import createDebug from 'debug' import { resolvePath } from '@nuxt/kit' -import { pkgModulesDir } from './dirs' import { resolve } from 'pathe' -import { VUE_I18N_PKG, VUE_I18N_BRIDGE_PKG, VUE_ROUTER_BRIDGE_PKG, VUE_I18N_ROUTING_PKG } from './constants' +import { + VUE_I18N_PKG, + VUE_I18N_BRIDGE_PKG, + VUE_ROUTER_BRIDGE_PKG, + VUE_I18N_ROUTING_PKG, + MESSAGE_COMPILER_PKG +} from './constants' +import { pkgModulesDir } from './dirs' import { tryResolve, getLayerRootDirs, getPackageManagerType } from './utils' import type { Nuxt } from '@nuxt/schema' @@ -24,6 +30,10 @@ export async function setupAlias(nuxt: Nuxt) { nuxt.options.build.transpile.push('@intlify/shared') debug('@intlify/shared alias', nuxt.options.alias['@intlify/shared']) + nuxt.options.alias['@intlify/message-compiler'] = await resolveMessageCompilerAlias(pkgModulesDir, nuxt, pkgMgr) + nuxt.options.build.transpile.push('@intlify/message-compiler') + debug('@intlify/message-compiler alias', nuxt.options.alias['@intlify/message-compiler']) + // resolve @intlify/vue-router-bridge nuxt.options.alias[VUE_ROUTER_BRIDGE_PKG] = await resolveVueRouterBridgeAlias(pkgModulesDir, nuxt, pkgMgr) nuxt.options.build.transpile.push(VUE_ROUTER_BRIDGE_PKG) @@ -136,3 +146,21 @@ export async function resolveVueI18nRoutingAlias(pkgModulesDir: string, nuxt: Nu return tryResolve(VUE_I18N_ROUTING_PKG, targets, pkgMgr) } + +export async function resolveMessageCompilerAlias(pkgModulesDir: string, nuxt: Nuxt, pkgMgr: PackageManager) { + const { rootDir, workspaceDir } = nuxt.options + const modulePath = `${MESSAGE_COMPILER_PKG}/dist/message-compiler.mjs` as const + const targets = [ + // for Nuxt layer + ...getLayerRootDirs(nuxt).map(root => resolve(root, 'node_modules', modulePath)), + // 1st, try to resolve from `node_modules` (hoisted case) + resolve(rootDir, 'node_modules', modulePath), + // 2nd, try to resolve from `node_modules/@nuxtjs/i18n` (not hoisted case) + resolve(pkgModulesDir, modulePath), + // workspace directories + resolve(workspaceDir, 'node_modules', modulePath) + ] + debug(`${MESSAGE_COMPILER_PKG} resolving from ...`, targets) + + return tryResolve(MESSAGE_COMPILER_PKG, targets, pkgMgr) +} diff --git a/src/bundler.ts b/src/bundler.ts index ec1c708a4..9131f097b 100644 --- a/src/bundler.ts +++ b/src/bundler.ts @@ -6,6 +6,7 @@ import VueI18nVitePlugin from '@intlify/unplugin-vue-i18n/vite' import { TransformMacroPlugin, TransformMacroPluginOptions } from './transform/macros' import { ResourceProxyPlugin, ResourceProxyPluginOptions } from './transform/proxy' import { ResourceDynamicPlugin, ResourceDynamicPluginOptions } from './transform/dynamic' +import { assign } from '@intlify/shared' import { getLayerLangPaths } from './layers' import type { Nuxt } from '@nuxt/schema' @@ -86,13 +87,7 @@ export async function extendBundler( extendWebpackConfig(config => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- `config.plugins` is safe, so it's assigned with nuxt! config.plugins!.push( - new webpack.DefinePlugin({ - __VUE_I18N_FULL_INSTALL__: 'true', - __VUE_I18N_LEGACY_API__: 'true', - __INTLIFY_PROD_DEVTOOLS__: 'false', - __INTLIFY_JIT_COMPILATION__: 'true', - __DEBUG__: JSON.stringify(nuxtOptions.debug) - }) + new webpack.DefinePlugin(assign(getFeatureFlags(), { __DEBUG__: String(nuxtOptions.debug) })) ) }) } catch (e: unknown) { @@ -130,3 +125,12 @@ export async function extendBundler( debug('vite.config.define', config.define) }) } + +export function getFeatureFlags() { + return { + __VUE_I18N_FULL_INSTALL__: 'true', + __VUE_I18N_LEGACY_API__: 'true', + __INTLIFY_PROD_DEVTOOLS__: 'false', + __INTLIFY_JIT_COMPILATION__: 'true' + } +} diff --git a/src/constants.ts b/src/constants.ts index 161f7a5ec..b0dcc9a1d 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -5,6 +5,7 @@ export const VUE_I18N_PKG = 'vue-i18n' as const export const VUE_I18N_BRIDGE_PKG = '@intlify/vue-i18n-bridge' as const export const VUE_ROUTER_BRIDGE_PKG = '@intlify/vue-router-bridge' as const export const VUE_I18N_ROUTING_PKG = 'vue-i18n-routing' as const +export const MESSAGE_COMPILER_PKG = '@intlify/message-compiler' as const // Options const STRATEGY_PREFIX = 'prefix' diff --git a/src/module.ts b/src/module.ts index 9256ee1a2..b5106fb8f 100644 --- a/src/module.ts +++ b/src/module.ts @@ -14,6 +14,7 @@ import { resolve, relative, isAbsolute } from 'pathe' import { defu } from 'defu' import { setupAlias, resolveVueI18nAlias } from './alias' import { setupPages } from './pages' +import { setupNitro } from './nitro' import { extendMessages } from './messages' import { extendBundler } from './bundler' import { generateLoaderOptions } from './gen' @@ -283,6 +284,12 @@ export default defineNuxtModule({ langPath }) + /** + * setup nitro + */ + + await setupNitro(nuxt) + /** * auto imports */ diff --git a/src/nitro.ts b/src/nitro.ts new file mode 100644 index 000000000..6497b0176 --- /dev/null +++ b/src/nitro.ts @@ -0,0 +1,16 @@ +import { assign } from '@intlify/shared' +import { getFeatureFlags } from './bundler' + +import type { Nuxt } from '@nuxt/schema' + +export async function setupNitro(nuxt: Nuxt) { + if (nuxt.options.ssr) { + if (!nuxt.options.nitro) { + nuxt.options.nitro = {} + } + const nitroConfig = nuxt.options.nitro + + // vue-i18n feature flags configuration for server-side (server api, server middleware, etc...) + nitroConfig.replace = assign(nitroConfig.replace || {}, getFeatureFlags()) + } +}