@i18n-micro/core 1.0.27

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 ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,176 @@
1
+ # @i18n-micro/core
2
+
3
+ `@i18n-micro/core` is the core module for handling translations, routing, and formatting in a Nuxt.js application. It provides utilities for managing translations, interpolating placeholders, formatting numbers, dates, and relative times, and handling locale-specific routing. This module is designed to work seamlessly with `nuxt-i18n-micro` and its associated utilities.
4
+
5
+ ## Installation
6
+
7
+ You can install `@i18n-micro/core` using npm or yarn:
8
+
9
+ ```bash
10
+ npm install @i18n-micro/core
11
+ ```
12
+
13
+ or
14
+
15
+ ```bash
16
+ yarn add @i18n-micro/core
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ This package provides several utilities for managing translations, formatting, and routing:
22
+
23
+ 1. **`useTranslationHelper`**: A helper for managing translations, including loading, caching, and retrieving translations.
24
+ 2. **`interpolate`**: A utility for interpolating placeholders in translation strings with dynamic values.
25
+ 3. **`FormatService`**: A service for formatting numbers, dates, and relative times.
26
+ 4. **`RouteService`**: A service for handling locale-specific routing and route localization.
27
+
28
+ ### Example
29
+
30
+ Here’s an example of how you might use these utilities in your Nuxt.js project:
31
+
32
+ ```typescript
33
+ import { useTranslationHelper, interpolate, FormatService, RouteService } from '@i18n-micro/core'
34
+
35
+ // Initialize the translation helper
36
+ const translationHelper = useTranslationHelper()
37
+
38
+ // Load translations for a specific locale
39
+ translationHelper.loadTranslations('en', {
40
+ greeting: 'Hello, {name}!',
41
+ nested: {
42
+ message: 'This is a nested message.',
43
+ },
44
+ })
45
+
46
+ // Load page-specific translations for a specific locale
47
+ translationHelper.loadPageTranslations('en', 'home', {
48
+ welcome: 'Welcome to the home page!',
49
+ })
50
+
51
+ // Retrieve a translation for a specific locale
52
+ const greeting = translationHelper.getTranslation<string>('en', 'index', 'greeting')
53
+ console.log(greeting) // 'Hello, {name}!'
54
+
55
+ // Interpolate placeholders
56
+ const interpolatedGreeting = interpolate(greeting!, { name: 'John' })
57
+ console.log(interpolatedGreeting) // 'Hello, John!'
58
+
59
+ // Format numbers, dates, and relative times
60
+ const formatService = new FormatService()
61
+ const formattedNumber = formatService.formatNumber(123456.789, 'en-US')
62
+ const formattedDate = formatService.formatDate(new Date(), 'en-US')
63
+ const formattedRelativeTime = formatService.formatRelativeTime(new Date(), 'en-US')
64
+
65
+ console.log(formattedNumber) // '123,456.789'
66
+ console.log(formattedDate) // '10/5/2023'
67
+ console.log(formattedRelativeTime) // 'just now'
68
+
69
+ // Handle locale-specific routing
70
+ const routeService = new RouteService(
71
+ i18nConfig,
72
+ router,
73
+ hashLocaleDefault,
74
+ noPrefixDefault,
75
+ navigateTo,
76
+ setCookie
77
+ )
78
+
79
+ const localizedRoute = routeService.getLocalizedRoute('/about', currentRoute, 'en')
80
+ console.log(localizedRoute) // Localized route object
81
+ ```
82
+
83
+ ## API Reference
84
+
85
+ ### `useTranslationHelper`
86
+
87
+ #### Methods
88
+ - **`hasCache(locale: string, page: string): boolean`**:
89
+ Checks if translations for a specific route and locale are cached.
90
+ - **`getCache(locale: string, routeName: string): Map<string, Translations | unknown> | undefined`**:
91
+ Retrieves the cache for a specific route and locale.
92
+ - **`setCache(locale: string, routeName: string, cache: Map<string, Translations | unknown>): void`**:
93
+ Sets the cache for a specific route and locale.
94
+ - **`mergeTranslation(locale: string, routeName: string, newTranslations: Translations, force = false): void`**:
95
+ Merges new translations into the cache for a specific route and locale.
96
+ - **`mergeGlobalTranslation(locale: string, newTranslations: Translations, force = false): void`**:
97
+ Merges new translations into the global cache for a specific locale.
98
+ - **`hasGeneralTranslation(locale: string): boolean`**:
99
+ Checks if global translations are loaded for the specified locale.
100
+ - **`hasPageTranslation(locale: string, routeName: string): boolean`**:
101
+ Checks if translations for a specific route and locale are loaded.
102
+ - **`hasTranslation(locale: string, key: string): boolean`**:
103
+ Checks if a translation exists for the given key and locale.
104
+ - **`getTranslation<T = unknown>(locale: string, routeName: string, key: string): T | null`**:
105
+ Retrieves a translation for the given key, route, and locale.
106
+ - **`loadPageTranslations(locale: string, routeName: string, translations: Translations): Promise<void>`**:
107
+ Loads translations for a specific route and locale.
108
+ - **`loadTranslations(locale: string, translations: Translations): Promise<void>`**:
109
+ Loads global translations for the specified locale.
110
+
111
+ ### `interpolate`
112
+
113
+ #### Function
114
+ ```typescript
115
+ interpolate(template: string, params: Params): string
116
+ ```
117
+ - **`template`**: The translation string with placeholders (e.g., `'Hello, {name}!'`).
118
+ - **`params`**: An object containing key-value pairs for interpolation (e.g., `{ name: 'John' }`).
119
+
120
+ #### Example
121
+ ```typescript
122
+ const result = interpolate('Hello, {name}!', { name: 'John' })
123
+ console.log(result) // 'Hello, John!'
124
+ ```
125
+
126
+ ### `FormatService`
127
+
128
+ #### Methods
129
+ - **`formatNumber(value: number, locale: string, options?: Intl.NumberFormatOptions): string`**:
130
+ Formats a number according to the specified locale and options.
131
+ - **`formatDate(value: Date | number | string, locale: string, options?: Intl.DateTimeFormatOptions): string`**:
132
+ Formats a date according to the specified locale and options.
133
+ - **`formatRelativeTime(value: Date | number | string, locale: string, options?: Intl.RelativeTimeFormatOptions): string`**:
134
+ Formats a relative time (e.g., "2 hours ago") according to the specified locale and options.
135
+
136
+ ### `RouteService`
137
+
138
+ #### Methods
139
+ - **`getCurrentLocale(route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric): string`**:
140
+ Returns the current locale based on the route or configuration.
141
+ - **`getCurrentName(route: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric): string | null`**:
142
+ Returns the display name of the current locale.
143
+ - **`getRouteName(route: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric, locale: string): string`**:
144
+ Returns the route name without the locale suffix.
145
+ - **`getFullPathWithBaseUrl(currentLocale: Locale, route: RouteLocationRaw): string`**:
146
+ Returns the full path with the base URL for the specified locale.
147
+ - **`switchLocaleRoute(fromLocale: string, toLocale: string, route: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric, i18nRouteParams: I18nRouteParams): RouteLocationRaw`**:
148
+ Switches the locale for the specified route.
149
+ - **`getLocalizedRoute(to: RouteLocationAsString | RouteLocationAsRelative | RouteLocationAsPath, route: RouteLocationNormalizedLoaded, locale?: string): RouteLocationResolved`**:
150
+ Returns a localized route for the specified locale.
151
+ - **`updateCookies(toLocale: string): void`**:
152
+ Updates cookies with the new locale.
153
+ - **`getCurrentRoute(): RouteLocationNormalizedLoaded`**:
154
+ Returns the current route.
155
+ - **`switchLocaleLogic(toLocale: string, i18nRouteParams: I18nRouteParams, route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric | string)`**:
156
+ Handles the logic for switching locales and navigating to the new route.
157
+ - **`resolveLocalizedRoute(to: RouteLocationAsString | RouteLocationAsRelative | RouteLocationAsPath | string, locale?: string): RouteLocationResolved`**:
158
+ Resolves a localized route for the specified locale.
159
+
160
+ ## Contributing
161
+
162
+ If you find any issues or have suggestions for improvements, feel free to open an issue or submit a pull request on the [GitHub repository](https://github.com/s00d/nuxt-i18n-micro).
163
+
164
+ ## License
165
+
166
+ This project is licensed under the MIT License. See the [LICENSE](https://github.com/s00d/nuxt-i18n-micro/blob/main/LICENSE) file for more details.
167
+
168
+ ---
169
+
170
+ For more information, visit the [GitHub repository](https://github.com/s00d/nuxt-i18n-micro).
171
+
172
+ ## Author
173
+
174
+ - **Name**: s00d
175
+ - **Email**: Virus191288@gmail.com
176
+ - **Website**: [https://s00d.github.io/](https://s00d.github.io/)
@@ -0,0 +1,5 @@
1
+ export declare class FormatService {
2
+ formatNumber(value: number, locale: string, options?: Intl.NumberFormatOptions): string;
3
+ formatDate(value: Date | number | string, locale: string, options?: Intl.DateTimeFormatOptions): string;
4
+ formatRelativeTime(value: Date | number | string, locale: string, options?: Intl.RelativeTimeFormatOptions): string;
5
+ }
@@ -0,0 +1,18 @@
1
+ import { Params, Strategies, PluralFunc } from '@i18n-micro/types';
2
+ export declare function interpolate(template: string, params: Params): string;
3
+ export declare function withPrefixStrategy(strategy: Strategies): strategy is "prefix" | "prefix_and_default";
4
+ export declare function isNoPrefixStrategy(strategy: Strategies): strategy is "no_prefix";
5
+ export declare function isPrefixStrategy(strategy: Strategies): strategy is "prefix";
6
+ export declare function isPrefixExceptDefaultStrategy(strategy: Strategies): strategy is "prefix_except_default";
7
+ export declare function isPrefixAndDefaultStrategy(strategy: Strategies): strategy is "prefix_and_default";
8
+ /**
9
+ * Default pluralization function
10
+ * Splits translation by '|' and selects form based on count
11
+ * @param key - Translation key
12
+ * @param count - Count for pluralization
13
+ * @param params - Parameters for translation
14
+ * @param _locale - Current locale (unused in default implementation)
15
+ * @param getTranslation - Function to get translation value
16
+ * @returns Selected plural form or null if not found
17
+ */
18
+ export declare const defaultPlural: PluralFunc;
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const R={},x={},P=[],w={};function b(a){return Array.isArray(a)||typeof a=="object"&&a!==null?JSON.parse(JSON.stringify(a)):a}function $(a,e){let r=a;if(a===null||typeof e!="string")return null;if(a[e])r=a[e];else{const n=e.toString().split(".");for(const i of n)if(r&&typeof r=="object"&&i in r)r=r[i];else return null}return typeof r=="object"&&r!==null?b(r):r??null}function C(a){return typeof a=="object"&&a!==null&&"value"in a?a.value:a}function m(a,e,r){const n=C(a);n[e]=r}function d(a,e){return C(a)[e]}function N(a){const e=(a==null?void 0:a.generalLocaleCache)??R,r=(a==null?void 0:a.routeLocaleCache)??x,n=(a==null?void 0:a.dynamicTranslationsCaches)??P,i=(a==null?void 0:a.serverTranslationCache)??w;return{hasCache(t,s){const o=`${t}:${s}`;return(d(i,o)??new Map).size>0},getCache(t,s){const o=`${t}:${s}`;return d(i,o)},setCache(t,s,o){const l=`${t}:${s}`;m(i,l,o)},mergeTranslation(t,s,o,l=!1){const u=`${t}:${s}`,c=d(r,u);(c||l)&&m(r,u,{...c??{},...o});const f=process.env.NODE_ENV!=="production";!c&&f&&console.warn(`[i18n] mergeTranslation called for '${u}' which was not pre-loaded. Skipping merge. Use force: true if this is intentional.`)},mergeGlobalTranslation(t,s,o=!1){const l=d(e,t);!o&&!l&&console.error(`marge: route ${t} not loaded`),m(e,t,{...l??{},...s})},hasGeneralTranslation(t){return!!d(e,t)},hasPageTranslation(t,s){const o=`${t}:${s}`;return!!d(r,o)},hasTranslation:(t,s)=>{const o=C(n);for(const u of o)if($(u[t]||null,s)!==null)return!0;const l=d(e,t);return $(l||null,s)!==null},getTranslation:(t,s,o)=>{const l=`${t}:${s}`,u=d(i,l),c=u==null?void 0:u.get(o);if(c)return c;let f=null;const v=C(n);for(const g of v)if(f=$(g[t]||null,o),f!==null)break;if(!f){const g=d(r,l),p=d(e,t);f=$(g||null,o)??$(p||null,o)}if(f){const g=u??new Map;g.set(o,f),m(i,l,g)}return f},async loadPageTranslations(t,s,o){const l=`${t}:${s}`;m(r,l,{...o})},async loadTranslations(t,s){m(e,t,{...s})},clearCache(){const t=C(e);Object.keys(t).forEach(u=>{m(e,u,{})});const s=C(r);Object.keys(s).forEach(u=>{m(r,u,{})});const o=C(n);o.length=0;const l=C(i);Object.keys(l).forEach(u=>{const c=d(i,u);c==null||c.clear()})}}}function T(a,e){let r=a;for(const n in e)r=r.split(`{${n}}`).join(String(e[n]));return r}function y(a){return a==="prefix"||a==="prefix_and_default"}function h(a){return a==="no_prefix"}function D(a){return a==="prefix"}function L(a){return a==="prefix_except_default"}function q(a){return a==="prefix_and_default"}const z=(a,e,r,n,i)=>{const t=i(a,r);if(!t)return null;const s=t.toString().split("|");if(s.length===0)return null;const o=e<s.length?s[e]:s[s.length-1];return o?o.trim().replace("{count}",e.toString()):null};class F{constructor(e,r,n,i,t,s,o=null,l=null){this.i18nConfig=e,this.router=r,this.hashLocaleDefault=n,this.noPrefixDefault=i,this.navigateTo=t,this.setCookie=s,this.cookieLocaleDefault=o,this.cookieLocaleName=l}extractLocaleFromPath(e){var o,l;if(!e)return null;const n=(o=e.split("?")[0])==null?void 0:o.split("#")[0];if(!n||n==="/")return null;const i=n.split("/").filter(Boolean);if(i.length===0)return null;const t=i[0];return t&&(((l=this.i18nConfig.locales)==null?void 0:l.map(u=>u.code))||[]).includes(t)?t:null}getCurrentLocale(e){var i;if(e=e??this.router.currentRoute.value,this.i18nConfig.hashMode&&this.hashLocaleDefault)return this.hashLocaleDefault;if(h(this.i18nConfig.strategy)&&this.noPrefixDefault)return this.noPrefixDefault;if((i=e.params)!=null&&i.locale)return e.params.locale.toString();const r=e.path||e.fullPath||"",n=this.extractLocaleFromPath(r);return n||(this.cookieLocaleDefault?this.cookieLocaleDefault:(this.i18nConfig.defaultLocale||"en").toString())}getCurrentName(e){var i;const r=this.getCurrentLocale(e),n=(i=this.i18nConfig.locales)==null?void 0:i.find(t=>t.code===r);return(n==null?void 0:n.displayName)??null}getRouteName(e,r){return(e.name??"").toString().toString().replace("localized-","").replace(new RegExp(`-${r}$`),"")}getPluginRouteName(e,r){return this.i18nConfig.disablePageLocales?"general":this.getRouteName(e,r)}getFullPathWithBaseUrl(e,r){let i=this.router.resolve(r).fullPath;e!=null&&e.baseDefault&&(i=i.replace(new RegExp(`^/${e.code}`),""));let t=e.baseUrl;return t||(t=""),t!=null&&t.endsWith("/")&&(t=t.slice(0,-1)),t+i}switchLocaleRoute(e,r,n,i){var f,v;const t=(f=this.i18nConfig.locales)==null?void 0:f.find(g=>g.code===r),s=this.getRouteName(n,e);if(this.router.hasRoute(`localized-${s}-${r}`)){const p={...i!=null&&i[r]?{...i[r]}:{...n.params??{}}};delete p.locale,h(this.i18nConfig.strategy)||(p.locale=r);const S={name:`localized-${s}-${r}`,params:p,query:n.query,hash:n.hash};return t!=null&&t.baseUrl?this.getFullPathWithBaseUrl(t,S):S}let o=s;const u={...i!=null&&i[r]?{...i[r]}:{...n.params??{}}};delete u.locale,h(this.i18nConfig.strategy)||(s==="custom-fallback-route"?o=s:o=r!==this.i18nConfig.defaultLocale||y(this.i18nConfig.strategy)?`localized-${s}`:s,h(this.i18nConfig.strategy)||(r!==this.i18nConfig.defaultLocale||y(this.i18nConfig.strategy))&&(u.locale=r));const c={name:o,params:u,query:n.query,hash:n.hash};return h(this.i18nConfig.strategy)&&((v=this.i18nConfig.locales)==null||v.forEach((g,p)=>{c.name.endsWith(`-${g.code}`)&&(c.name=c.name.slice(0,-g.code-1))})),t!=null&&t.baseUrl?this.getFullPathWithBaseUrl(t,c):c}resolveParams(e){const r=typeof e=="object"&&"params"in e&&typeof e.params=="object"?{...e.params}:{};if(typeof e=="string"){const n=this.router.resolve(e);n&&n.params&&Object.assign(r,n.params)}return r}handlePrefixStrategy(e){if(!y(this.i18nConfig.strategy))return e;const r=this.i18nConfig.defaultLocale;let n=e;typeof e=="string"&&(n=this.router.resolve("/"+r+e));const i=this.getRouteName(n,r),t=this.resolveParams(n);return h(this.i18nConfig.strategy)||(t.locale=r),this.router.hasRoute(`localized-${i}`)?this.router.resolve({name:`localized-${i}`,query:n.query,hash:n.hash,params:t}):this.router.hasRoute(`localized-${i}-${r}`)?this.router.resolve({name:`localized-${i}-${r}`,query:n.query,hash:n.hash,params:t}):e}createLocalizedRoute(e,r,n){const i=this.router.resolve(e),t=this.getRouteName(i,n).replace(new RegExp(`-${this.i18nConfig.defaultLocale}$`),"");if(!h(this.i18nConfig.strategy)&&(!t||t==="")){let u=this.router.resolve(e).path.replace(new RegExp(`^/${n}/`),"/");return(n!==this.i18nConfig.defaultLocale||y(this.i18nConfig.strategy))&&(u="/"+n+u),this.router.resolve({path:u,query:i.query,hash:i.hash})}if(this.router.hasRoute(`localized-${t}-${n}`)){const l=this.resolveParams(i);return h(this.i18nConfig.strategy)||(l.locale=n),this.router.resolve({name:`localized-${t}-${n}`,params:l,query:i.query,hash:i.hash})}const s=n!==this.i18nConfig.defaultLocale||y(this.i18nConfig.strategy)?`localized-${t}`:t;if(!this.router.hasRoute(s)){const l=this.resolveParams(e);return delete l.locale,this.router.hasRoute(t)?this.router.resolve({name:t,params:l,query:i.query,hash:i.hash}):this.router.resolve("/")}const o=this.resolveParams(e);return delete o.locale,h(this.i18nConfig.strategy)||(n!==this.i18nConfig.defaultLocale||y(this.i18nConfig.strategy))&&(o.locale=n),this.router.resolve({name:s,params:o,query:i.query,hash:i.hash})}getLocalizedRoute(e,r,n){const i=n||this.getCurrentLocale(r),t=this.handlePrefixStrategy(e);return this.createLocalizedRoute(t,r,i)}updateCookies(e){this.i18nConfig.hashMode&&(this.setCookie("hash-locale",e),this.hashLocaleDefault=e),h(this.i18nConfig.strategy)&&(this.setCookie("no-prefix-locale",e),this.noPrefixDefault=e),!this.i18nConfig.hashMode&&!h(this.i18nConfig.strategy)&&this.cookieLocaleName&&(this.setCookie(this.cookieLocaleName,e),this.cookieLocaleDefault=e)}getCurrentRoute(){return this.router.currentRoute.value}resolveRouteWithStrategy(e,r,n){return h(this.i18nConfig.strategy)?this.router.resolve(e):r!==this.i18nConfig.defaultLocale||y(this.i18nConfig.strategy)?this.router.resolve(`/${n}${e}`):this.router.resolve(e)}switchLocaleLogic(e,r,n){const i=this.getCurrentLocale();let t;typeof n=="string"?t=this.resolveRouteWithStrategy(n,e,i):t=n??this.getCurrentRoute(),this.updateCookies(e);const s=this.switchLocaleRoute(i,e,t,r);return typeof s=="string"&&s.startsWith("http")?this.navigateTo(s,{redirectCode:200,external:!0}):(h(this.i18nConfig.strategy)&&(s.force=!0),this.router.push(s))}resolveLocalizedRoute(e,r){const n=this.getCurrentRoute(),i=this.getCurrentLocale(),t=r??i;let s;if(typeof e=="string")if(e.startsWith("/"))s=this.resolveRouteWithStrategy(e,t,i);else{const o=e;this.router.hasRoute(o)?s=this.router.resolve({name:o}):(e=`/${e}`,s=this.resolveRouteWithStrategy(e,t,i))}else s=e;return this.getLocalizedRoute(s,n,t)}}class j{formatNumber(e,r,n){return new Intl.NumberFormat(r,n).format(e)}formatDate(e,r,n){const i=new Date(e);return Number.isNaN(i.getTime())?"Invalid Date":new Intl.DateTimeFormat(r,n).format(i)}formatRelativeTime(e,r,n){const i=new Date(e);if(Number.isNaN(i.getTime()))return new Intl.RelativeTimeFormat(r,n).format(0,"second");const s=Math.floor((new Date().getTime()-i.getTime())/1e3),o=[{unit:"year",seconds:31536e3},{unit:"month",seconds:2592e3},{unit:"day",seconds:86400},{unit:"hour",seconds:3600},{unit:"minute",seconds:60},{unit:"second",seconds:1}];for(const{unit:l,seconds:u}of o){const c=Math.floor(s/u);if(c>=1)return new Intl.RelativeTimeFormat(r,n).format(-c,l)}return new Intl.RelativeTimeFormat(r,n).format(0,"second")}}exports.FormatService=j;exports.RouteService=F;exports.defaultPlural=z;exports.interpolate=T;exports.isNoPrefixStrategy=h;exports.isPrefixAndDefaultStrategy=q;exports.isPrefixExceptDefaultStrategy=L;exports.isPrefixStrategy=D;exports.useTranslationHelper=N;exports.withPrefixStrategy=y;
@@ -0,0 +1,5 @@
1
+ import { useTranslationHelper, TranslationCache } from './translation';
2
+ import { RouteService } from './route-service';
3
+ import { FormatService } from './format-service';
4
+ import { interpolate, withPrefixStrategy, isNoPrefixStrategy, isPrefixStrategy, isPrefixExceptDefaultStrategy, isPrefixAndDefaultStrategy, defaultPlural } from './helpers';
5
+ export { useTranslationHelper, interpolate, withPrefixStrategy, isNoPrefixStrategy, isPrefixStrategy, isPrefixExceptDefaultStrategy, isPrefixAndDefaultStrategy, defaultPlural, RouteService, FormatService, type TranslationCache, };
package/dist/index.mjs ADDED
@@ -0,0 +1,366 @@
1
+ const w = {}, x = {}, S = [], N = {};
2
+ function b(a) {
3
+ return Array.isArray(a) || typeof a == "object" && a !== null ? JSON.parse(JSON.stringify(a)) : a;
4
+ }
5
+ function $(a, e) {
6
+ let r = a;
7
+ if (a === null || typeof e != "string")
8
+ return null;
9
+ if (a[e])
10
+ r = a[e];
11
+ else {
12
+ const n = e.toString().split(".");
13
+ for (const i of n)
14
+ if (r && typeof r == "object" && i in r)
15
+ r = r[i];
16
+ else
17
+ return null;
18
+ }
19
+ return typeof r == "object" && r !== null ? b(r) : r ?? null;
20
+ }
21
+ function C(a) {
22
+ return typeof a == "object" && a !== null && "value" in a ? a.value : a;
23
+ }
24
+ function m(a, e, r) {
25
+ const n = C(a);
26
+ n[e] = r;
27
+ }
28
+ function d(a, e) {
29
+ return C(a)[e];
30
+ }
31
+ function P(a) {
32
+ const e = (a == null ? void 0 : a.generalLocaleCache) ?? w, r = (a == null ? void 0 : a.routeLocaleCache) ?? x, n = (a == null ? void 0 : a.dynamicTranslationsCaches) ?? S, i = (a == null ? void 0 : a.serverTranslationCache) ?? N;
33
+ return {
34
+ hasCache(t, s) {
35
+ const o = `${t}:${s}`;
36
+ return (d(i, o) ?? /* @__PURE__ */ new Map()).size > 0;
37
+ },
38
+ getCache(t, s) {
39
+ const o = `${t}:${s}`;
40
+ return d(i, o);
41
+ },
42
+ setCache(t, s, o) {
43
+ const l = `${t}:${s}`;
44
+ m(i, l, o);
45
+ },
46
+ mergeTranslation(t, s, o, l = !1) {
47
+ const u = `${t}:${s}`, c = d(r, u);
48
+ (c || l) && m(r, u, {
49
+ ...c ?? {},
50
+ ...o
51
+ });
52
+ const f = process.env.NODE_ENV !== "production";
53
+ !c && f && console.warn(`[i18n] mergeTranslation called for '${u}' which was not pre-loaded. Skipping merge. Use force: true if this is intentional.`);
54
+ },
55
+ mergeGlobalTranslation(t, s, o = !1) {
56
+ const l = d(e, t);
57
+ !o && !l && console.error(`marge: route ${t} not loaded`), m(e, t, {
58
+ ...l ?? {},
59
+ ...s
60
+ });
61
+ },
62
+ hasGeneralTranslation(t) {
63
+ return !!d(e, t);
64
+ },
65
+ hasPageTranslation(t, s) {
66
+ const o = `${t}:${s}`;
67
+ return !!d(r, o);
68
+ },
69
+ hasTranslation: (t, s) => {
70
+ const o = C(n);
71
+ for (const u of o)
72
+ if ($(u[t] || null, s) !== null)
73
+ return !0;
74
+ const l = d(e, t);
75
+ return $(l || null, s) !== null;
76
+ },
77
+ getTranslation: (t, s, o) => {
78
+ const l = `${t}:${s}`, u = d(i, l), c = u == null ? void 0 : u.get(o);
79
+ if (c)
80
+ return c;
81
+ let f = null;
82
+ const v = C(n);
83
+ for (const g of v)
84
+ if (f = $(g[t] || null, o), f !== null) break;
85
+ if (!f) {
86
+ const g = d(r, l), p = d(e, t);
87
+ f = $(g || null, o) ?? $(p || null, o);
88
+ }
89
+ if (f) {
90
+ const g = u ?? /* @__PURE__ */ new Map();
91
+ g.set(o, f), m(i, l, g);
92
+ }
93
+ return f;
94
+ },
95
+ async loadPageTranslations(t, s, o) {
96
+ const l = `${t}:${s}`;
97
+ m(r, l, { ...o });
98
+ },
99
+ async loadTranslations(t, s) {
100
+ m(e, t, { ...s });
101
+ },
102
+ clearCache() {
103
+ const t = C(e);
104
+ Object.keys(t).forEach((u) => {
105
+ m(e, u, {});
106
+ });
107
+ const s = C(r);
108
+ Object.keys(s).forEach((u) => {
109
+ m(r, u, {});
110
+ });
111
+ const o = C(n);
112
+ o.length = 0;
113
+ const l = C(i);
114
+ Object.keys(l).forEach((u) => {
115
+ const c = d(i, u);
116
+ c == null || c.clear();
117
+ });
118
+ }
119
+ };
120
+ }
121
+ function T(a, e) {
122
+ let r = a;
123
+ for (const n in e)
124
+ r = r.split(`{${n}}`).join(String(e[n]));
125
+ return r;
126
+ }
127
+ function y(a) {
128
+ return a === "prefix" || a === "prefix_and_default";
129
+ }
130
+ function h(a) {
131
+ return a === "no_prefix";
132
+ }
133
+ function D(a) {
134
+ return a === "prefix";
135
+ }
136
+ function L(a) {
137
+ return a === "prefix_except_default";
138
+ }
139
+ function q(a) {
140
+ return a === "prefix_and_default";
141
+ }
142
+ const z = (a, e, r, n, i) => {
143
+ const t = i(a, r);
144
+ if (!t)
145
+ return null;
146
+ const s = t.toString().split("|");
147
+ if (s.length === 0) return null;
148
+ const o = e < s.length ? s[e] : s[s.length - 1];
149
+ return o ? o.trim().replace("{count}", e.toString()) : null;
150
+ };
151
+ class F {
152
+ constructor(e, r, n, i, t, s, o = null, l = null) {
153
+ this.i18nConfig = e, this.router = r, this.hashLocaleDefault = n, this.noPrefixDefault = i, this.navigateTo = t, this.setCookie = s, this.cookieLocaleDefault = o, this.cookieLocaleName = l;
154
+ }
155
+ /**
156
+ * Extracts locale from URL path by checking the first path segment
157
+ * @param path - URL path (e.g., '/ru/sdfsdf' or '/en/about')
158
+ * @returns Locale code or null if not found
159
+ */
160
+ extractLocaleFromPath(e) {
161
+ var o, l;
162
+ if (!e)
163
+ return null;
164
+ const n = (o = e.split("?")[0]) == null ? void 0 : o.split("#")[0];
165
+ if (!n || n === "/")
166
+ return null;
167
+ const i = n.split("/").filter(Boolean);
168
+ if (i.length === 0)
169
+ return null;
170
+ const t = i[0];
171
+ return t && (((l = this.i18nConfig.locales) == null ? void 0 : l.map((u) => u.code)) || []).includes(t) ? t : null;
172
+ }
173
+ getCurrentLocale(e) {
174
+ var i;
175
+ if (e = e ?? this.router.currentRoute.value, this.i18nConfig.hashMode && this.hashLocaleDefault)
176
+ return this.hashLocaleDefault;
177
+ if (h(this.i18nConfig.strategy) && this.noPrefixDefault)
178
+ return this.noPrefixDefault;
179
+ if ((i = e.params) != null && i.locale)
180
+ return e.params.locale.toString();
181
+ const r = e.path || e.fullPath || "", n = this.extractLocaleFromPath(r);
182
+ return n || (this.cookieLocaleDefault ? this.cookieLocaleDefault : (this.i18nConfig.defaultLocale || "en").toString());
183
+ }
184
+ getCurrentName(e) {
185
+ var i;
186
+ const r = this.getCurrentLocale(e), n = (i = this.i18nConfig.locales) == null ? void 0 : i.find((t) => t.code === r);
187
+ return (n == null ? void 0 : n.displayName) ?? null;
188
+ }
189
+ getRouteName(e, r) {
190
+ return (e.name ?? "").toString().toString().replace("localized-", "").replace(new RegExp(`-${r}$`), "");
191
+ }
192
+ getPluginRouteName(e, r) {
193
+ return this.i18nConfig.disablePageLocales ? "general" : this.getRouteName(e, r);
194
+ }
195
+ getFullPathWithBaseUrl(e, r) {
196
+ let i = this.router.resolve(r).fullPath;
197
+ e != null && e.baseDefault && (i = i.replace(new RegExp(`^/${e.code}`), ""));
198
+ let t = e.baseUrl;
199
+ return t || (t = ""), t != null && t.endsWith("/") && (t = t.slice(0, -1)), t + i;
200
+ }
201
+ switchLocaleRoute(e, r, n, i) {
202
+ var f, v;
203
+ const t = (f = this.i18nConfig.locales) == null ? void 0 : f.find((g) => g.code === r), s = this.getRouteName(n, e);
204
+ if (this.router.hasRoute(`localized-${s}-${r}`)) {
205
+ const p = { ...i != null && i[r] ? { ...i[r] } : { ...n.params ?? {} } };
206
+ delete p.locale, h(this.i18nConfig.strategy) || (p.locale = r);
207
+ const R = {
208
+ name: `localized-${s}-${r}`,
209
+ params: p,
210
+ query: n.query,
211
+ hash: n.hash
212
+ };
213
+ return t != null && t.baseUrl ? this.getFullPathWithBaseUrl(t, R) : R;
214
+ }
215
+ let o = s;
216
+ const u = { ...i != null && i[r] ? { ...i[r] } : { ...n.params ?? {} } };
217
+ delete u.locale, h(this.i18nConfig.strategy) || (s === "custom-fallback-route" ? o = s : o = r !== this.i18nConfig.defaultLocale || y(this.i18nConfig.strategy) ? `localized-${s}` : s, h(this.i18nConfig.strategy) || (r !== this.i18nConfig.defaultLocale || y(this.i18nConfig.strategy)) && (u.locale = r));
218
+ const c = {
219
+ name: o,
220
+ params: u,
221
+ query: n.query,
222
+ hash: n.hash
223
+ };
224
+ return h(this.i18nConfig.strategy) && ((v = this.i18nConfig.locales) == null || v.forEach((g, p) => {
225
+ c.name.endsWith(`-${g.code}`) && (c.name = c.name.slice(0, -g.code - 1));
226
+ })), t != null && t.baseUrl ? this.getFullPathWithBaseUrl(t, c) : c;
227
+ }
228
+ resolveParams(e) {
229
+ const r = typeof e == "object" && "params" in e && typeof e.params == "object" ? { ...e.params } : {};
230
+ if (typeof e == "string") {
231
+ const n = this.router.resolve(e);
232
+ n && n.params && Object.assign(r, n.params);
233
+ }
234
+ return r;
235
+ }
236
+ handlePrefixStrategy(e) {
237
+ if (!y(this.i18nConfig.strategy))
238
+ return e;
239
+ const r = this.i18nConfig.defaultLocale;
240
+ let n = e;
241
+ typeof e == "string" && (n = this.router.resolve("/" + r + e));
242
+ const i = this.getRouteName(n, r), t = this.resolveParams(n);
243
+ return h(this.i18nConfig.strategy) || (t.locale = r), this.router.hasRoute(`localized-${i}`) ? this.router.resolve({
244
+ name: `localized-${i}`,
245
+ query: n.query,
246
+ hash: n.hash,
247
+ params: t
248
+ }) : this.router.hasRoute(`localized-${i}-${r}`) ? this.router.resolve({
249
+ name: `localized-${i}-${r}`,
250
+ query: n.query,
251
+ hash: n.hash,
252
+ params: t
253
+ }) : e;
254
+ }
255
+ createLocalizedRoute(e, r, n) {
256
+ const i = this.router.resolve(e), t = this.getRouteName(i, n).replace(new RegExp(`-${this.i18nConfig.defaultLocale}$`), "");
257
+ if (!h(this.i18nConfig.strategy) && (!t || t === "")) {
258
+ let u = this.router.resolve(e).path.replace(new RegExp(`^/${n}/`), "/");
259
+ return (n !== this.i18nConfig.defaultLocale || y(this.i18nConfig.strategy)) && (u = "/" + n + u), this.router.resolve({
260
+ path: u,
261
+ query: i.query,
262
+ hash: i.hash
263
+ });
264
+ }
265
+ if (this.router.hasRoute(`localized-${t}-${n}`)) {
266
+ const l = this.resolveParams(i);
267
+ return h(this.i18nConfig.strategy) || (l.locale = n), this.router.resolve({
268
+ name: `localized-${t}-${n}`,
269
+ params: l,
270
+ query: i.query,
271
+ hash: i.hash
272
+ });
273
+ }
274
+ const s = n !== this.i18nConfig.defaultLocale || y(this.i18nConfig.strategy) ? `localized-${t}` : t;
275
+ if (!this.router.hasRoute(s)) {
276
+ const l = this.resolveParams(e);
277
+ return delete l.locale, this.router.hasRoute(t) ? this.router.resolve({
278
+ name: t,
279
+ params: l,
280
+ query: i.query,
281
+ hash: i.hash
282
+ }) : this.router.resolve("/");
283
+ }
284
+ const o = this.resolveParams(e);
285
+ return delete o.locale, h(this.i18nConfig.strategy) || (n !== this.i18nConfig.defaultLocale || y(this.i18nConfig.strategy)) && (o.locale = n), this.router.resolve({
286
+ name: s,
287
+ params: o,
288
+ query: i.query,
289
+ hash: i.hash
290
+ });
291
+ }
292
+ getLocalizedRoute(e, r, n) {
293
+ const i = n || this.getCurrentLocale(r), t = this.handlePrefixStrategy(e);
294
+ return this.createLocalizedRoute(t, r, i);
295
+ }
296
+ updateCookies(e) {
297
+ this.i18nConfig.hashMode && (this.setCookie("hash-locale", e), this.hashLocaleDefault = e), h(this.i18nConfig.strategy) && (this.setCookie("no-prefix-locale", e), this.noPrefixDefault = e), !this.i18nConfig.hashMode && !h(this.i18nConfig.strategy) && this.cookieLocaleName && (this.setCookie(this.cookieLocaleName, e), this.cookieLocaleDefault = e);
298
+ }
299
+ getCurrentRoute() {
300
+ return this.router.currentRoute.value;
301
+ }
302
+ resolveRouteWithStrategy(e, r, n) {
303
+ return h(this.i18nConfig.strategy) ? this.router.resolve(e) : r !== this.i18nConfig.defaultLocale || y(this.i18nConfig.strategy) ? this.router.resolve(`/${n}${e}`) : this.router.resolve(e);
304
+ }
305
+ switchLocaleLogic(e, r, n) {
306
+ const i = this.getCurrentLocale();
307
+ let t;
308
+ typeof n == "string" ? t = this.resolveRouteWithStrategy(n, e, i) : t = n ?? this.getCurrentRoute(), this.updateCookies(e);
309
+ const s = this.switchLocaleRoute(i, e, t, r);
310
+ return typeof s == "string" && s.startsWith("http") ? this.navigateTo(s, { redirectCode: 200, external: !0 }) : (h(this.i18nConfig.strategy) && (s.force = !0), this.router.push(s));
311
+ }
312
+ resolveLocalizedRoute(e, r) {
313
+ const n = this.getCurrentRoute(), i = this.getCurrentLocale(), t = r ?? i;
314
+ let s;
315
+ if (typeof e == "string")
316
+ if (e.startsWith("/"))
317
+ s = this.resolveRouteWithStrategy(e, t, i);
318
+ else {
319
+ const o = e;
320
+ this.router.hasRoute(o) ? s = this.router.resolve({ name: o }) : (e = `/${e}`, s = this.resolveRouteWithStrategy(e, t, i));
321
+ }
322
+ else
323
+ s = e;
324
+ return this.getLocalizedRoute(s, n, t);
325
+ }
326
+ }
327
+ class j {
328
+ formatNumber(e, r, n) {
329
+ return new Intl.NumberFormat(r, n).format(e);
330
+ }
331
+ formatDate(e, r, n) {
332
+ const i = new Date(e);
333
+ return Number.isNaN(i.getTime()) ? "Invalid Date" : new Intl.DateTimeFormat(r, n).format(i);
334
+ }
335
+ formatRelativeTime(e, r, n) {
336
+ const i = new Date(e);
337
+ if (Number.isNaN(i.getTime()))
338
+ return new Intl.RelativeTimeFormat(r, n).format(0, "second");
339
+ const s = Math.floor(((/* @__PURE__ */ new Date()).getTime() - i.getTime()) / 1e3), o = [
340
+ { unit: "year", seconds: 31536e3 },
341
+ { unit: "month", seconds: 2592e3 },
342
+ { unit: "day", seconds: 86400 },
343
+ { unit: "hour", seconds: 3600 },
344
+ { unit: "minute", seconds: 60 },
345
+ { unit: "second", seconds: 1 }
346
+ ];
347
+ for (const { unit: l, seconds: u } of o) {
348
+ const c = Math.floor(s / u);
349
+ if (c >= 1)
350
+ return new Intl.RelativeTimeFormat(r, n).format(-c, l);
351
+ }
352
+ return new Intl.RelativeTimeFormat(r, n).format(0, "second");
353
+ }
354
+ }
355
+ export {
356
+ j as FormatService,
357
+ F as RouteService,
358
+ z as defaultPlural,
359
+ T as interpolate,
360
+ h as isNoPrefixStrategy,
361
+ q as isPrefixAndDefaultStrategy,
362
+ L as isPrefixExceptDefaultStrategy,
363
+ D as isPrefixStrategy,
364
+ P as useTranslationHelper,
365
+ y as withPrefixStrategy
366
+ };
@@ -0,0 +1,40 @@
1
+ import { NavigationFailure, RouteLocationAsPathGeneric, RouteLocationNamedRaw, RouteLocationNormalizedLoaded, RouteLocationRaw, RouteLocationResolved, RouteLocationResolvedGeneric, Router } from 'vue-router';
2
+ import { I18nRouteParams, Locale, ModuleOptionsExtend } from '@i18n-micro/types';
3
+ interface NavigateToInterface {
4
+ replace?: boolean;
5
+ redirectCode?: number;
6
+ external?: boolean;
7
+ }
8
+ export declare class RouteService {
9
+ private i18nConfig;
10
+ private router;
11
+ private hashLocaleDefault;
12
+ private noPrefixDefault;
13
+ private navigateTo;
14
+ private setCookie;
15
+ private cookieLocaleDefault;
16
+ private cookieLocaleName;
17
+ constructor(i18nConfig: ModuleOptionsExtend, router: Router, hashLocaleDefault: string | null | undefined, noPrefixDefault: string | null | undefined, navigateTo: (to: RouteLocationRaw | undefined | null, options?: NavigateToInterface) => Promise<void | NavigationFailure | false> | false | void | RouteLocationRaw, setCookie: (name: string, value: string) => void, cookieLocaleDefault?: string | null | undefined, cookieLocaleName?: string | null | undefined);
18
+ /**
19
+ * Extracts locale from URL path by checking the first path segment
20
+ * @param path - URL path (e.g., '/ru/sdfsdf' or '/en/about')
21
+ * @returns Locale code or null if not found
22
+ */
23
+ private extractLocaleFromPath;
24
+ getCurrentLocale(route?: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric): string;
25
+ getCurrentName(route: RouteLocationNormalizedLoaded | RouteLocationResolvedGeneric): string | null;
26
+ getRouteName(route: RouteLocationResolvedGeneric | RouteLocationNamedRaw, locale: string): string;
27
+ getPluginRouteName(route: RouteLocationResolvedGeneric | RouteLocationNamedRaw, locale: string): string;
28
+ getFullPathWithBaseUrl(currentLocale: Locale, route: RouteLocationRaw): string;
29
+ switchLocaleRoute(fromLocale: string, toLocale: string, route: RouteLocationResolvedGeneric | RouteLocationNamedRaw, i18nRouteParams: I18nRouteParams): RouteLocationRaw;
30
+ private resolveParams;
31
+ private handlePrefixStrategy;
32
+ private createLocalizedRoute;
33
+ getLocalizedRoute(to: RouteLocationResolvedGeneric | RouteLocationAsPathGeneric | RouteLocationNamedRaw | string, route: RouteLocationNormalizedLoaded, locale?: string): RouteLocationResolved;
34
+ updateCookies(toLocale: string): void;
35
+ getCurrentRoute(): RouteLocationNormalizedLoaded;
36
+ private resolveRouteWithStrategy;
37
+ switchLocaleLogic(toLocale: string, i18nRouteParams: I18nRouteParams, to?: RouteLocationNamedRaw | RouteLocationResolvedGeneric | string): string | false | void | import('vue-router').RouteLocationAsRelativeGeneric | RouteLocationAsPathGeneric | Promise<false | void | NavigationFailure>;
38
+ resolveLocalizedRoute(to: RouteLocationNamedRaw | RouteLocationAsPathGeneric | string, locale?: string): RouteLocationResolved;
39
+ }
40
+ export {};