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 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.29 MB gzip)
41
- - **Max CPU Usage**: 394.0%
42
- - **Max Memory Usage**: 8746 MB
43
- - **Elapsed Time**: 0h 1m 30s
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**: 22.4 MB (2.9 MB gzip) — **59% smaller**
47
- - **Max CPU Usage**: 305.3% — **23% lower**
48
- - **Max Memory Usage**: 3247 MB — **63% less memory**
49
- - **Elapsed Time**: 0h 0m 17s — **81% faster**
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
 
@@ -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:"3c9001bb-920a-4988-9ad0-29eff844b06b",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
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>
@@ -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:"3c9001bb-920a-4988-9ad0-29eff844b06b",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
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":"3c9001bb-920a-4988-9ad0-29eff844b06b","timestamp":1723999902563}
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":[]}
@@ -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:"3c9001bb-920a-4988-9ad0-29eff844b06b",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script></body></html>
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-i18n-micro",
3
3
  "configKey": "i18n",
4
- "version": "1.1.9",
4
+ "version": "1.1.11",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.8.3",
7
7
  "unbuild": "2.0.0"
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/02.meta"),
113
- order: 2
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: 3
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.ts")
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
- nuxt.options.generate.routes = Array.isArray(nuxt.options.generate.routes) ? nuxt.options.generate.routes : [];
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;
@@ -5,6 +5,7 @@ export function useI18n() {
5
5
  $getLocale: nuxtApp.$getLocale,
6
6
  $getLocales: nuxtApp.$getLocales,
7
7
  $t: nuxtApp.$t,
8
+ $has: nuxtApp.$has,
8
9
  $tc: nuxtApp.$tc,
9
10
  $mergeTranslations: nuxtApp.$mergeTranslations,
10
11
  $switchLocale: nuxtApp.$switchLocale,
@@ -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("../module").Locale[];
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("../module").Locale[];
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 extends PluginsInjections {
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 (_nuxtApp) => {
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
- const locale = (to.params?.locale ?? i18nConfig.defaultLocale).toString();
98
- const routeName = to.name.replace(`localized-`, "");
99
- if (!routeLocaleCache[`${locale}:${routeName}`]) {
100
- await loadTranslations(locale, routeName, i18nConfig.translationDir);
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 loadTranslations(initialLocale, initialRouteName, i18nConfig.translationDir);
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(routeLocaleCache[`${locale}:${routeName}`] ?? {}, key) || getTranslation(generalLocaleCache[locale] ?? {}, key) || dynamicTranslationsCaches.reduce((result, cache) => {
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 = getPluralTranslation(routeLocaleCache[`${locale}:${routeName}`] ?? {}, key) || getPluralTranslation(generalLocaleCache[locale] ?? {}, key) || dynamicTranslationsCaches.reduce((result, cache) => {
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
- const value = plural(translation.toString(), count, locale);
163
- if (i18nConfig.cache) {
164
- translationCache.map.set(cacheKey, value);
165
- }
166
- return value;
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
- mergeTranslations(routeName, locale, newTranslations);
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.9",
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