nuxt-i18n-micro 1.0.1
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/LICENSE +21 -0
- package/README.md +198 -0
- package/dist/client/200.html +11 -0
- package/dist/client/404.html +11 -0
- package/dist/client/_nuxt/6EJ4fAZ2.js +29 -0
- package/dist/client/_nuxt/BdbjBFjr.js +1 -0
- package/dist/client/_nuxt/DdqCv46g.js +1 -0
- package/dist/client/_nuxt/HZLiFEh-.js +1 -0
- package/dist/client/_nuxt/builds/latest.json +1 -0
- package/dist/client/_nuxt/builds/meta/3cd90d35-1876-4439-af68-a6e627aa85e6.json +1 -0
- package/dist/client/_nuxt/entry.BkbVpYOg.css +1 -0
- package/dist/client/_nuxt/error-404.CjTTbIxB.css +1 -0
- package/dist/client/_nuxt/error-500.B4KzowuE.css +1 -0
- package/dist/client/index.html +11 -0
- package/dist/module.cjs +5 -0
- package/dist/module.d.mts +40 -0
- package/dist/module.d.ts +40 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +170 -0
- package/dist/runtime/01.plugin.d.ts +26 -0
- package/dist/runtime/01.plugin.js +151 -0
- package/dist/runtime/02.meta.d.ts +2 -0
- package/dist/runtime/02.meta.js +55 -0
- package/dist/runtime/03.define.d.ts +10 -0
- package/dist/runtime/03.define.js +23 -0
- package/dist/runtime/04.auto-detect.d.ts +2 -0
- package/dist/runtime/04.auto-detect.js +39 -0
- package/dist/runtime/server/tsconfig.json +3 -0
- package/dist/types.d.mts +11 -0
- package/dist/types.d.ts +11 -0
- package/package.json +80 -0
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { useNuxt, defineNuxtModule, createResolver, addPlugin, extendPages } from '@nuxt/kit';
|
|
5
|
+
import { onDevToolsInitialized, extendServerRpc } from '@nuxt/devtools-kit';
|
|
6
|
+
|
|
7
|
+
const DEVTOOLS_UI_PORT = 3030;
|
|
8
|
+
const DEVTOOLS_UI_ROUTE = "/__nuxt-i18n-micro";
|
|
9
|
+
function setupDevToolsUI(options, resolve) {
|
|
10
|
+
const nuxt = useNuxt();
|
|
11
|
+
const clientPath = resolve("./client");
|
|
12
|
+
const isProductionBuild = fs.existsSync(clientPath);
|
|
13
|
+
if (isProductionBuild) {
|
|
14
|
+
nuxt.hook("vite:serverCreated", async (server) => {
|
|
15
|
+
const sirv = await import('sirv').then((r) => r.default || r);
|
|
16
|
+
server.middlewares.use(DEVTOOLS_UI_ROUTE, sirv(clientPath, { dev: true, single: true }));
|
|
17
|
+
});
|
|
18
|
+
} else {
|
|
19
|
+
nuxt.hook("vite:extendConfig", (config) => {
|
|
20
|
+
config.server = config.server || {};
|
|
21
|
+
config.server.proxy = config.server.proxy || {};
|
|
22
|
+
config.server.proxy[DEVTOOLS_UI_ROUTE] = {
|
|
23
|
+
target: `http://localhost:${DEVTOOLS_UI_PORT}${DEVTOOLS_UI_ROUTE}`,
|
|
24
|
+
changeOrigin: true,
|
|
25
|
+
followRedirects: true,
|
|
26
|
+
rewrite: (path2) => path2.replace(DEVTOOLS_UI_ROUTE, "")
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
onDevToolsInitialized(async () => {
|
|
31
|
+
extendServerRpc("nuxt-i18n-micro", {
|
|
32
|
+
async getLocalesAndTranslations() {
|
|
33
|
+
const localesDir = path.join(nuxt.options.rootDir, options.translationDir || "locales");
|
|
34
|
+
const pagesDir = path.join(nuxt.options.rootDir, options.translationDir || "locales", "pages");
|
|
35
|
+
const localeFiles = fs.readdirSync(localesDir);
|
|
36
|
+
const pageFiles = fs.readdirSync(pagesDir);
|
|
37
|
+
console.log(1111, pageFiles);
|
|
38
|
+
const locales = options.locales?.map((locale) => locale.code) || [];
|
|
39
|
+
return locales.map((locale) => {
|
|
40
|
+
const files = localeFiles.filter((file) => file.startsWith(locale));
|
|
41
|
+
const content = files.reduce((acc, file) => {
|
|
42
|
+
const filePath = path.join(localesDir, file);
|
|
43
|
+
acc[file] = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
44
|
+
return acc;
|
|
45
|
+
}, {});
|
|
46
|
+
const pageLocaleFiles = pageFiles.filter((file) => file.endsWith(`_${locale}.json`));
|
|
47
|
+
pageLocaleFiles.forEach((file) => {
|
|
48
|
+
const filePath = path.join(pagesDir, file);
|
|
49
|
+
content[file] = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
50
|
+
files.push(file);
|
|
51
|
+
});
|
|
52
|
+
return { locale, files, content };
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
nuxt.hook("devtools:customTabs", (tabs) => {
|
|
57
|
+
tabs.push({
|
|
58
|
+
name: "nuxt-i18n-micro",
|
|
59
|
+
title: "i18n Micro",
|
|
60
|
+
icon: "carbon:language",
|
|
61
|
+
view: {
|
|
62
|
+
type: "iframe",
|
|
63
|
+
src: DEVTOOLS_UI_ROUTE
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const module = defineNuxtModule({
|
|
71
|
+
meta: {
|
|
72
|
+
name: "nuxt-i18n-micro",
|
|
73
|
+
configKey: "i18n"
|
|
74
|
+
},
|
|
75
|
+
// Default configuration options of the Nuxt module
|
|
76
|
+
defaults: {
|
|
77
|
+
locales: [],
|
|
78
|
+
mata: true,
|
|
79
|
+
defaultLocale: "en",
|
|
80
|
+
translationDir: "locales",
|
|
81
|
+
autoDetectLanguage: true,
|
|
82
|
+
plural: `function (translation, count, _locale) {
|
|
83
|
+
const forms = translation.toString().split('|')
|
|
84
|
+
if (count === 0 && forms.length > 2) {
|
|
85
|
+
return forms[0].trim() // Case for "no apples"
|
|
86
|
+
}
|
|
87
|
+
if (count === 1 && forms.length > 1) {
|
|
88
|
+
return forms[1].trim() // Case for "one apple"
|
|
89
|
+
}
|
|
90
|
+
return (forms.length > 2 ? forms[2].trim() : forms[forms.length - 1].trim()).replace('{count}', count.toString())
|
|
91
|
+
}`
|
|
92
|
+
},
|
|
93
|
+
setup(options, nuxt) {
|
|
94
|
+
const resolver = createResolver(import.meta.url);
|
|
95
|
+
nuxt.options.runtimeConfig.public.i18nConfig = {
|
|
96
|
+
rootDir: nuxt.options.rootDir,
|
|
97
|
+
plural: options.plural,
|
|
98
|
+
locales: options.locales ?? [],
|
|
99
|
+
mata: options.mata ?? true,
|
|
100
|
+
defaultLocale: options.defaultLocale ?? "en",
|
|
101
|
+
translationDir: options.translationDir ?? "locales",
|
|
102
|
+
autoDetectLanguage: options.autoDetectLanguage ?? true
|
|
103
|
+
};
|
|
104
|
+
addPlugin({
|
|
105
|
+
src: resolver.resolve("./runtime/01.plugin"),
|
|
106
|
+
order: 1
|
|
107
|
+
});
|
|
108
|
+
addPlugin({
|
|
109
|
+
src: resolver.resolve("./runtime/02.meta"),
|
|
110
|
+
order: 2
|
|
111
|
+
});
|
|
112
|
+
addPlugin({
|
|
113
|
+
src: resolver.resolve("./runtime/03.define"),
|
|
114
|
+
order: 2
|
|
115
|
+
});
|
|
116
|
+
if (options.autoDetectLanguage) {
|
|
117
|
+
addPlugin({
|
|
118
|
+
src: resolver.resolve("./runtime/04.auto-detect"),
|
|
119
|
+
mode: "client",
|
|
120
|
+
order: 3
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
const localeRegex = options.locales.filter((locale) => locale.code !== options.defaultLocale).map((locale) => locale.code).join("|");
|
|
124
|
+
const pagesDir = path.resolve(nuxt.options.rootDir, options.translationDir, "pages");
|
|
125
|
+
extendPages((pages) => {
|
|
126
|
+
nuxt.options.generate.routes = Array.isArray(nuxt.options.generate.routes) ? nuxt.options.generate.routes : [];
|
|
127
|
+
const newRoutes = pages.map((page) => {
|
|
128
|
+
options.locales.forEach((locale) => {
|
|
129
|
+
pages.forEach((page2) => {
|
|
130
|
+
const filePath = path.join(pagesDir, `${page2.name}/${locale.code}.json`);
|
|
131
|
+
const fileDir = path.dirname(filePath);
|
|
132
|
+
if (!existsSync(fileDir)) {
|
|
133
|
+
mkdirSync(fileDir, { recursive: true });
|
|
134
|
+
}
|
|
135
|
+
if (!existsSync(filePath)) {
|
|
136
|
+
writeFileSync(filePath, JSON.stringify({}), "utf-8");
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
...page,
|
|
142
|
+
path: `/:locale(${localeRegex})${page.path}`,
|
|
143
|
+
name: `localized-${page.name}`,
|
|
144
|
+
meta: {
|
|
145
|
+
...page.meta
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
pages.push(...newRoutes);
|
|
150
|
+
});
|
|
151
|
+
nuxt.hook("nitro:config", (nitroConfig) => {
|
|
152
|
+
const routes = nitroConfig.prerender?.routes || [];
|
|
153
|
+
nuxt.options.generate.routes = Array.isArray(nuxt.options.generate.routes) ? nuxt.options.generate.routes : [];
|
|
154
|
+
const pages = nuxt.options.generate.routes || [];
|
|
155
|
+
options.locales.forEach((locale) => {
|
|
156
|
+
if (locale.code !== options.defaultLocale) {
|
|
157
|
+
pages.forEach((page) => {
|
|
158
|
+
routes.push(`/${locale}${page}`);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
nitroConfig.prerender = nitroConfig.prerender || {};
|
|
163
|
+
nitroConfig.prerender.routes = routes;
|
|
164
|
+
});
|
|
165
|
+
if (nuxt.options.dev)
|
|
166
|
+
setupDevToolsUI(options, resolver.resolve);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
export { module as default };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { RouteLocationRaw } from 'vue-router';
|
|
2
|
+
interface Translations {
|
|
3
|
+
[key: string]: string | number | boolean | Translations | PluralTranslations | unknown[] | null;
|
|
4
|
+
}
|
|
5
|
+
interface PluralTranslations {
|
|
6
|
+
singular: string;
|
|
7
|
+
plural: string;
|
|
8
|
+
}
|
|
9
|
+
declare const _default: import("#app").Plugin<{
|
|
10
|
+
getLocale: () => string;
|
|
11
|
+
getLocales: () => import("~/src/module").Locale[];
|
|
12
|
+
t: <T extends Record<string, string | number | boolean>>(key: string, params?: T, defaultValue?: string) => string | number | boolean | Translations | PluralTranslations | unknown[] | unknown | null;
|
|
13
|
+
tc: (key: string, count: number, defaultValue?: string) => string;
|
|
14
|
+
mergeTranslations: (newTranslations: Translations) => void;
|
|
15
|
+
switchLocale: (locale: string) => void;
|
|
16
|
+
localeRoute: (to: RouteLocationRaw) => RouteLocationRaw;
|
|
17
|
+
}> & import("#app").ObjectPlugin<{
|
|
18
|
+
getLocale: () => string;
|
|
19
|
+
getLocales: () => import("~/src/module").Locale[];
|
|
20
|
+
t: <T extends Record<string, string | number | boolean>>(key: string, params?: T, defaultValue?: string) => string | number | boolean | Translations | PluralTranslations | unknown[] | unknown | null;
|
|
21
|
+
tc: (key: string, count: number, defaultValue?: string) => string;
|
|
22
|
+
mergeTranslations: (newTranslations: Translations) => void;
|
|
23
|
+
switchLocale: (locale: string) => void;
|
|
24
|
+
localeRoute: (to: RouteLocationRaw) => RouteLocationRaw;
|
|
25
|
+
}>;
|
|
26
|
+
export default _default;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { defineNuxtPlugin, useRuntimeConfig } from "#app";
|
|
2
|
+
import { useRoute, useRouter, watch } from "#imports";
|
|
3
|
+
const generalLocaleCache = {};
|
|
4
|
+
const routeLocaleCache = {};
|
|
5
|
+
const dynamicTranslationsCaches = [];
|
|
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 getTranslation(translations, key) {
|
|
15
|
+
const value = key.split(".").reduce((acc, part) => {
|
|
16
|
+
if (typeof acc === "object" && acc !== null && part in acc) {
|
|
17
|
+
return acc[part];
|
|
18
|
+
}
|
|
19
|
+
return void 0;
|
|
20
|
+
}, translations);
|
|
21
|
+
if (value && typeof value === "object") {
|
|
22
|
+
return deepClone(value);
|
|
23
|
+
}
|
|
24
|
+
return value ?? null;
|
|
25
|
+
}
|
|
26
|
+
function interpolate(template, params) {
|
|
27
|
+
return template.replace(/\{(\w+)\}/g, (_, match) => {
|
|
28
|
+
return params[match] !== void 0 ? String(params[match]) : `{${match}}`;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function getPluralTranslation(translations, key) {
|
|
32
|
+
return getTranslation(translations, key);
|
|
33
|
+
}
|
|
34
|
+
async function loadTranslations(locale, routeName, translationDir) {
|
|
35
|
+
try {
|
|
36
|
+
if (!generalLocaleCache[locale]) {
|
|
37
|
+
const translations = await import(`~/${translationDir}/${locale}.json`);
|
|
38
|
+
generalLocaleCache[locale] = { ...translations.default };
|
|
39
|
+
}
|
|
40
|
+
if (!routeLocaleCache[`${locale}:${routeName}`]) {
|
|
41
|
+
const translations = await import(`~/${translationDir}/pages/${routeName}/${locale}.json`);
|
|
42
|
+
routeLocaleCache[`${locale}:${routeName}`] = { ...translations.default };
|
|
43
|
+
}
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error(`Error loading translations for ${locale} and ${routeName}:`, error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function mergeTranslations(routeName, locale, newTranslations) {
|
|
49
|
+
routeLocaleCache[`${locale}:${routeName}`] = {
|
|
50
|
+
...generalLocaleCache[locale],
|
|
51
|
+
...newTranslations
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function switchLocale(locale, route, router, i18nConfig) {
|
|
55
|
+
const checkLocale = i18nConfig.locales?.find((l) => l.code === locale);
|
|
56
|
+
if (!checkLocale) {
|
|
57
|
+
console.warn(`Locale ${locale} is not available`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const { defaultLocale } = i18nConfig;
|
|
61
|
+
const currentLocale = route.params.locale || defaultLocale;
|
|
62
|
+
let routeName = route.name;
|
|
63
|
+
if (currentLocale !== defaultLocale) {
|
|
64
|
+
routeName = routeName.replace(`localized-`, "");
|
|
65
|
+
}
|
|
66
|
+
const newRouteName = locale === defaultLocale ? routeName : `localized-${routeName}`;
|
|
67
|
+
const newParams = { ...route.params };
|
|
68
|
+
delete newParams.locale;
|
|
69
|
+
if (locale !== defaultLocale) {
|
|
70
|
+
newParams.locale = locale;
|
|
71
|
+
}
|
|
72
|
+
window.location.href = router.resolve({ name: newRouteName, params: newParams }).fullPath;
|
|
73
|
+
}
|
|
74
|
+
function getLocalizedRoute(to, router, route, i18nConfig) {
|
|
75
|
+
const { defaultLocale } = i18nConfig;
|
|
76
|
+
const currentLocale = (route.params.locale || defaultLocale).toString();
|
|
77
|
+
let resolvedRoute = router.resolve(to);
|
|
78
|
+
if (typeof to === "object" && "name" in to) {
|
|
79
|
+
resolvedRoute = router.resolve({ name: to.name, params: to.params, query: to.query, hash: to.hash });
|
|
80
|
+
delete resolvedRoute.params.locale;
|
|
81
|
+
if (resolvedRoute.name) {
|
|
82
|
+
resolvedRoute.name = currentLocale === defaultLocale ? resolvedRoute.name.replace(`localized-`, "") : `localized-${resolvedRoute.name.toString()}`;
|
|
83
|
+
if (defaultLocale !== currentLocale) {
|
|
84
|
+
resolvedRoute.params.locale = currentLocale;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return resolvedRoute;
|
|
89
|
+
}
|
|
90
|
+
export default defineNuxtPlugin(async (_nuxtApp) => {
|
|
91
|
+
const router = useRouter();
|
|
92
|
+
const route = useRoute();
|
|
93
|
+
const config = useRuntimeConfig();
|
|
94
|
+
const i18nConfig = config.public.i18nConfig;
|
|
95
|
+
const initialLocale = (route.params?.locale ?? i18nConfig.defaultLocale).toString();
|
|
96
|
+
const initialRouteName = route.name.replace(`localized-`, "");
|
|
97
|
+
const plural = new Function("return " + i18nConfig.plural)();
|
|
98
|
+
await loadTranslations(initialLocale, initialRouteName, i18nConfig.translationDir);
|
|
99
|
+
let init = false;
|
|
100
|
+
watch(route, async () => {
|
|
101
|
+
if (!init) {
|
|
102
|
+
init = true;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const locale = (route.params?.locale ?? i18nConfig.defaultLocale).toString();
|
|
106
|
+
const routeName = route.name.replace(`localized-`, "");
|
|
107
|
+
await loadTranslations(locale, routeName, i18nConfig.translationDir);
|
|
108
|
+
}, { immediate: true });
|
|
109
|
+
return {
|
|
110
|
+
provide: {
|
|
111
|
+
getLocale: () => (route.params?.locale ?? i18nConfig.defaultLocale).toString(),
|
|
112
|
+
getLocales: () => i18nConfig.locales || [],
|
|
113
|
+
t: (key, params, defaultValue) => {
|
|
114
|
+
const locale = (route.params?.locale ?? i18nConfig.defaultLocale).toString();
|
|
115
|
+
const routeName = route.name.replace(`localized-`, "");
|
|
116
|
+
let value = getTranslation(routeLocaleCache[`${locale}:${routeName}`] ?? {}, key) || getTranslation(generalLocaleCache[locale] ?? {}, key) || dynamicTranslationsCaches.reduce((result, cache) => {
|
|
117
|
+
return result || getTranslation(cache[locale] ?? {}, key);
|
|
118
|
+
}, null);
|
|
119
|
+
if (!value) {
|
|
120
|
+
value = defaultValue || key;
|
|
121
|
+
}
|
|
122
|
+
if (typeof value === "string" && params) {
|
|
123
|
+
value = interpolate(value, params);
|
|
124
|
+
}
|
|
125
|
+
return value;
|
|
126
|
+
},
|
|
127
|
+
tc: (key, count, defaultValue) => {
|
|
128
|
+
const locale = (route.params?.locale ?? i18nConfig.defaultLocale).toString();
|
|
129
|
+
const routeName = route.name.replace(`localized-`, "");
|
|
130
|
+
let translation = getPluralTranslation(routeLocaleCache[`${locale}:${routeName}`] ?? {}, key) || getPluralTranslation(generalLocaleCache[locale] ?? {}, key) || dynamicTranslationsCaches.reduce((result, cache) => {
|
|
131
|
+
return result || getPluralTranslation(cache[locale] ?? {}, key);
|
|
132
|
+
}, null);
|
|
133
|
+
if (!translation) {
|
|
134
|
+
translation = defaultValue || key;
|
|
135
|
+
}
|
|
136
|
+
return plural(translation.toString(), count, locale);
|
|
137
|
+
},
|
|
138
|
+
mergeTranslations: (newTranslations) => {
|
|
139
|
+
const routeName = route.name.replace(`localized-`, "");
|
|
140
|
+
const locale = (route.params?.locale ?? i18nConfig.defaultLocale).toString();
|
|
141
|
+
mergeTranslations(routeName, locale, newTranslations);
|
|
142
|
+
},
|
|
143
|
+
switchLocale: (locale) => {
|
|
144
|
+
switchLocale(locale, route, router, i18nConfig);
|
|
145
|
+
},
|
|
146
|
+
localeRoute: (to) => {
|
|
147
|
+
return getLocalizedRoute(to, router, route, i18nConfig);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useHead, defineNuxtPlugin, useRuntimeConfig } from "#app";
|
|
2
|
+
import { useRoute } from "#imports";
|
|
3
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
4
|
+
const route = useRoute();
|
|
5
|
+
const config = useRuntimeConfig();
|
|
6
|
+
const i18nConfig = config.public.i18nConfig;
|
|
7
|
+
if (!i18nConfig.mata) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
nuxtApp.hook("app:rendered", (_context) => {
|
|
11
|
+
const locale = (route.params?.locale ?? i18nConfig.defaultLocale).toString();
|
|
12
|
+
const locales = i18nConfig.locales || [];
|
|
13
|
+
const currentIso = locales.find((l) => l.code === locale)?.iso || locale;
|
|
14
|
+
const currentDir = locales.find((l) => l.code === locale)?.dir || "ltr";
|
|
15
|
+
const baseUrl = config.public.baseURL || "http://localhost:3000";
|
|
16
|
+
const ogUrl = `${baseUrl}${route.fullPath}`;
|
|
17
|
+
useHead({
|
|
18
|
+
htmlAttrs: {
|
|
19
|
+
lang: currentIso,
|
|
20
|
+
dir: currentDir
|
|
21
|
+
},
|
|
22
|
+
meta: [
|
|
23
|
+
{ id: "i18n-og", property: "og:locale", content: currentIso },
|
|
24
|
+
{ id: "i18n-og-url", property: "og:url", content: ogUrl },
|
|
25
|
+
...locales.filter((l) => l.code !== locale).map((loc) => ({
|
|
26
|
+
id: `i18n-og-alt-${loc.iso || loc.code}`,
|
|
27
|
+
property: "og:locale:alternate",
|
|
28
|
+
content: loc.iso || loc.code
|
|
29
|
+
}))
|
|
30
|
+
],
|
|
31
|
+
link: [
|
|
32
|
+
{ id: "i18n-can", rel: "canonical", href: ogUrl },
|
|
33
|
+
...locales.flatMap((loc) => {
|
|
34
|
+
const links = [
|
|
35
|
+
{
|
|
36
|
+
id: `i18n-alternate-${loc.code}`,
|
|
37
|
+
rel: "alternate",
|
|
38
|
+
href: `${baseUrl}/${loc.code}${route.fullPath}`,
|
|
39
|
+
hreflang: loc.code
|
|
40
|
+
}
|
|
41
|
+
];
|
|
42
|
+
if (loc.iso) {
|
|
43
|
+
links.push({
|
|
44
|
+
id: `i18n-alternate-${loc.iso}`,
|
|
45
|
+
rel: "alternate",
|
|
46
|
+
href: `${baseUrl}/${loc.code}${route.fullPath}`,
|
|
47
|
+
hreflang: loc.iso
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return links;
|
|
51
|
+
})
|
|
52
|
+
]
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare const _default: import("#app").Plugin<{
|
|
2
|
+
defineI18nRoute: (routeDefinition: {
|
|
3
|
+
locales?: string[];
|
|
4
|
+
}) => Promise<void | import("vue-router").NavigationFailure | undefined> | undefined;
|
|
5
|
+
}> & import("#app").ObjectPlugin<{
|
|
6
|
+
defineI18nRoute: (routeDefinition: {
|
|
7
|
+
locales?: string[];
|
|
8
|
+
}) => Promise<void | import("vue-router").NavigationFailure | undefined> | undefined;
|
|
9
|
+
}>;
|
|
10
|
+
export default _default;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineNuxtPlugin, useRuntimeConfig } from "#app";
|
|
2
|
+
import { useRoute, useRouter } from "#imports";
|
|
3
|
+
export default defineNuxtPlugin((_nuxtApp) => {
|
|
4
|
+
const config = useRuntimeConfig();
|
|
5
|
+
const route = useRoute();
|
|
6
|
+
const router = useRouter();
|
|
7
|
+
const i18nConfig = config.public.i18nConfig;
|
|
8
|
+
const defineI18nRoute = (routeDefinition) => {
|
|
9
|
+
const currentLocale = (route.params.locale || i18nConfig.defaultLocale).toString();
|
|
10
|
+
const { locales } = routeDefinition;
|
|
11
|
+
const { name } = route;
|
|
12
|
+
if (locales && !locales.includes(currentLocale)) {
|
|
13
|
+
const defaultRouteName = i18nConfig.defaultLocale !== i18nConfig.defaultLocale ? `localized-${name?.toString}` : name;
|
|
14
|
+
const resolvedRoute = router.resolve({ name: defaultRouteName });
|
|
15
|
+
return router.push(resolvedRoute);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
return {
|
|
19
|
+
provide: {
|
|
20
|
+
defineI18nRoute
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { defineNuxtPlugin, useCookie } from "#app";
|
|
2
|
+
import { useRoute, useRouter } from "#imports";
|
|
3
|
+
export default defineNuxtPlugin(async ({ $config }) => {
|
|
4
|
+
const i18nConfig = $config.public.i18nConfig;
|
|
5
|
+
const userLocaleCookie = useCookie("user-locale");
|
|
6
|
+
const supportedLocales = i18nConfig.locales?.map((locale) => locale.code) ?? [];
|
|
7
|
+
const defaultLocale = i18nConfig.defaultLocale || "en";
|
|
8
|
+
if (userLocaleCookie.value) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const router = useRouter();
|
|
12
|
+
const route = useRoute();
|
|
13
|
+
console.log(navigator);
|
|
14
|
+
const browserLanguages = navigator.languages || [navigator.language];
|
|
15
|
+
let detectedLocale = defaultLocale;
|
|
16
|
+
for (const language of browserLanguages) {
|
|
17
|
+
const primaryLanguage = language.split("-")[0];
|
|
18
|
+
if (supportedLocales.includes(primaryLanguage)) {
|
|
19
|
+
detectedLocale = primaryLanguage;
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (supportedLocales.includes(detectedLocale)) {
|
|
24
|
+
const currentPath = router.currentRoute;
|
|
25
|
+
const resolvedRoute = router.resolve(currentPath.value);
|
|
26
|
+
const routeName = resolvedRoute.name.replace(`localized-`, "");
|
|
27
|
+
const { defaultLocale: defaultLocale2 } = i18nConfig;
|
|
28
|
+
const newRouteName = detectedLocale === defaultLocale2 ? routeName : `localized-${routeName}`;
|
|
29
|
+
const newParams = { ...route.params };
|
|
30
|
+
delete newParams.locale;
|
|
31
|
+
if (detectedLocale !== defaultLocale2) {
|
|
32
|
+
newParams.locale = detectedLocale;
|
|
33
|
+
}
|
|
34
|
+
userLocaleCookie.value = detectedLocale;
|
|
35
|
+
location.href = router.resolve({ name: newRouteName, params: newParams }).href;
|
|
36
|
+
} else {
|
|
37
|
+
userLocaleCookie.value = defaultLocale;
|
|
38
|
+
}
|
|
39
|
+
});
|
package/dist/types.d.mts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ModuleHooks } from './module.js'
|
|
2
|
+
|
|
3
|
+
declare module '@nuxt/schema' {
|
|
4
|
+
interface NuxtHooks extends ModuleHooks {}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
declare module 'nuxt/schema' {
|
|
8
|
+
interface NuxtHooks extends ModuleHooks {}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export { type Locale, type ModuleHooks, type ModuleOptions, default } from './module.js'
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ModuleHooks } from './module'
|
|
2
|
+
|
|
3
|
+
declare module '@nuxt/schema' {
|
|
4
|
+
interface NuxtHooks extends ModuleHooks {}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
declare module 'nuxt/schema' {
|
|
8
|
+
interface NuxtHooks extends ModuleHooks {}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export { type Locale, type ModuleHooks, type ModuleOptions, default } from './module'
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nuxt-i18n-micro",
|
|
3
|
+
"version": "1.0.1",
|
|
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
|
+
"repository": "s00d/nuxt-i18n-micro",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "s00d",
|
|
10
|
+
"email": "Virus191288@gmail.com",
|
|
11
|
+
"url": "https://s00d.github.io/"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"nuxt",
|
|
15
|
+
"i18n",
|
|
16
|
+
"internationalization",
|
|
17
|
+
"localization",
|
|
18
|
+
"multi-language",
|
|
19
|
+
"translation",
|
|
20
|
+
"nuxt-module",
|
|
21
|
+
"performance",
|
|
22
|
+
"seo",
|
|
23
|
+
"nuxt3"
|
|
24
|
+
],
|
|
25
|
+
"homepage": "https://github.com/s00d/nuxt-i18n-micro",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/s00d/nuxt-i18n-micro/issues"
|
|
28
|
+
},
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/types.d.ts",
|
|
32
|
+
"import": "./dist/module.mjs",
|
|
33
|
+
"require": "./dist/module.cjs"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"main": "./dist/module.cjs",
|
|
37
|
+
"types": "./dist/types.d.ts",
|
|
38
|
+
"files": [
|
|
39
|
+
"dist"
|
|
40
|
+
],
|
|
41
|
+
"scripts": {
|
|
42
|
+
"prepack": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt-module-build build && npm run client:build",
|
|
43
|
+
"dev": "nuxi dev playground",
|
|
44
|
+
"dev:build": "nuxi build playground",
|
|
45
|
+
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
|
|
46
|
+
"dev:generate": "nuxi generate playground",
|
|
47
|
+
"release": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
|
|
48
|
+
"lint": "eslint .",
|
|
49
|
+
"lint:fix": "eslint . --fix",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"test:watch": "vitest watch",
|
|
52
|
+
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit",
|
|
53
|
+
"client:build": "nuxi generate client",
|
|
54
|
+
"client:dev": "nuxi dev client --port 3300",
|
|
55
|
+
"typecheck": "tsc --noEmit"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@nuxt/devtools-kit": "^1.3.9",
|
|
59
|
+
"@nuxt/kit": "^3.12.4",
|
|
60
|
+
"sirv": "^2.0.4"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@nuxt/devtools": "^1.3.9",
|
|
64
|
+
"@nuxt/devtools-kit": "^1.3.9",
|
|
65
|
+
"@nuxt/devtools-ui-kit": "^1.3.9",
|
|
66
|
+
"@nuxt/eslint-config": "^0.5.0",
|
|
67
|
+
"@nuxt/module-builder": "^0.8.3",
|
|
68
|
+
"@nuxt/schema": "^3.12.4",
|
|
69
|
+
"@nuxt/test-utils": "^3.14.1",
|
|
70
|
+
"@types/node": "^20.14.11",
|
|
71
|
+
"changelogen": "^0.5.5",
|
|
72
|
+
"eslint": "^9.7.0",
|
|
73
|
+
"nuxt": "^3.12.4",
|
|
74
|
+
"execa": "^9.3.0",
|
|
75
|
+
"typescript": "latest",
|
|
76
|
+
"vitest": "^2.0.3",
|
|
77
|
+
"vue-tsc": "^2.0.26"
|
|
78
|
+
},
|
|
79
|
+
"packageManager": "yarn@3.7.0+sha256.7bf0c78a106332886ea4e59641fd819b1af953edcd72c4d93a32b1c71000ee67"
|
|
80
|
+
}
|