nuxt-i18n-micro 1.1.9 → 1.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/dist/client/200.html +1 -1
- package/dist/client/404.html +1 -1
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/9969f09f-b918-4abc-aac8-448faa6d5c24.json +1 -0
- package/dist/client/index.html +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +29 -15
- package/dist/runtime/composables/useI18n.d.ts +1 -1
- package/dist/runtime/composables/useI18n.js +1 -0
- package/dist/runtime/{01.plugin.d.ts → plugins/01.plugin.d.ts} +16 -4
- package/dist/runtime/{01.plugin.js → plugins/01.plugin.js} +56 -81
- package/dist/runtime/server/middleware/i18n-loader.d.ts +6 -0
- package/dist/runtime/server/middleware/i18n-loader.js +22 -0
- package/dist/runtime/translationHelper.d.ts +12 -0
- package/dist/runtime/translationHelper.js +115 -0
- package/package.json +1 -1
- package/dist/client/_nuxt/builds/meta/3c9001bb-920a-4988-9ad0-29eff844b06b.json +0 -1
- package/dist/runtime/_01.plugin.d.ts +0 -1
- package/dist/runtime/_01.plugin.js +0 -0
- /package/dist/runtime/{02.meta.d.ts → plugins/02.meta.d.ts} +0 -0
- /package/dist/runtime/{02.meta.js → plugins/02.meta.js} +0 -0
- /package/dist/runtime/{03.define.d.ts → plugins/03.define.d.ts} +0 -0
- /package/dist/runtime/{03.define.js → plugins/03.define.js} +0 -0
- /package/dist/runtime/{04.auto-detect.d.ts → plugins/04.auto-detect.d.ts} +0 -0
- /package/dist/runtime/{04.auto-detect.js → plugins/04.auto-detect.js} +0 -0
package/README.md
CHANGED
|
@@ -37,16 +37,16 @@ To showcase the efficiency of `Nuxt I18n Micro`, we conducted tests under identi
|
|
|
37
37
|
#### Build Time and Resource Consumption
|
|
38
38
|
|
|
39
39
|
**Nuxt I18n**:
|
|
40
|
-
- **Total size**: 54.7 MB (3.
|
|
41
|
-
- **Max CPU Usage**:
|
|
42
|
-
- **Max Memory Usage**:
|
|
43
|
-
- **Elapsed Time**: 0h 1m
|
|
40
|
+
- **Total size**: 54.7 MB (3.31 MB gzip)
|
|
41
|
+
- **Max CPU Usage**: 391.4%
|
|
42
|
+
- **Max Memory Usage**: 8305 MB
|
|
43
|
+
- **Elapsed Time**: 0h 1m 31s
|
|
44
44
|
|
|
45
45
|
**Nuxt I18n Micro**:
|
|
46
|
-
- **Total size**:
|
|
47
|
-
- **Max CPU Usage**:
|
|
48
|
-
- **Max Memory Usage**:
|
|
49
|
-
- **Elapsed Time**: 0h 0m
|
|
46
|
+
- **Total size**: 1.93 MB (473 kB gzip) — **96% smaller**
|
|
47
|
+
- **Max CPU Usage**: 220.1% — **44% lower**
|
|
48
|
+
- **Max Memory Usage**: 655 MB — **92% less memory**
|
|
49
|
+
- **Elapsed Time**: 0h 0m 5s — **94% faster**
|
|
50
50
|
|
|
51
51
|
#### Server Performance (10k Requests)
|
|
52
52
|
|
package/dist/client/200.html
CHANGED
|
@@ -8,4 +8,4 @@
|
|
|
8
8
|
<link rel="prefetch" as="style" href="/__nuxt-i18n-micro/_nuxt/error-500.B4KzowuE.css">
|
|
9
9
|
<link rel="prefetch" as="script" crossorigin href="/__nuxt-i18n-micro/_nuxt/HZLiFEh-.js">
|
|
10
10
|
<script type="module" src="/__nuxt-i18n-micro/_nuxt/6EJ4fAZ2.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script type="application/json" id="__NUXT_DATA__" data-ssr="false">[{"serverRendered":1},false]</script>
|
|
11
|
-
<script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-i18n-micro",buildId:"
|
|
11
|
+
<script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-i18n-micro",buildId:"9969f09f-b918-4abc-aac8-448faa6d5c24",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
|
package/dist/client/404.html
CHANGED
|
@@ -8,4 +8,4 @@
|
|
|
8
8
|
<link rel="prefetch" as="style" href="/__nuxt-i18n-micro/_nuxt/error-500.B4KzowuE.css">
|
|
9
9
|
<link rel="prefetch" as="script" crossorigin href="/__nuxt-i18n-micro/_nuxt/HZLiFEh-.js">
|
|
10
10
|
<script type="module" src="/__nuxt-i18n-micro/_nuxt/6EJ4fAZ2.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script type="application/json" id="__NUXT_DATA__" data-ssr="false">[{"serverRendered":1},false]</script>
|
|
11
|
-
<script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-i18n-micro",buildId:"
|
|
11
|
+
<script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-i18n-micro",buildId:"9969f09f-b918-4abc-aac8-448faa6d5c24",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"id":"
|
|
1
|
+
{"id":"9969f09f-b918-4abc-aac8-448faa6d5c24","timestamp":1724077235706}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"id":"9969f09f-b918-4abc-aac8-448faa6d5c24","timestamp":1724077235706,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
|
package/dist/client/index.html
CHANGED
|
@@ -8,4 +8,4 @@
|
|
|
8
8
|
<link rel="prefetch" as="style" href="/__nuxt-i18n-micro/_nuxt/error-500.B4KzowuE.css">
|
|
9
9
|
<link rel="prefetch" as="script" crossorigin href="/__nuxt-i18n-micro/_nuxt/HZLiFEh-.js">
|
|
10
10
|
<script type="module" src="/__nuxt-i18n-micro/_nuxt/6EJ4fAZ2.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script type="application/json" id="__NUXT_DATA__" data-ssr="false">[{"serverRendered":1},false]</script>
|
|
11
|
-
<script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-i18n-micro",buildId:"
|
|
11
|
+
<script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-i18n-micro",buildId:"9969f09f-b918-4abc-aac8-448faa6d5c24",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
3
|
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
4
|
-
import { useNuxt, defineNuxtModule, createResolver, addPlugin, addImportsDir, addServerHandler, extendPages } from '@nuxt/kit';
|
|
4
|
+
import { useNuxt, defineNuxtModule, createResolver, addPlugin, addImportsDir, addServerHandler, extendPages, addPrerenderRoutes } from '@nuxt/kit';
|
|
5
5
|
import { onDevToolsInitialized, extendServerRpc } from '@nuxt/devtools-kit';
|
|
6
6
|
|
|
7
7
|
const DEVTOOLS_UI_PORT = 3030;
|
|
@@ -91,7 +91,7 @@ const module = defineNuxtModule({
|
|
|
91
91
|
return (forms.length > 2 ? forms[2].trim() : forms[forms.length - 1].trim()).replace('{count}', count.toString())
|
|
92
92
|
}`
|
|
93
93
|
},
|
|
94
|
-
setup(options, nuxt) {
|
|
94
|
+
setup: function(options, nuxt) {
|
|
95
95
|
const resolver = createResolver(import.meta.url);
|
|
96
96
|
nuxt.options.runtimeConfig.public.i18nConfig = {
|
|
97
97
|
rootDir: nuxt.options.rootDir,
|
|
@@ -105,35 +105,41 @@ const module = defineNuxtModule({
|
|
|
105
105
|
cache: options.cache ?? false
|
|
106
106
|
};
|
|
107
107
|
addPlugin({
|
|
108
|
-
src: resolver.resolve("./runtime/01.plugin"),
|
|
108
|
+
src: resolver.resolve("./runtime/plugins/01.plugin"),
|
|
109
109
|
order: 1
|
|
110
110
|
});
|
|
111
|
+
if (options.mata) {
|
|
112
|
+
addPlugin({
|
|
113
|
+
src: resolver.resolve("./runtime/plugins/02.meta"),
|
|
114
|
+
order: 2
|
|
115
|
+
});
|
|
116
|
+
}
|
|
111
117
|
addPlugin({
|
|
112
|
-
src: resolver.resolve("./runtime/
|
|
113
|
-
order:
|
|
114
|
-
});
|
|
115
|
-
addPlugin({
|
|
116
|
-
src: resolver.resolve("./runtime/03.define"),
|
|
117
|
-
order: 2
|
|
118
|
+
src: resolver.resolve("./runtime/plugins/03.define"),
|
|
119
|
+
order: 3
|
|
118
120
|
});
|
|
119
121
|
if (options.autoDetectLanguage) {
|
|
120
122
|
addPlugin({
|
|
121
|
-
src: resolver.resolve("./runtime/04.auto-detect"),
|
|
123
|
+
src: resolver.resolve("./runtime/plugins/04.auto-detect"),
|
|
122
124
|
mode: "client",
|
|
123
|
-
order:
|
|
125
|
+
order: 4
|
|
124
126
|
});
|
|
125
127
|
}
|
|
126
128
|
addImportsDir(resolver.resolve("./runtime/composables"));
|
|
127
129
|
if (options.includeDefaultLocaleRoute) {
|
|
128
130
|
addServerHandler({
|
|
129
131
|
middleware: true,
|
|
130
|
-
handler: resolver.resolve("./runtime/server/middleware/i18n-redirect
|
|
132
|
+
handler: resolver.resolve("./runtime/server/middleware/i18n-redirect")
|
|
131
133
|
});
|
|
132
134
|
}
|
|
135
|
+
addServerHandler({
|
|
136
|
+
route: "/_locales/:page/:locale/data.json",
|
|
137
|
+
handler: resolver.resolve("./runtime/server/middleware/i18n-loader")
|
|
138
|
+
});
|
|
133
139
|
const localeRegex = options.locales.filter((locale) => locale.code !== options.defaultLocale || options.includeDefaultLocaleRoute).map((locale) => locale.code).join("|");
|
|
134
140
|
const pagesDir = path.resolve(nuxt.options.rootDir, options.translationDir, "pages");
|
|
135
141
|
extendPages((pages) => {
|
|
136
|
-
|
|
142
|
+
const pagesNames = pages.map((page) => page.name);
|
|
137
143
|
const newRoutes = pages.map((page) => {
|
|
138
144
|
options.locales.forEach((locale) => {
|
|
139
145
|
pages.forEach((page2) => {
|
|
@@ -157,6 +163,13 @@ const module = defineNuxtModule({
|
|
|
157
163
|
};
|
|
158
164
|
});
|
|
159
165
|
pages.push(...newRoutes);
|
|
166
|
+
nuxt.options.generate.routes = Array.isArray(nuxt.options.generate.routes) ? nuxt.options.generate.routes : [];
|
|
167
|
+
options.locales?.forEach((locale) => {
|
|
168
|
+
pagesNames.forEach((name) => {
|
|
169
|
+
addPrerenderRoutes(`/_locales/${name}/${locale.code}/data.json`);
|
|
170
|
+
});
|
|
171
|
+
addPrerenderRoutes(`/_locales/general/${locale.code}/data.json`);
|
|
172
|
+
});
|
|
160
173
|
});
|
|
161
174
|
nuxt.hook("nitro:config", (nitroConfig) => {
|
|
162
175
|
const routes = nitroConfig.prerender?.routes || [];
|
|
@@ -165,15 +178,16 @@ const module = defineNuxtModule({
|
|
|
165
178
|
options.locales.forEach((locale) => {
|
|
166
179
|
if (locale.code !== options.defaultLocale || options.includeDefaultLocaleRoute) {
|
|
167
180
|
pages.forEach((page) => {
|
|
168
|
-
routes.push(`/${locale}${page}`);
|
|
181
|
+
routes.push(`/${locale.code}${page}`);
|
|
169
182
|
});
|
|
170
183
|
}
|
|
171
184
|
});
|
|
172
185
|
nitroConfig.prerender = nitroConfig.prerender || {};
|
|
173
186
|
nitroConfig.prerender.routes = routes;
|
|
174
187
|
});
|
|
175
|
-
if (nuxt.options.dev)
|
|
188
|
+
if (nuxt.options.dev) {
|
|
176
189
|
setupDevToolsUI(options, resolver.resolve);
|
|
190
|
+
}
|
|
177
191
|
}
|
|
178
192
|
});
|
|
179
193
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { PluginsInjections } from '../01.plugin.js';
|
|
1
|
+
import type { PluginsInjections } from '../plugins/01.plugin.js';
|
|
2
2
|
export declare function useI18n(): PluginsInjections;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RouteLocationRaw } from 'vue-router';
|
|
2
|
-
interface Translations {
|
|
2
|
+
export interface Translations {
|
|
3
3
|
[key: string]: string | number | boolean | Translations | PluralTranslations | unknown[] | null;
|
|
4
4
|
}
|
|
5
5
|
interface PluralTranslations {
|
|
@@ -8,17 +8,19 @@ interface PluralTranslations {
|
|
|
8
8
|
}
|
|
9
9
|
declare const _default: import("#app").Plugin<{
|
|
10
10
|
getLocale: () => string;
|
|
11
|
-
getLocales: () => import("
|
|
11
|
+
getLocales: () => import("../../module").Locale[];
|
|
12
12
|
t: <T extends Record<string, string | number | boolean>>(key: string, params?: T, defaultValue?: string) => string | number | boolean | Translations | PluralTranslations | unknown[] | unknown | null;
|
|
13
13
|
tc: (key: string, count: number, defaultValue?: string) => string;
|
|
14
|
+
has: (key: string) => boolean;
|
|
14
15
|
mergeTranslations: (newTranslations: Translations) => void;
|
|
15
16
|
switchLocale: (locale: string) => void;
|
|
16
17
|
localeRoute: (to: RouteLocationRaw, locale?: string) => RouteLocationRaw;
|
|
17
18
|
}> & import("#app").ObjectPlugin<{
|
|
18
19
|
getLocale: () => string;
|
|
19
|
-
getLocales: () => import("
|
|
20
|
+
getLocales: () => import("../../module").Locale[];
|
|
20
21
|
t: <T extends Record<string, string | number | boolean>>(key: string, params?: T, defaultValue?: string) => string | number | boolean | Translations | PluralTranslations | unknown[] | unknown | null;
|
|
21
22
|
tc: (key: string, count: number, defaultValue?: string) => string;
|
|
23
|
+
has: (key: string) => boolean;
|
|
22
24
|
mergeTranslations: (newTranslations: Translations) => void;
|
|
23
25
|
switchLocale: (locale: string) => void;
|
|
24
26
|
localeRoute: (to: RouteLocationRaw, locale?: string) => RouteLocationRaw;
|
|
@@ -29,6 +31,7 @@ export interface PluginsInjections {
|
|
|
29
31
|
$getLocales: () => string[];
|
|
30
32
|
$t: <T extends Record<string, string | number | boolean>>(key: string, params?: T, defaultValue?: string) => string | number | boolean | Translations | PluralTranslations | unknown[] | unknown | null;
|
|
31
33
|
$tc: (key: string, count: number, defaultValue?: string) => string;
|
|
34
|
+
$has: (key: string) => boolean;
|
|
32
35
|
$mergeTranslations: (newTranslations: Translations) => void;
|
|
33
36
|
$switchLocale: (locale: string) => void;
|
|
34
37
|
$localeRoute: (to: RouteLocationRaw, locale?: string) => RouteLocationRaw;
|
|
@@ -43,7 +46,16 @@ declare module 'nuxt/dist/app/nuxt' {
|
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
declare module '@vue/runtime-core' {
|
|
46
|
-
interface ComponentCustomProperties
|
|
49
|
+
interface ComponentCustomProperties {
|
|
50
|
+
$getLocale: () => string;
|
|
51
|
+
$getLocales: () => string[];
|
|
52
|
+
$t: <T extends Record<string, string | number | boolean>>(key: string, params?: T, defaultValue?: string) => string | number | boolean | Translations | PluralTranslations | unknown[] | unknown | null;
|
|
53
|
+
$tc: (key: string, count: number, defaultValue?: string) => string;
|
|
54
|
+
$has: (key: string) => boolean;
|
|
55
|
+
$mergeTranslations: (newTranslations: Translations) => void;
|
|
56
|
+
$switchLocale: (locale: string) => void;
|
|
57
|
+
$localeRoute: (to: RouteLocationRaw, locale?: string) => RouteLocationRaw;
|
|
58
|
+
$loadPageTranslations: (locale: string, routeName: string) => Promise<void>;
|
|
47
59
|
}
|
|
48
60
|
}
|
|
49
61
|
declare module 'vue' {
|
|
@@ -1,61 +1,13 @@
|
|
|
1
|
+
import { useTranslationHelper } from "../translationHelper.js";
|
|
1
2
|
import { defineNuxtPlugin, useRuntimeConfig } from "#app";
|
|
2
|
-
import { useRoute, useRouter } from "#imports";
|
|
3
|
+
import { useFetch, useRoute, useRouter } from "#imports";
|
|
4
|
+
const i18nHelper = useTranslationHelper();
|
|
3
5
|
const isDev = process.env.NODE_ENV !== "production";
|
|
4
|
-
const generalLocaleCache = {};
|
|
5
|
-
const routeLocaleCache = {};
|
|
6
|
-
const dynamicTranslationsCaches = [];
|
|
7
|
-
const translationCache = { map: /* @__PURE__ */ new Map() };
|
|
8
|
-
function deepClone(value) {
|
|
9
|
-
if (Array.isArray(value)) {
|
|
10
|
-
return value.slice();
|
|
11
|
-
} else if (typeof value === "object" && value !== null) {
|
|
12
|
-
return { ...value };
|
|
13
|
-
}
|
|
14
|
-
return value;
|
|
15
|
-
}
|
|
16
|
-
function getTranslation(translations, key) {
|
|
17
|
-
const parts = key.split(".");
|
|
18
|
-
let value = translations;
|
|
19
|
-
for (let i = 0; i < parts.length; i++) {
|
|
20
|
-
if (value && typeof value === "object" && parts[i] in value) {
|
|
21
|
-
value = value[parts[i]];
|
|
22
|
-
} else {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
if (typeof value === "object" && value !== null) {
|
|
27
|
-
return deepClone(value);
|
|
28
|
-
}
|
|
29
|
-
return value ?? null;
|
|
30
|
-
}
|
|
31
6
|
function interpolate(template, params) {
|
|
32
7
|
return template.replace(/\{(\w+)\}/g, (_, match) => {
|
|
33
8
|
return params[match] !== void 0 ? String(params[match]) : `{${match}}`;
|
|
34
9
|
});
|
|
35
10
|
}
|
|
36
|
-
function getPluralTranslation(translations, key) {
|
|
37
|
-
return getTranslation(translations, key);
|
|
38
|
-
}
|
|
39
|
-
async function loadTranslations(locale, routeName, translationDir) {
|
|
40
|
-
try {
|
|
41
|
-
if (!generalLocaleCache[locale]) {
|
|
42
|
-
const translations = await import(`~/${translationDir}/${locale}.json`);
|
|
43
|
-
generalLocaleCache[locale] = { ...translations.default };
|
|
44
|
-
}
|
|
45
|
-
if (!routeLocaleCache[`${locale}:${routeName}`]) {
|
|
46
|
-
const translations = await import(`~/${translationDir}/pages/${routeName}/${locale}.json`);
|
|
47
|
-
routeLocaleCache[`${locale}:${routeName}`] = { ...translations.default };
|
|
48
|
-
}
|
|
49
|
-
} catch (error) {
|
|
50
|
-
console.error(`Error loading translations for ${locale} and ${routeName}:`, error);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
function mergeTranslations(routeName, locale, newTranslations) {
|
|
54
|
-
routeLocaleCache[`${locale}:${routeName}`] = {
|
|
55
|
-
...generalLocaleCache[locale],
|
|
56
|
-
...newTranslations
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
11
|
function switchLocale(locale, route, router, i18nConfig) {
|
|
60
12
|
const checkLocale = i18nConfig.locales?.find((l) => l.code === locale);
|
|
61
13
|
if (!checkLocale) {
|
|
@@ -85,8 +37,32 @@ function getLocalizedRoute(to, router, route, i18nConfig, locale) {
|
|
|
85
37
|
}
|
|
86
38
|
return router.resolve({ name: newRouteName, params: newParams });
|
|
87
39
|
}
|
|
88
|
-
export default defineNuxtPlugin(async (
|
|
40
|
+
export default defineNuxtPlugin(async (nuxtApp) => {
|
|
89
41
|
const router = useRouter();
|
|
42
|
+
if (!nuxtApp.payload.data.translations) {
|
|
43
|
+
nuxtApp.payload.data.translations = {};
|
|
44
|
+
}
|
|
45
|
+
if (import.meta.server) {
|
|
46
|
+
nuxtApp.hook("app:rendered", async () => {
|
|
47
|
+
if (import.meta.server) {
|
|
48
|
+
const route2 = useRoute();
|
|
49
|
+
const locale = (route2.params?.locale ?? i18nConfig.defaultLocale).toString();
|
|
50
|
+
const routeName = (route2?.name ?? "").toString().replace(`localized-`, "");
|
|
51
|
+
const cacheKey = `${locale}:${routeName}`;
|
|
52
|
+
nuxtApp.payload.data.translations[cacheKey] = i18nHelper.getCache(locale, routeName) ?? /* @__PURE__ */ new Map();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
} else {
|
|
56
|
+
router.beforeEach(async (to, from, next) => {
|
|
57
|
+
const locale = (to.params?.locale ?? i18nConfig.defaultLocale).toString();
|
|
58
|
+
const routeName = to.name.replace(`localized-`, "");
|
|
59
|
+
const cacheKey = `${locale}:${routeName}`;
|
|
60
|
+
if (nuxtApp.payload.data.translations) {
|
|
61
|
+
i18nHelper.setCache(locale, routeName, nuxtApp.payload.data.translations[cacheKey] || /* @__PURE__ */ new Map());
|
|
62
|
+
}
|
|
63
|
+
next();
|
|
64
|
+
});
|
|
65
|
+
}
|
|
90
66
|
const route = useRoute();
|
|
91
67
|
const config = useRuntimeConfig();
|
|
92
68
|
const i18nConfig = config.public.i18nConfig;
|
|
@@ -94,14 +70,26 @@ export default defineNuxtPlugin(async (_nuxtApp) => {
|
|
|
94
70
|
const initialRouteName = route.name.replace(`localized-`, "");
|
|
95
71
|
const plural = new Function("return " + i18nConfig.plural)();
|
|
96
72
|
router.beforeEach(async (to, from, next) => {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
73
|
+
if (import.meta.client) {
|
|
74
|
+
const locale = (to.params?.locale ?? i18nConfig.defaultLocale).toString();
|
|
75
|
+
const routeName = to.name.replace(`localized-`, "");
|
|
76
|
+
if (!i18nHelper.hasPageTranslation(locale, routeName)) {
|
|
77
|
+
const resp = await fetch(`/_locales/${routeName}/${locale}/data.json`);
|
|
78
|
+
if (resp.ok) {
|
|
79
|
+
await i18nHelper.loadPageTranslations(locale, routeName, await resp.json());
|
|
80
|
+
}
|
|
81
|
+
}
|
|
101
82
|
}
|
|
102
83
|
next();
|
|
103
84
|
});
|
|
104
|
-
await
|
|
85
|
+
const { data } = await useFetch(`/_locales/general/${initialLocale}/data.json`);
|
|
86
|
+
await i18nHelper.loadTranslations(initialLocale, initialRouteName, data.value ?? {});
|
|
87
|
+
if (import.meta.server) {
|
|
88
|
+
const locale = (route.params?.locale ?? i18nConfig.defaultLocale).toString();
|
|
89
|
+
const routeName = route.name.replace(`localized-`, "");
|
|
90
|
+
const { data: data2 } = await useFetch(`/_locales/${routeName}/${locale}/data.json`);
|
|
91
|
+
await i18nHelper.loadPageTranslations(initialLocale, initialRouteName, data2.value ?? {});
|
|
92
|
+
}
|
|
105
93
|
return {
|
|
106
94
|
provide: {
|
|
107
95
|
getLocale: () => {
|
|
@@ -116,26 +104,16 @@ export default defineNuxtPlugin(async (_nuxtApp) => {
|
|
|
116
104
|
}
|
|
117
105
|
const route2 = useRoute();
|
|
118
106
|
const locale = (route2.params?.locale ?? i18nConfig.defaultLocale).toString();
|
|
119
|
-
const cacheKey = locale + ":" + key;
|
|
120
|
-
if (i18nConfig.cache && translationCache.map.has(cacheKey)) {
|
|
121
|
-
return translationCache.map.get(cacheKey);
|
|
122
|
-
}
|
|
123
107
|
const routeName = route2.name.replace(`localized-`, "");
|
|
124
|
-
let value = getTranslation(
|
|
125
|
-
return result || getTranslation(cache[locale] ?? {}, key);
|
|
126
|
-
}, null);
|
|
108
|
+
let value = i18nHelper.getTranslation(locale, routeName, key, !!i18nConfig.cache);
|
|
127
109
|
if (!value) {
|
|
128
110
|
if (isDev && import.meta.client) {
|
|
129
|
-
console.warn(`Not found '${key}' key in '${locale}' locale messages.`);
|
|
130
111
|
}
|
|
131
112
|
value = defaultValue || key;
|
|
132
113
|
}
|
|
133
114
|
if (typeof value === "string" && params) {
|
|
134
115
|
value = interpolate(value, params);
|
|
135
116
|
}
|
|
136
|
-
if (i18nConfig.cache) {
|
|
137
|
-
translationCache.map.set(cacheKey, value);
|
|
138
|
-
}
|
|
139
117
|
return value;
|
|
140
118
|
},
|
|
141
119
|
tc: (key, count, defaultValue) => {
|
|
@@ -145,31 +123,28 @@ export default defineNuxtPlugin(async (_nuxtApp) => {
|
|
|
145
123
|
}
|
|
146
124
|
const route2 = useRoute();
|
|
147
125
|
const locale = (route2.params?.locale ?? i18nConfig.defaultLocale).toString();
|
|
148
|
-
const cacheKey = locale + ":" + key + ":" + count;
|
|
149
|
-
if (i18nConfig.cache && translationCache.map.has(cacheKey)) {
|
|
150
|
-
return translationCache.map.get(cacheKey);
|
|
151
|
-
}
|
|
152
126
|
const routeName = route2.name.replace(`localized-`, "");
|
|
153
|
-
let translation =
|
|
154
|
-
return result || getPluralTranslation(cache[locale] ?? {}, key);
|
|
155
|
-
}, null);
|
|
127
|
+
let translation = i18nHelper.getTranslation(locale, routeName, key, !!i18nConfig.cache);
|
|
156
128
|
if (!translation) {
|
|
157
129
|
if (isDev && import.meta.client) {
|
|
158
130
|
console.warn(`Not found '${key}' key in '${locale}' locale messages.`);
|
|
159
131
|
}
|
|
160
132
|
translation = defaultValue || key;
|
|
161
133
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
134
|
+
return plural(translation.toString(), count, locale);
|
|
135
|
+
},
|
|
136
|
+
has: (key) => {
|
|
137
|
+
const route2 = useRoute();
|
|
138
|
+
const locale = (route2.params?.locale ?? i18nConfig.defaultLocale).toString();
|
|
139
|
+
const routeName = route2.name.replace(`localized-`, "");
|
|
140
|
+
const translation = i18nHelper.getTranslation(locale, routeName, key, !!i18nConfig.cache);
|
|
141
|
+
return !!translation;
|
|
167
142
|
},
|
|
168
143
|
mergeTranslations: (newTranslations) => {
|
|
169
144
|
const route2 = useRoute();
|
|
170
145
|
const routeName = route2.name.replace(`localized-`, "");
|
|
171
146
|
const locale = (route2.params?.locale ?? i18nConfig.defaultLocale).toString();
|
|
172
|
-
|
|
147
|
+
i18nHelper.margeTranslation(routeName, locale, newTranslations);
|
|
173
148
|
},
|
|
174
149
|
switchLocale: (locale) => {
|
|
175
150
|
const route2 = useRoute();
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ModuleOptions } from '../../../module.js';
|
|
2
|
+
export interface ModuleOptionsExtend extends ModuleOptions {
|
|
3
|
+
rootDir?: string;
|
|
4
|
+
}
|
|
5
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
|
|
6
|
+
export default _default;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { defineEventHandler } from "h3";
|
|
4
|
+
import { useRuntimeConfig } from "#imports";
|
|
5
|
+
export default defineEventHandler(async (event) => {
|
|
6
|
+
const { page, locale } = event.context.params;
|
|
7
|
+
const config = useRuntimeConfig();
|
|
8
|
+
const { rootDir, translationDir } = config.public.i18nConfig;
|
|
9
|
+
let path = `${locale}.json`;
|
|
10
|
+
if (page !== "general") {
|
|
11
|
+
path = `pages/${page}/${locale}.json`;
|
|
12
|
+
}
|
|
13
|
+
const translationPath = resolve(rootDir, translationDir, path);
|
|
14
|
+
try {
|
|
15
|
+
const fileContent = await readFile(translationPath, "utf-8");
|
|
16
|
+
return JSON.parse(fileContent);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.log("error", error);
|
|
19
|
+
event.node.res.statusCode = 404;
|
|
20
|
+
return { error: "Translations not found" };
|
|
21
|
+
}
|
|
22
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Translations } from './plugins/01.plugin.js';
|
|
2
|
+
export declare function useTranslationHelper(): {
|
|
3
|
+
hasCache(locale: string, page: string): boolean;
|
|
4
|
+
getCache(locale: string, routeName: string): Map<string, unknown>;
|
|
5
|
+
setCache(locale: string, routeName: string, cache: Map<string, Translations | unknown>): void;
|
|
6
|
+
margeTranslation(locale: string, routeName: string, newTranslations: Translations): void;
|
|
7
|
+
hasGeneralTranslation(locale: string): boolean;
|
|
8
|
+
hasPageTranslation(locale: string, routeName: string): boolean;
|
|
9
|
+
getTranslation: <T = unknown>(locale: string, routeName: string, key: string, useCache: boolean) => T | null;
|
|
10
|
+
loadPageTranslations: (locale: string, routeName: string, translations: Translations) => Promise<void>;
|
|
11
|
+
loadTranslations: (locale: string, routeName: string, translations: Translations) => Promise<void>;
|
|
12
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
const generalLocaleCache = {};
|
|
2
|
+
const routeLocaleCache = {};
|
|
3
|
+
const dynamicTranslationsCaches = [];
|
|
4
|
+
const serverTranslationCache = {};
|
|
5
|
+
const serverTranslationInit = {};
|
|
6
|
+
function deepClone(value) {
|
|
7
|
+
if (Array.isArray(value)) {
|
|
8
|
+
return value.slice();
|
|
9
|
+
} else if (typeof value === "object" && value !== null) {
|
|
10
|
+
return { ...value };
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
function findTranslation(translations, parts) {
|
|
15
|
+
let value = translations;
|
|
16
|
+
for (let i = 0; i < parts.length; i++) {
|
|
17
|
+
if (value && typeof value === "object" && parts[i] in value) {
|
|
18
|
+
value = value[parts[i]];
|
|
19
|
+
} else {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (typeof value === "object" && value !== null) {
|
|
24
|
+
return deepClone(value);
|
|
25
|
+
}
|
|
26
|
+
return value ?? null;
|
|
27
|
+
}
|
|
28
|
+
export function useTranslationHelper() {
|
|
29
|
+
return {
|
|
30
|
+
hasCache(locale, page) {
|
|
31
|
+
return serverTranslationInit[`${locale}:${page}`] ?? false;
|
|
32
|
+
},
|
|
33
|
+
getCache(locale, routeName) {
|
|
34
|
+
return serverTranslationCache[`${locale}:${routeName}`];
|
|
35
|
+
},
|
|
36
|
+
setCache(locale, routeName, cache) {
|
|
37
|
+
serverTranslationCache[`${locale}:${routeName}`] = cache;
|
|
38
|
+
serverTranslationInit[`${locale}:index`] = true;
|
|
39
|
+
serverTranslationInit[`${locale}:${routeName}`] = true;
|
|
40
|
+
},
|
|
41
|
+
margeTranslation(locale, routeName, newTranslations) {
|
|
42
|
+
if (!routeLocaleCache[`${locale}:${routeName}`]) {
|
|
43
|
+
console.error(`marge: route ${routeName} not loaded`);
|
|
44
|
+
}
|
|
45
|
+
routeLocaleCache[`${locale}:${routeName}`] = {
|
|
46
|
+
...generalLocaleCache[locale],
|
|
47
|
+
...newTranslations
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
hasGeneralTranslation(locale) {
|
|
51
|
+
return !!generalLocaleCache[locale];
|
|
52
|
+
},
|
|
53
|
+
hasPageTranslation(locale, routeName) {
|
|
54
|
+
return !!routeLocaleCache[`${locale}:${routeName}`];
|
|
55
|
+
},
|
|
56
|
+
getTranslation: (locale, routeName, key, useCache) => {
|
|
57
|
+
const cacheKey = `${locale}:${routeName}`;
|
|
58
|
+
if (useCache && serverTranslationCache[cacheKey]) {
|
|
59
|
+
const cached = serverTranslationCache[cacheKey].get(key);
|
|
60
|
+
if (cached) {
|
|
61
|
+
return cached;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const parts = key.split(".");
|
|
65
|
+
let result = null;
|
|
66
|
+
if (dynamicTranslationsCaches.length) {
|
|
67
|
+
for (const dynamicCache of dynamicTranslationsCaches) {
|
|
68
|
+
const value = findTranslation(dynamicCache[locale], parts);
|
|
69
|
+
if (value !== null) {
|
|
70
|
+
result = value;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (!result) {
|
|
75
|
+
const value = findTranslation(routeLocaleCache[`${locale}:${routeName}`], parts);
|
|
76
|
+
if (value !== null) {
|
|
77
|
+
result = value;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (!result) {
|
|
81
|
+
const value = findTranslation(generalLocaleCache[locale], parts);
|
|
82
|
+
if (value !== null) {
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (useCache && result) {
|
|
87
|
+
if (!serverTranslationCache[cacheKey]) {
|
|
88
|
+
serverTranslationCache[cacheKey] = /* @__PURE__ */ new Map();
|
|
89
|
+
}
|
|
90
|
+
serverTranslationCache[cacheKey].set(key, result);
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
93
|
+
},
|
|
94
|
+
loadPageTranslations: async (locale, routeName, translations) => {
|
|
95
|
+
try {
|
|
96
|
+
if (!routeLocaleCache[`${locale}:${routeName}`]) {
|
|
97
|
+
routeLocaleCache[`${locale}:${routeName}`] = { ...translations };
|
|
98
|
+
serverTranslationInit[`${locale}:${routeName}`] = true;
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error(`Error loading translations for ${locale} and ${routeName}:`, error);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
loadTranslations: async (locale, routeName, translations) => {
|
|
105
|
+
try {
|
|
106
|
+
if (!generalLocaleCache[locale]) {
|
|
107
|
+
generalLocaleCache[locale] = { ...translations };
|
|
108
|
+
serverTranslationInit[`${locale}:index`] = true;
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(`Error loading translations for general ${locale}:`, error);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-i18n-micro",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.11",
|
|
4
4
|
"description": "Nuxt I18n Micro is a lightweight, high-performance internationalization module for Nuxt, designed to handle multi-language support with minimal overhead, fast build times, and efficient runtime performance.",
|
|
5
5
|
"repository": "s00d/nuxt-i18n-micro",
|
|
6
6
|
"license": "MIT",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"id":"3c9001bb-920a-4988-9ad0-29eff844b06b","timestamp":1723999902563,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|