Skip to content

Commit

Permalink
feat: support layer locales and pages
Browse files Browse the repository at this point in the history
  • Loading branch information
BobbieGoede committed Mar 16, 2023
1 parent 5fc911e commit 11cd655
Show file tree
Hide file tree
Showing 12 changed files with 189 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
"source.fixAll.eslint": true
},
"typescript.tsdk": "node_modules/typescript/lib"
}
}
3 changes: 3 additions & 0 deletions playground/layers/i18n-layer/locales/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"layerText": "This is a merged locale key"
}
3 changes: 3 additions & 0 deletions playground/layers/i18n-layer/locales/fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"layerText": "This is a merged locale key in French"
}
3 changes: 3 additions & 0 deletions playground/layers/i18n-layer/locales/nl.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"layerText": "This is a merged locale key in Dutch"
}
62 changes: 62 additions & 0 deletions playground/layers/i18n-layer/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// import type { NuxtApp } from 'nuxt/dist/app/index'

// https://nuxt.com/docs/guide/directory-structure/nuxt.config
export default defineNuxtConfig({
modules: ['@nuxtjs/i18n'],
i18n: {
langDir: 'locales',
lazy: true,
baseUrl: 'http://localhost:3000',
customRoutes: 'config',
pages: {
history: {
en: '/history',
fr: '/history-fr',
ja: '/history-ja'
}
},
locales: [
{
code: 'en',
iso: 'en-US',
file: 'en.json',
// domain: 'localhost',
name: 'English'
},
{
code: 'fr',
iso: 'fr-FR',
file: 'fr.json',
// domain: 'localhost',
name: 'Francais'
},
{
code: 'nl',
iso: 'nl-NL',
file: 'nl.json',
// domain: 'localhost',
name: 'Nederlands'
}
// {
// code: 'en-GB',
// iso: 'en-GB',
// files: ['en.json', 'en-GB.json'],
// name: 'English (UK)'
// },
// {
// code: 'ja',
// iso: 'ja-JP',
// file: 'ja.json',
// domain: 'mydomain.com',
// name: 'Japanses'
// },
// {
// code: 'fr',
// iso: 'fr-FR',
// file: 'fr.json',
// domain: 'mydomain.fr',
// name: 'Français'
// }
]
}
})
3 changes: 3 additions & 0 deletions playground/layers/i18n-layer/pages/history.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<div></div>
</template>
5 changes: 5 additions & 0 deletions playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import type { NuxtApp } from 'nuxt/dist/app/index'

// https://nuxt.com/docs/guide/directory-structure/nuxt.config
export default defineNuxtConfig({
extends: [
'layers/i18n-layer'
// 'github:BobbieGoede/nuxt-i18n-layer-test'
],
// debug: false,
modules: [Module1, '@nuxtjs/i18n'],

vite: {
Expand Down
5 changes: 4 additions & 1 deletion playground/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ console.log('route base name', getRouteBaseName())
console.log('useBrowserLocale', useBrowserLocale())
console.log('localeProperties', localeProperties)
console.log('foo', t('foo'))
console.log('message if local layer merged:', t('layerText'))
console.log('message if github layer merged:', t('layer-test-key'))
function getLocaleName(code: string) {
const locale = (locales.value as LocaleObject[]).find(i => i.code === code)
Expand Down Expand Up @@ -54,7 +56,8 @@ definePageMeta({
<nav>
<NuxtLink :to="localePath('/')">Home</NuxtLink> | <NuxtLink :to="localePath({ name: 'about' })">About</NuxtLink> |
<NuxtLink :to="localePath({ name: 'blog' })">Blog</NuxtLink> |
<NuxtLink :to="localePath({ name: 'category-id', params: { id: 'foo' } })">Category</NuxtLink>
<NuxtLink :to="localePath({ name: 'category-id', params: { id: 'foo' } })">Category</NuxtLink> |
<NuxtLink :to="localePath({ name: 'history' })">History</NuxtLink>
</nav>
<h2>Current Language: {{ getLocaleName(locale) }}</h2>
<h2>Current Strategy: {{ strategy }}</h2>
Expand Down
88 changes: 88 additions & 0 deletions src/layers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { Nuxt } from '@nuxt/schema'
import { LocaleObject } from 'vue-i18n-routing'
import { NuxtI18nOptions } from './types'
import createDebug from 'debug'
import pathe from 'pathe'

const debug = createDebug('@nuxtjs/i18n:layers')

const getLocaleFiles = (locale: LocaleObject): string[] => {
if (locale.file != null) return [locale.file]
if (locale.files != null) return locale.files
return []
}

const localeFilesToRelative = (projectLangDir: string, layerLangDir: string, files: string[]) => {
const absoluteFiles = files.map(file => pathe.resolve(layerLangDir, file))
const relativeFiles = absoluteFiles.map(file => pathe.relative(projectLangDir, file))
return relativeFiles
}

const getProjectPath = (nuxt: Nuxt, ...target: string[]) => {
const projectLayer = nuxt.options._layers[0]
return pathe.resolve(projectLayer.config.rootDir, ...target)
}

export const applyLayerOptions = (options: NuxtI18nOptions, nuxt: Nuxt) => {
const project = nuxt.options._layers[0]
const layers = nuxt.options._layers

const resolvedLayerPaths = layers.map(l => pathe.resolve(project.config.rootDir, l.config.rootDir))
debug('using layers at paths -', resolvedLayerPaths)

const mergedLocales = mergeLayerLocales(nuxt)
debug('merged locales - ', mergedLocales)
options.locales = mergedLocales
}

export const mergeLayerPages = (analyzer: (pathOverride: string) => void, nuxt: Nuxt) => {
const project = nuxt.options._layers[0]
const layers = nuxt.options._layers

for (const l of layers) {
const lPath = pathe.resolve(project.config.rootDir, l.config.rootDir, l.config.dir?.pages ?? 'pages')
analyzer(lPath)
}
}

export const mergeLayerLocales = (nuxt: Nuxt) => {
const projectLayer = nuxt.options._layers[0]
const projectI18n = projectLayer.config.i18n

if (projectI18n == null) {
debug('project layer `i18n` configuration is required')
return []
}
if (projectI18n.langDir == null) {
debug('project layer `i18n.langDir` is required')
return []
}

const mergedLocales: LocaleObject[] = []
const projectLangDir = getProjectPath(nuxt, projectI18n.langDir)
debug('project path', getProjectPath(nuxt))
for (const layer of nuxt.options._layers) {
if (layer.config.i18n?.locales == null) continue
if (layer.config.i18n?.langDir == null) continue

const layerLangDir = pathe.resolve(layer.config.rootDir, layer.config.i18n.langDir)
debug('layer langDir -', layerLangDir)
for (const locale of layer.config.i18n.locales) {
if (typeof locale === 'string') continue

const { file, files, ...entry } = locale
const localeEntry = mergedLocales.find(x => x.code === locale.code)

const fileEntries = getLocaleFiles(locale)
const relativeFiles = localeFilesToRelative(projectLangDir, layerLangDir, fileEntries)

if (localeEntry == null) {
mergedLocales.push({ ...entry, files: relativeFiles })
} else {
localeEntry.files = [...relativeFiles, ...(localeEntry?.files ?? [])]
}
}
}

return mergedLocales
}
3 changes: 3 additions & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { generateLoaderOptions } from './gen'
import { NUXT_I18N_MODULE_ID, DEFAULT_OPTIONS } from './constants'
import { formatMessage, getNormalizedLocales, resolveLocales, getPackageManagerType } from './utils'
import { distDir, runtimeDir, pkgModulesDir } from './dirs'
import { applyLayerOptions } from './layers'

import type { NuxtI18nOptions } from './types'
import type { DefineLocaleMessage, LocaleMessages } from 'vue-i18n'
Expand Down Expand Up @@ -52,6 +53,8 @@ export default defineNuxtModule<NuxtI18nOptions>({
throw new Error(formatMessage(`Cannot support nuxt version: ${getNuxtVersion(nuxt)}`))
}

applyLayerOptions(options, nuxt)

if (options.strategy === 'no_prefix' && options.differentDomains) {
console.warn(
formatMessage(
Expand Down
11 changes: 9 additions & 2 deletions src/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { parse as parseSFC, compileScript } from '@vue/compiler-sfc'
import { walk } from 'estree-walker'
import MagicString from 'magic-string'
import { formatMessage, getRoutePath, parseSegment } from './utils'
import { mergeLayerPages } from './layers'
import { resolve, parse as parsePath } from 'pathe'

import type { Nuxt, NuxtPage } from '@nuxt/schema'
Expand Down Expand Up @@ -60,7 +61,13 @@ export function setupPages(
pagesDir,
pages: new Map<NuxtPage, AnalizedNuxtPageMeta>()
}

analyzeNuxtPages(ctx, pages)
if (options.layers?.pages) {
const analyzer = (pageDirOverride: string) => analyzeNuxtPages(ctx, pages, pageDirOverride)
mergeLayerPages(analyzer, nuxt)
}

const localizedPages = localizeRoutes(pages, {
...options,
includeUprefixedFallback,
Expand All @@ -77,8 +84,8 @@ export function setupPages(
* Construct the map of full paths from nuxtpage to support custom routes.
* `NuxtPage` of the nested route doesn't have a slash (`/`) and isn’t the full path.
*/
export function analyzeNuxtPages(ctx: NuxtPageAnalizeContext, pages: NuxtPage[]): void {
const pagesPath = resolve(ctx.srcDir, ctx.pagesDir)
export function analyzeNuxtPages(ctx: NuxtPageAnalizeContext, pages: NuxtPage[], pageDirOverride?: string): void {
const pagesPath = resolve(ctx.srcDir, pageDirOverride ?? ctx.pagesDir)
for (const page of pages) {
const splited = page.file.split(pagesPath)
if (splited.length === 2 && splited[1]) {
Expand Down
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export type CustomRoutePages = {
}
}

export type LayersOptions = {
locales?: boolean
pages?: boolean
}

export type NuxtI18nOptions<Context = unknown> = {
differentDomains?: boolean
detectBrowserLanguage?: DetectBrowserLanguageOptions | false
Expand Down

0 comments on commit 11cd655

Please sign in to comment.