@i18n-micro/astro 1.2.1 → 1.3.0

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.
@@ -1,6 +1,6 @@
1
1
  import { Params, Translations } from '@i18n-micro/types';
2
2
  /**
3
- * Состояние i18n для клиентских островов
3
+ * I18n state for client islands
4
4
  */
5
5
  export interface I18nState {
6
6
  locale: string;
@@ -9,7 +9,7 @@ export interface I18nState {
9
9
  currentRoute: string;
10
10
  }
11
11
  /**
12
- * Чистая функция для получения перевода из состояния
12
+ * Pure function to get a translation from state
13
13
  *
14
14
  * Note: This is a simplified version optimized for client-side islands.
15
15
  * It supports basic translation lookup, interpolation, and fallback to general translations.
@@ -18,6 +18,6 @@ export interface I18nState {
18
18
  */
19
19
  export declare function translate(state: I18nState, key: string, params?: Params, defaultValue?: string | null, routeName?: string): string | number | boolean | Translations | null;
20
20
  /**
21
- * Проверяет наличие перевода в состоянии
21
+ * Checks if a translation exists in the state
22
22
  */
23
23
  export declare function hasTranslation(state: I18nState, key: string, routeName?: string): boolean;
@@ -3,12 +3,12 @@ import { Writable } from 'svelte/store';
3
3
  import { I18nClientProps } from '../utils';
4
4
  import { I18nState } from './core';
5
5
  /**
6
- * Создает Svelte store для i18n состояния
6
+ * Creates a Svelte store for i18n state
7
7
  */
8
8
  export declare function createI18nStore(props: I18nClientProps): Writable<I18nState>;
9
9
  /**
10
- * Хук для использования i18n в Svelte компонентах
11
- * Используйте в <script> блоке компонента
10
+ * Hook for using i18n in Svelte components
11
+ * Use in the component's <script> block
12
12
  */
13
13
  export declare function useAstroI18n(store: Writable<I18nState>): {
14
14
  store: Writable<I18nState>;
@@ -13,7 +13,7 @@ function F(o) {
13
13
  function P(o) {
14
14
  const n = () => v(o), c = (t, e, r, a) => S(n(), t, e, r, a);
15
15
  return {
16
- // Store для реактивности в шаблонах (используйте $i18nStore в шаблонах)
16
+ // Store for reactivity in templates (use $i18nStore in templates)
17
17
  store: o,
18
18
  // Translation methods
19
19
  t: c,
@@ -38,7 +38,7 @@ function P(o) {
38
38
  return l.formatRelativeTime(t, r.locale, e);
39
39
  },
40
40
  has: (t, e) => L(n(), t, e),
41
- // Геттеры для текущего состояния (для использования в скриптах)
41
+ // Getters for current state (for use in scripts)
42
42
  get locale() {
43
43
  return n().locale;
44
44
  },
@@ -3,12 +3,12 @@ import { Ref } from 'vue';
3
3
  import { I18nClientProps } from '../utils';
4
4
  import { I18nState } from './core';
5
5
  /**
6
- * Инициализирует i18n провайдер для Vue острова
7
- * Вызывайте в корневом компоненте острова
6
+ * Initializes the i18n provider for a Vue island
7
+ * Call in the island's root component
8
8
  */
9
9
  export declare function provideI18n(props: I18nClientProps): Ref<I18nState>;
10
10
  /**
11
- * Хук для использования i18n в Vue компонентах
11
+ * Hook for using i18n in Vue components
12
12
  */
13
13
  export declare function useAstroI18n(): {
14
14
  t: (key: TranslationKey, params?: Params, defaultValue?: string | null, routeName?: string) => CleanTranslation;
@@ -30,6 +30,5 @@ export declare class AstroI18n extends BaseI18n {
30
30
  addTranslations(locale: string, translations: Translations, merge?: boolean): void;
31
31
  addRouteTranslations(locale: string, routeName: string, translations: Translations, merge?: boolean): void;
32
32
  mergeTranslations(locale: string, routeName: string, translations: Translations): void;
33
- mergeGlobalTranslations(locale: string, translations: Translations): void;
34
33
  clearCache(): void;
35
34
  }
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const T=require("@i18n-micro/core"),b=require("node:fs"),I=require("node:path");class j extends T.BaseI18n{constructor(e){const s=e._storage||{translations:new Map};if(super({storage:s,plural:e.plural,missingWarn:e.missingWarn,missingHandler:e.missingHandler}),this.initialMessages={},this.storage=s,this._locale=e.locale,this._fallbackLocale=e.fallbackLocale||e.locale,this._currentRoute="general",e.messages){this.initialMessages={...e.messages};for(const[a,i]of Object.entries(e.messages))this.helper.loadTranslations(a,i)}}cloneStorage(e){const s=new Map;for(const[a,i]of e.translations)s.set(a,{...i});return{translations:s}}clone(e){const s=this.cloneStorage(this.storage);return new j({locale:e||this._locale,fallbackLocale:this._fallbackLocale,plural:this.pluralFunc,missingWarn:this.missingWarn,missingHandler:this.missingHandler,_storage:s})}get locale(){return this._locale}set locale(e){this._locale=e}get fallbackLocale(){return this._fallbackLocale}set fallbackLocale(e){this._fallbackLocale=e}setRoute(e){this._currentRoute=e}getLocale(){return this._locale}getFallbackLocale(){return this._fallbackLocale}getRoute(){return this._currentRoute}getRouteTranslations(e,s){const a=`${e}:${s}`;return this.storage.translations.get(a)??null}addTranslations(e,s,a=!0){super.loadTranslationsCore(e,s,a)}addRouteTranslations(e,s,a,i=!0){super.loadRouteTranslationsCore(e,s,a,i)}mergeTranslations(e,s,a){this.helper.mergeTranslation(e,s,a,!0)}mergeGlobalTranslations(e,s){this.helper.mergeGlobalTranslation(e,s,!0)}clearCache(){const e={...this.initialMessages};if(super.clearCache(),Object.keys(e).length>0)for(const[s,a]of Object.entries(e))this.helper.loadTranslations(s,a)}}let $=null;function D(){return $}function C(n){const{locale:e,fallbackLocale:s,translationDir:a,routingStrategy:i}=n;return $=i||null,{name:"@i18n-micro/astro",hooks:{"astro:config:setup":c=>{const{updateConfig:f}=c,t="virtual:i18n-micro/config",l=`\0${t}`,r={defaultLocale:e,fallbackLocale:s||e,locales:n.locales||[],localeCodes:(n.locales||[]).map(o=>o.code),translationDir:a||null,autoDetect:n.autoDetect??!0,redirectToDefault:n.redirectToDefault??!1,localeCookie:n.localeCookie===null?null:n.localeCookie||"i18n-locale",missingWarn:n.missingWarn??!1};f({vite:{plugins:[{name:"vite-plugin-i18n-micro-config",resolveId(o){if(o===t)return l},load(o){if(o===l)return`export const config = ${JSON.stringify(r)}`}}]}})},"astro:config:done":c=>{const{injectTypes:f}=c;f({filename:"i18n-micro-env.d.ts",content:`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const j=require("@i18n-micro/core"),b=require("node:fs"),I=require("node:path");class w extends j.BaseI18n{constructor(e){const s=e._storage||{translations:new Map};if(super({storage:s,plural:e.plural,missingWarn:e.missingWarn,missingHandler:e.missingHandler}),this.initialMessages={},this.storage=s,this._locale=e.locale,this._fallbackLocale=e.fallbackLocale||e.locale,this._currentRoute="index",e.messages){this.initialMessages={...e.messages};for(const[o,i]of Object.entries(e.messages))this.helper.loadTranslations(o,i)}}cloneStorage(e){const s=new Map;for(const[o,i]of e.translations)s.set(o,{...i});return{translations:s}}clone(e){const s=this.cloneStorage(this.storage);return new w({locale:e||this._locale,fallbackLocale:this._fallbackLocale,plural:this.pluralFunc,missingWarn:this.missingWarn,missingHandler:this.missingHandler,_storage:s})}get locale(){return this._locale}set locale(e){this._locale=e}get fallbackLocale(){return this._fallbackLocale}set fallbackLocale(e){this._fallbackLocale=e}setRoute(e){this._currentRoute=e}getLocale(){return this._locale}getFallbackLocale(){return this._fallbackLocale}getRoute(){return this._currentRoute}getRouteTranslations(e,s){const o=`${e}:${s}`;return this.storage.translations.get(o)??null}addTranslations(e,s,o=!0){super.loadTranslationsCore(e,s,o)}addRouteTranslations(e,s,o,i=!0){super.loadRouteTranslationsCore(e,s,o,i)}mergeTranslations(e,s,o){this.helper.mergeTranslation(e,s,o,!0)}clearCache(){const e={...this.initialMessages};if(super.clearCache(),Object.keys(e).length>0)for(const[s,o]of Object.entries(e))this.helper.loadTranslations(s,o)}}let $=null;function D(){return $}function C(n){const{locale:e,fallbackLocale:s,translationDir:o,routingStrategy:i}=n;return $=i||null,{name:"@i18n-micro/astro",hooks:{"astro:config:setup":c=>{const{updateConfig:f}=c,t="virtual:i18n-micro/config",l=`\0${t}`,r={defaultLocale:e,fallbackLocale:s||e,locales:n.locales||[],localeCodes:(n.locales||[]).map(a=>a.code),translationDir:o||null,autoDetect:n.autoDetect??!0,redirectToDefault:n.redirectToDefault??!1,localeCookie:n.localeCookie===null?null:n.localeCookie||"i18n-locale",missingWarn:n.missingWarn??!1};f({vite:{plugins:[{name:"vite-plugin-i18n-micro-config",resolveId(a){if(a===t)return l},load(a){if(a===l)return`export const config = ${JSON.stringify(r)}`}}]}})},"astro:config:done":c=>{const{injectTypes:f}=c;f({filename:"i18n-micro-env.d.ts",content:`
2
2
  /// <reference types="@i18n-micro/astro/env" />
3
3
 
4
4
  declare module 'virtual:i18n-micro/config' {
@@ -14,4 +14,4 @@
14
14
  missingWarn: boolean | null;
15
15
  }
16
16
  }
17
- `})}}}}function N(n){return new j(n)}function F(n){const{translationDir:e,rootDir:s=process.cwd(),disablePageLocales:a=!1}=n,i=I.resolve(s,e);if(!b.existsSync(i))return console.warn(`[i18n] Translation directory not found: ${i}`),{general:{},routes:{}};const c={},f={},t=(l,r="")=>{if(!b.existsSync(l))return;const o=b.readdirSync(l);for(const u of o){const h=I.join(l,u);if(b.statSync(h).isDirectory())u==="pages"&&!a?t(h,""):r||a?t(h,r):t(h,u);else if(u.endsWith(".json")){const m=u.replace(".json","");try{const g=b.readFileSync(h,"utf-8"),p=JSON.parse(g);r&&!a?(f[r]||(f[r]={}),f[r][m]=p):c[m]=p}catch(g){console.error(`[i18n] Failed to load translation file: ${h}`,g)}}}};return t(i),{general:c,routes:f}}function q(n,e){const{general:s,routes:a}=F(e);for(const[i,c]of Object.entries(s))n.addTranslations(i,c,!1);for(const[i,c]of Object.entries(a))for(const[f,t]of Object.entries(c))n.addRouteTranslations(f,i,t,!1)}function M(n){const{i18n:e,defaultLocale:s,locales:a,localeObjects:i,autoDetect:c=!0,redirectToDefault:f=!1,routingStrategy:t}=n,l=t||D();return async(r,o)=>{if(r.locals.locale&&r.locals.i18n)return o();const u=r.url,h=u.pathname;if(!l){const S=e.clone(s),O=h==="/"||h===""?"index":h.split("/").filter(Boolean).join("-");return S.setRoute(O),r.locals.i18n=S,r.locals.locale=s,r.locals.defaultLocale=s,r.locals.locales=i||a.map(_=>({code:_})),r.locals.currentUrl=u,o()}const d={...l,getCurrentPath:()=>h,getRoute:()=>({fullPath:u.pathname+u.search,query:Object.fromEntries(u.searchParams)})},g=h.split("/").filter(Boolean)[0],p=g!==void 0&&a.includes(g);let P;p&&g?P=g:d.getLocaleFromPath?P=d.getLocaleFromPath(h,s,a):P=s;const L=e.clone(P),y=d.getRouteName?d.getRouteName(h,a):"general";return L.setRoute(y),r.locals.i18n=L,r.locals.locale=P,r.locals.defaultLocale=s,r.locals.locales=i||a.map(S=>({code:S})),r.locals.currentUrl=u,r.locals.routingStrategy=d,o()}}function A(n){const e=[],s=n.split(",");for(const a of s){const[i,c="1.0"]=a.trim().split(";q=");if(Number.parseFloat(c)>0&&i){const t=i.split("-")[0]?.toLowerCase();t&&(e.push(t),i!==t&&e.push(i.toLowerCase()))}}return e}function W(n,e,s,a,i,c="i18n-locale"){const f=D();let t=a;if(f?.getLocaleFromPath)t=f.getLocaleFromPath(n,a,i);else{const r=n.split("/").filter(Boolean)[0];r&&i.includes(r)&&(t=r)}if(c!==null&&t===a&&e.get(c)){const l=e.get(c)?.value;l&&i.includes(l)&&(t=l)}if(t===a)try{const l=s.get("accept-language");if(l){const r=A(l);for(const o of r)if(i.includes(o)){t=o;break}}}catch{}return t}function z(n,e,s){const a=n.map(o=>o.code),i=(o,u=[])=>{const h=o.replace(/^\//,"").replace(/\/$/,"");if(!h)return"index";const d=h.split("/").filter(Boolean),m=d[0];return m&&u.includes(m)&&d.shift(),d.length===0?"index":d.join("-")},c=(o,u="en",h=[])=>{const m=o.split("/").filter(Boolean)[0];return m&&h.includes(m)?m:u},f=(o,u,h=[],d)=>{const m=o.split("/").filter(Boolean),g=m[0];return g&&h.includes(g)&&m.shift(),(u!==d||d===void 0)&&m.unshift(u),`/${m.join("/")}`},t=(o,u,h=[],d)=>{const g=(o.replace(/^\//,"").replace(/\/$/,"")||"").split("/").filter(Boolean),p=g[0];return p&&h.includes(p)&&g.shift(),(u!==d||d===void 0)&&g.unshift(u),`/${g.join("/")}`};return{getCurrentPath:()=>s?s().pathname:typeof window<"u"?window.location.pathname:"/",getRouteName:i,getLocaleFromPath:c,switchLocalePath:f,localizePath:t,removeLocaleFromPath:(o,u=[])=>{const h=o.split("/").filter(Boolean),d=h[0];return d&&u.includes(d)&&h.shift(),`/${h.join("/")}`},resolvePath:(o,u)=>{const h=typeof o=="string"?o:o.path||"/";return t(h,u,a,e)},getRoute:()=>{if(s){const o=s();return{fullPath:o.pathname+o.search,query:Object.fromEntries(o.searchParams)}}if(typeof window<"u"){const o=new URL(window.location.href);return{fullPath:o.pathname+o.search,query:Object.fromEntries(o.searchParams)}}return{fullPath:"/",query:{}}},push:o=>{typeof window<"u"&&(window.location.href=o.path)},replace:o=>{typeof window<"u"&&window.location.replace(o.path)}}}function H(n,e=[]){const s=n.replace(/^\//,"").replace(/\/$/,"");if(!s)return"index";const a=s.split("/").filter(Boolean),i=a[0];return i&&e.includes(i)&&a.shift(),a.length===0?"index":a.join("-")}function G(n,e="en",s=[]){const i=n.split("/").filter(Boolean)[0];return i&&s.includes(i)?i:e}function U(n,e,s=[],a){const i=n.split("/").filter(Boolean),c=i[0];return c&&s.includes(c)&&i.shift(),(e!==a||a===void 0)&&i.unshift(e),`/${i.join("/")}`}function E(n,e,s=[],a){const c=(n.replace(/^\//,"").replace(/\/$/,"")||"").split("/").filter(Boolean),f=c[0];return f&&s.includes(f)&&c.shift(),(e!==a||a===void 0)&&c.unshift(e),`/${c.join("/")}`}function J(n,e=[]){const s=n.split("/").filter(Boolean),a=s[0];return a&&e.includes(a)&&s.shift(),`/${s.join("/")}`}function v(n){const e=n.locals.i18n;if(!e)throw new Error("i18n instance not found. Make sure i18n middleware is configured.");return e}function w(n){return n.locals.locale||"en"}function R(n){return n.locals.defaultLocale||"en"}function k(n){return n.locals.locales||[]}function B(n){return n.locals.routingStrategy||null}function V(n){const e=v(n),s=w(n),a=R(n),i=k(n),c=i.map(t=>t.code),f=B(n);return{locale:s,defaultLocale:a,locales:i,t:(t,l,r,o)=>e.t(t,l,r,o),ts:(t,l,r,o)=>e.ts(t,l,r,o),tc:(t,l,r)=>e.tc(t,l,r),tn:(t,l)=>e.tn(t,l),td:(t,l)=>e.td(t,l),tdr:(t,l)=>e.tdr(t,l),has:(t,l)=>e.has(t,l),getRoute:()=>e.getRoute(),getRouteName:t=>{const l=t||n.url.pathname;if(f?.getRouteName)return f.getRouteName(l,c);const r=l.replace(/^\//,"").replace(/\/$/,"");if(!r)return"index";const o=r.split("/").filter(Boolean),u=o[0];return u&&c.includes(u)&&o.shift(),o.length===0?"index":o.join("-")},getLocaleFromPath:t=>{const l=t||n.url.pathname;if(f?.getLocaleFromPath)return f.getLocaleFromPath(l,a,c);const o=l.split("/").filter(Boolean)[0];return o&&c.includes(o)?o:a},switchLocalePath:t=>{if(f?.switchLocalePath)return f.switchLocalePath(n.url.pathname,t,c,a);const l=n.url.pathname.split("/").filter(Boolean),r=l[0];return r&&c.includes(r)&&l.shift(),t!==a&&l.unshift(t),`/${l.join("/")}`},localizePath:(t,l)=>{if(f?.localizePath)return f.localizePath(t,l||s,c,a);const o=(t.replace(/^\//,"").replace(/\/$/,"")||"").split("/").filter(Boolean),u=o[0];return u&&c.includes(u)&&o.shift(),l&&l!==a&&o.unshift(l),`/${o.join("/")}`},getI18n:()=>e,getBasePath:t=>{const o=(t||n.url).pathname.split("/").filter(Boolean),u=o[0];return u&&c.includes(u)&&o.shift(),o.length>0?`/${o.join("/")}`:"/"},addTranslations:(t,l,r=!0)=>{e.addTranslations(t,l,r)},addRouteTranslations:(t,l,r,o=!0)=>{e.addRouteTranslations(t,l,r,o)},mergeTranslations:(t,l,r)=>{e.mergeTranslations(t,l,r)},mergeGlobalTranslations:(t,l)=>{e.mergeGlobalTranslations(t,l)},clearCache:()=>{e.clearCache()}}}function K(n,e={}){const{baseUrl:s="/",addDirAttribute:a=!0,addSeoAttributes:i=!0}=e,c=w(n),f=R(n),t=k(n),l=t.find(g=>g.code===c);if(!l)return{htmlAttrs:{},link:[],meta:[]};const r=l.iso||c,o=l.dir||"auto",u={htmlAttrs:{lang:r,...a?{dir:o}:{}},link:[],meta:[]};if(!i)return u;const h=`${s}${n.url.pathname}`;u.link.push({rel:"canonical",href:h});const d=B(n),m=t.map(g=>g.code);for(const g of t){if(g.code===c)continue;let p=n.url.pathname;if(d?.switchLocalePath)p=d.switchLocalePath(n.url.pathname,g.code,m,f);else{const L=n.url.pathname.split("/").filter(Boolean),y=L[0];y&&m.includes(y)&&L.shift(),g.code!==f&&L.unshift(g.code),p=`/${L.join("/")}`}const P=`${s}${p}`;u.link.push({rel:"alternate",href:P,hreflang:g.code}),g.iso&&g.iso!==g.code&&u.link.push({rel:"alternate",href:P,hreflang:g.iso})}u.meta.push({property:"og:locale",content:r}),u.meta.push({property:"og:url",content:h});for(const g of t)g.code!==c&&u.meta.push({property:"og:locale:alternate",content:g.iso||g.code});return u}function Q(n,e,s){const a=e.split(".");let i=n;for(let f=0;f<a.length-1;f++){const t=a[f];i[t]||(i[t]={}),i=i[t]}const c=a[a.length-1];c!==void 0&&(i[c]=s)}function X(n,e){const s=v(n),a=w(n),i=R(n),c=s.getRoute(),f={};if(e&&e.length>0){const t={};for(const l of e){const r=s.t(l,void 0,void 0,c);r!=null&&r!==l&&Q(t,l,r)}Object.keys(t).length>0&&(f[c]=t)}else{const t=s.getRouteTranslations(a,c);t&&(f[c]=t)}return{locale:a,fallbackLocale:i,currentRoute:c,translations:f}}Object.defineProperty(exports,"FormatService",{enumerable:!0,get:()=>T.FormatService});Object.defineProperty(exports,"defaultPlural",{enumerable:!0,get:()=>T.defaultPlural});Object.defineProperty(exports,"interpolate",{enumerable:!0,get:()=>T.interpolate});exports.AstroI18n=j;exports.createAstroRouterAdapter=z;exports.createI18n=N;exports.createI18nMiddleware=M;exports.detectLocale=W;exports.getDefaultLocale=R;exports.getI18n=v;exports.getI18nProps=X;exports.getLocale=w;exports.getLocaleFromPath=G;exports.getLocales=k;exports.getRouteName=H;exports.i18nIntegration=C;exports.loadTranslationsFromDir=F;exports.loadTranslationsIntoI18n=q;exports.localizePath=E;exports.removeLocaleFromPath=J;exports.switchLocalePath=U;exports.useI18n=V;exports.useLocaleHead=K;
17
+ `})}}}}function N(n){return new w(n)}function F(n){const{translationDir:e,rootDir:s=process.cwd(),disablePageLocales:o=!1}=n,i=I.resolve(s,e);if(!b.existsSync(i))return console.warn(`[i18n] Translation directory not found: ${i}`),{root:{},routes:{}};const c={},f={},t=(l,r="")=>{if(!b.existsSync(l))return;const a=b.readdirSync(l);for(const u of a){const h=I.join(l,u);if(b.statSync(h).isDirectory())u==="pages"&&!o?t(h,""):r||o?t(h,r):t(h,u);else if(u.endsWith(".json")){const m=u.replace(".json","");try{const g=b.readFileSync(h,"utf-8"),p=JSON.parse(g);r&&!o?(f[r]||(f[r]={}),f[r][m]=p):c[m]=p}catch(g){console.error(`[i18n] Failed to load translation file: ${h}`,g)}}}};return t(i),{root:c,routes:f}}function q(n,e){const{root:s,routes:o}=F(e);for(const[i,c]of Object.entries(s))n.addTranslations(i,c,!1);for(const[i,c]of Object.entries(o))for(const[f,t]of Object.entries(c)){const l=s[f]||{};n.addRouteTranslations(f,i,{...l,...t},!1)}}function M(n){const{i18n:e,defaultLocale:s,locales:o,localeObjects:i,autoDetect:c=!0,redirectToDefault:f=!1,routingStrategy:t}=n,l=t||D();return async(r,a)=>{if(r.locals.locale&&r.locals.i18n)return a();const u=r.url,h=u.pathname;if(!l){const S=e.clone(s),O=h==="/"||h===""?"index":h.split("/").filter(Boolean).join("-");return S.setRoute(O),r.locals.i18n=S,r.locals.locale=s,r.locals.defaultLocale=s,r.locals.locales=i||o.map(_=>({code:_})),r.locals.currentUrl=u,a()}const d={...l,getCurrentPath:()=>h,getRoute:()=>({fullPath:u.pathname+u.search,query:Object.fromEntries(u.searchParams)})},g=h.split("/").filter(Boolean)[0],p=g!==void 0&&o.includes(g);let P;p&&g?P=g:d.getLocaleFromPath?P=d.getLocaleFromPath(h,s,o):P=s;const L=e.clone(P),y=d.getRouteName?d.getRouteName(h,o):"index";return L.setRoute(y),r.locals.i18n=L,r.locals.locale=P,r.locals.defaultLocale=s,r.locals.locales=i||o.map(S=>({code:S})),r.locals.currentUrl=u,r.locals.routingStrategy=d,a()}}function A(n){const e=[],s=n.split(",");for(const o of s){const[i,c="1.0"]=o.trim().split(";q=");if(Number.parseFloat(c)>0&&i){const t=i.split("-")[0]?.toLowerCase();t&&(e.push(t),i!==t&&e.push(i.toLowerCase()))}}return e}function W(n,e,s,o,i,c="i18n-locale"){const f=D();let t=o;if(f?.getLocaleFromPath)t=f.getLocaleFromPath(n,o,i);else{const r=n.split("/").filter(Boolean)[0];r&&i.includes(r)&&(t=r)}if(c!==null&&t===o&&e.get(c)){const l=e.get(c)?.value;l&&i.includes(l)&&(t=l)}if(t===o)try{const l=s.get("accept-language");if(l){const r=A(l);for(const a of r)if(i.includes(a)){t=a;break}}}catch{}return t}function z(n,e,s){const o=n.map(a=>a.code),i=(a,u=[])=>{const h=a.replace(/^\//,"").replace(/\/$/,"");if(!h)return"index";const d=h.split("/").filter(Boolean),m=d[0];return m&&u.includes(m)&&d.shift(),d.length===0?"index":d.join("-")},c=(a,u="en",h=[])=>{const m=a.split("/").filter(Boolean)[0];return m&&h.includes(m)?m:u},f=(a,u,h=[],d)=>{const m=a.split("/").filter(Boolean),g=m[0];return g&&h.includes(g)&&m.shift(),(u!==d||d===void 0)&&m.unshift(u),`/${m.join("/")}`},t=(a,u,h=[],d)=>{const g=(a.replace(/^\//,"").replace(/\/$/,"")||"").split("/").filter(Boolean),p=g[0];return p&&h.includes(p)&&g.shift(),(u!==d||d===void 0)&&g.unshift(u),`/${g.join("/")}`};return{getCurrentPath:()=>s?s().pathname:typeof window<"u"?window.location.pathname:"/",getRouteName:i,getLocaleFromPath:c,switchLocalePath:f,localizePath:t,removeLocaleFromPath:(a,u=[])=>{const h=a.split("/").filter(Boolean),d=h[0];return d&&u.includes(d)&&h.shift(),`/${h.join("/")}`},resolvePath:(a,u)=>{const h=typeof a=="string"?a:a.path||"/";return t(h,u,o,e)},getRoute:()=>{if(s){const a=s();return{fullPath:a.pathname+a.search,query:Object.fromEntries(a.searchParams)}}if(typeof window<"u"){const a=new URL(window.location.href);return{fullPath:a.pathname+a.search,query:Object.fromEntries(a.searchParams)}}return{fullPath:"/",query:{}}},push:a=>{typeof window<"u"&&(window.location.href=a.path)},replace:a=>{typeof window<"u"&&window.location.replace(a.path)}}}function H(n,e=[]){const s=n.replace(/^\//,"").replace(/\/$/,"");if(!s)return"index";const o=s.split("/").filter(Boolean),i=o[0];return i&&e.includes(i)&&o.shift(),o.length===0?"index":o.join("-")}function U(n,e="en",s=[]){const i=n.split("/").filter(Boolean)[0];return i&&s.includes(i)?i:e}function E(n,e,s=[],o){const i=n.split("/").filter(Boolean),c=i[0];return c&&s.includes(c)&&i.shift(),(e!==o||o===void 0)&&i.unshift(e),`/${i.join("/")}`}function J(n,e,s=[],o){const c=(n.replace(/^\//,"").replace(/\/$/,"")||"").split("/").filter(Boolean),f=c[0];return f&&s.includes(f)&&c.shift(),(e!==o||o===void 0)&&c.unshift(e),`/${c.join("/")}`}function V(n,e=[]){const s=n.split("/").filter(Boolean),o=s[0];return o&&e.includes(o)&&s.shift(),`/${s.join("/")}`}function v(n){const e=n.locals.i18n;if(!e)throw new Error("i18n instance not found. Make sure i18n middleware is configured.");return e}function R(n){return n.locals.locale||"en"}function T(n){return n.locals.defaultLocale||"en"}function k(n){return n.locals.locales||[]}function B(n){return n.locals.routingStrategy||null}function G(n){const e=v(n),s=R(n),o=T(n),i=k(n),c=i.map(t=>t.code),f=B(n);return{locale:s,defaultLocale:o,locales:i,t:(t,l,r,a)=>e.t(t,l,r,a),ts:(t,l,r,a)=>e.ts(t,l,r,a),tc:(t,l,r)=>e.tc(t,l,r),tn:(t,l)=>e.tn(t,l),td:(t,l)=>e.td(t,l),tdr:(t,l)=>e.tdr(t,l),has:(t,l)=>e.has(t,l),getRoute:()=>e.getRoute(),getRouteName:t=>{const l=t||n.url.pathname;if(f?.getRouteName)return f.getRouteName(l,c);const r=l.replace(/^\//,"").replace(/\/$/,"");if(!r)return"index";const a=r.split("/").filter(Boolean),u=a[0];return u&&c.includes(u)&&a.shift(),a.length===0?"index":a.join("-")},getLocaleFromPath:t=>{const l=t||n.url.pathname;if(f?.getLocaleFromPath)return f.getLocaleFromPath(l,o,c);const a=l.split("/").filter(Boolean)[0];return a&&c.includes(a)?a:o},switchLocalePath:t=>{if(f?.switchLocalePath)return f.switchLocalePath(n.url.pathname,t,c,o);const l=n.url.pathname.split("/").filter(Boolean),r=l[0];return r&&c.includes(r)&&l.shift(),t!==o&&l.unshift(t),`/${l.join("/")}`},localizePath:(t,l)=>{if(f?.localizePath)return f.localizePath(t,l||s,c,o);const a=(t.replace(/^\//,"").replace(/\/$/,"")||"").split("/").filter(Boolean),u=a[0];return u&&c.includes(u)&&a.shift(),l&&l!==o&&a.unshift(l),`/${a.join("/")}`},getI18n:()=>e,getBasePath:t=>{const a=(t||n.url).pathname.split("/").filter(Boolean),u=a[0];return u&&c.includes(u)&&a.shift(),a.length>0?`/${a.join("/")}`:"/"},addTranslations:(t,l,r=!0)=>{e.addTranslations(t,l,r)},addRouteTranslations:(t,l,r,a=!0)=>{e.addRouteTranslations(t,l,r,a)},mergeTranslations:(t,l,r)=>{e.mergeTranslations(t,l,r)},clearCache:()=>{e.clearCache()}}}function K(n,e={}){const{baseUrl:s="/",addDirAttribute:o=!0,addSeoAttributes:i=!0}=e,c=R(n),f=T(n),t=k(n),l=t.find(g=>g.code===c);if(!l)return{htmlAttrs:{},link:[],meta:[]};const r=l.iso||c,a=l.dir||"auto",u={htmlAttrs:{lang:r,...o?{dir:a}:{}},link:[],meta:[]};if(!i)return u;const h=`${s}${n.url.pathname}`;u.link.push({rel:"canonical",href:h});const d=B(n),m=t.map(g=>g.code);for(const g of t){if(g.code===c)continue;let p=n.url.pathname;if(d?.switchLocalePath)p=d.switchLocalePath(n.url.pathname,g.code,m,f);else{const L=n.url.pathname.split("/").filter(Boolean),y=L[0];y&&m.includes(y)&&L.shift(),g.code!==f&&L.unshift(g.code),p=`/${L.join("/")}`}const P=`${s}${p}`;u.link.push({rel:"alternate",href:P,hreflang:g.code}),g.iso&&g.iso!==g.code&&u.link.push({rel:"alternate",href:P,hreflang:g.iso})}u.meta.push({property:"og:locale",content:r}),u.meta.push({property:"og:url",content:h});for(const g of t)g.code!==c&&u.meta.push({property:"og:locale:alternate",content:g.iso||g.code});return u}function Q(n,e,s){const o=e.split(".");let i=n;for(let f=0;f<o.length-1;f++){const t=o[f];i[t]||(i[t]={}),i=i[t]}const c=o[o.length-1];c!==void 0&&(i[c]=s)}function X(n,e){const s=v(n),o=R(n),i=T(n),c=s.getRoute(),f={};if(e&&e.length>0){const t={};for(const l of e){const r=s.t(l,void 0,void 0,c);r!=null&&r!==l&&Q(t,l,r)}Object.keys(t).length>0&&(f[c]=t)}else{const t=s.getRouteTranslations(o,c);t&&(f[c]=t)}return{locale:o,fallbackLocale:i,currentRoute:c,translations:f}}Object.defineProperty(exports,"FormatService",{enumerable:!0,get:()=>j.FormatService});Object.defineProperty(exports,"defaultPlural",{enumerable:!0,get:()=>j.defaultPlural});Object.defineProperty(exports,"interpolate",{enumerable:!0,get:()=>j.interpolate});exports.AstroI18n=w;exports.createAstroRouterAdapter=z;exports.createI18n=N;exports.createI18nMiddleware=M;exports.detectLocale=W;exports.getDefaultLocale=T;exports.getI18n=v;exports.getI18nProps=X;exports.getLocale=R;exports.getLocaleFromPath=U;exports.getLocales=k;exports.getRouteName=H;exports.i18nIntegration=C;exports.loadTranslationsFromDir=F;exports.loadTranslationsIntoI18n=q;exports.localizePath=J;exports.removeLocaleFromPath=V;exports.switchLocalePath=E;exports.useI18n=G;exports.useLocaleHead=K;
package/dist/index.mjs CHANGED
@@ -1,38 +1,38 @@
1
1
  import { BaseI18n as I } from "@i18n-micro/core";
2
2
  import { FormatService as le, defaultPlural as re, interpolate as ie } from "@i18n-micro/core";
3
- import { existsSync as R, readdirSync as _, statSync as C, readFileSync as N } from "node:fs";
3
+ import { existsSync as j, readdirSync as _, statSync as C, readFileSync as N } from "node:fs";
4
4
  import { resolve as O, join as q } from "node:path";
5
5
  class S extends I {
6
6
  constructor(e) {
7
- const s = e._storage || {
7
+ const a = e._storage || {
8
8
  translations: /* @__PURE__ */ new Map()
9
9
  };
10
10
  if (super({
11
- storage: s,
11
+ storage: a,
12
12
  plural: e.plural,
13
13
  missingWarn: e.missingWarn,
14
14
  missingHandler: e.missingHandler
15
- }), this.initialMessages = {}, this.storage = s, this._locale = e.locale, this._fallbackLocale = e.fallbackLocale || e.locale, this._currentRoute = "general", e.messages) {
15
+ }), this.initialMessages = {}, this.storage = a, this._locale = e.locale, this._fallbackLocale = e.fallbackLocale || e.locale, this._currentRoute = "index", e.messages) {
16
16
  this.initialMessages = { ...e.messages };
17
17
  for (const [o, i] of Object.entries(e.messages))
18
18
  this.helper.loadTranslations(o, i);
19
19
  }
20
20
  }
21
21
  cloneStorage(e) {
22
- const s = /* @__PURE__ */ new Map();
22
+ const a = /* @__PURE__ */ new Map();
23
23
  for (const [o, i] of e.translations)
24
- s.set(o, { ...i });
25
- return { translations: s };
24
+ a.set(o, { ...i });
25
+ return { translations: a };
26
26
  }
27
27
  clone(e) {
28
- const s = this.cloneStorage(this.storage);
28
+ const a = this.cloneStorage(this.storage);
29
29
  return new S({
30
30
  locale: e || this._locale,
31
31
  fallbackLocale: this._fallbackLocale,
32
32
  plural: this.pluralFunc,
33
33
  missingWarn: this.missingWarn,
34
34
  missingHandler: this.missingHandler,
35
- _storage: s
35
+ _storage: a
36
36
  });
37
37
  }
38
38
  get locale() {
@@ -59,45 +59,42 @@ class S extends I {
59
59
  getRoute() {
60
60
  return this._currentRoute;
61
61
  }
62
- getRouteTranslations(e, s) {
63
- const o = `${e}:${s}`;
62
+ getRouteTranslations(e, a) {
63
+ const o = `${e}:${a}`;
64
64
  return this.storage.translations.get(o) ?? null;
65
65
  }
66
- addTranslations(e, s, o = !0) {
67
- super.loadTranslationsCore(e, s, o);
66
+ addTranslations(e, a, o = !0) {
67
+ super.loadTranslationsCore(e, a, o);
68
68
  }
69
- addRouteTranslations(e, s, o, i = !0) {
70
- super.loadRouteTranslationsCore(e, s, o, i);
69
+ addRouteTranslations(e, a, o, i = !0) {
70
+ super.loadRouteTranslationsCore(e, a, o, i);
71
71
  }
72
- mergeTranslations(e, s, o) {
73
- this.helper.mergeTranslation(e, s, o, !0);
74
- }
75
- mergeGlobalTranslations(e, s) {
76
- this.helper.mergeGlobalTranslation(e, s, !0);
72
+ mergeTranslations(e, a, o) {
73
+ this.helper.mergeTranslation(e, a, o, !0);
77
74
  }
78
75
  clearCache() {
79
76
  const e = { ...this.initialMessages };
80
77
  if (super.clearCache(), Object.keys(e).length > 0)
81
- for (const [s, o] of Object.entries(e))
82
- this.helper.loadTranslations(s, o);
78
+ for (const [a, o] of Object.entries(e))
79
+ this.helper.loadTranslations(a, o);
83
80
  }
84
81
  }
85
- let j = null;
82
+ let T = null;
86
83
  function v() {
87
- return j;
84
+ return T;
88
85
  }
89
- function U(n) {
90
- const { locale: e, fallbackLocale: s, translationDir: o, routingStrategy: i } = n;
91
- return j = i || null, {
86
+ function E(n) {
87
+ const { locale: e, fallbackLocale: a, translationDir: o, routingStrategy: i } = n;
88
+ return T = i || null, {
92
89
  name: "@i18n-micro/astro",
93
90
  hooks: {
94
- // 1. Настройка Vite (Виртуальный модуль) происходит здесь
91
+ // 1. Vite setup (virtual module) happens here
95
92
  "astro:config:setup": (c) => {
96
93
  const { updateConfig: f } = c, t = "virtual:i18n-micro/config", l = `\0${t}`, r = {
97
94
  defaultLocale: e,
98
- fallbackLocale: s || e,
95
+ fallbackLocale: a || e,
99
96
  locales: n.locales || [],
100
- localeCodes: (n.locales || []).map((a) => a.code),
97
+ localeCodes: (n.locales || []).map((s) => s.code),
101
98
  translationDir: o || null,
102
99
  autoDetect: n.autoDetect ?? !0,
103
100
  redirectToDefault: n.redirectToDefault ?? !1,
@@ -109,12 +106,12 @@ function U(n) {
109
106
  plugins: [
110
107
  {
111
108
  name: "vite-plugin-i18n-micro-config",
112
- resolveId(a) {
113
- if (a === t)
109
+ resolveId(s) {
110
+ if (s === t)
114
111
  return l;
115
112
  },
116
- load(a) {
117
- if (a === l)
113
+ load(s) {
114
+ if (s === l)
118
115
  return `export const config = ${JSON.stringify(r)}`;
119
116
  }
120
117
  }
@@ -122,7 +119,7 @@ function U(n) {
122
119
  }
123
120
  });
124
121
  },
125
- // 2. Инъекция типов происходит здесь (согласно документации Astro)
122
+ // 2. Type injection happens here (per Astro documentation)
126
123
  "astro:config:done": (c) => {
127
124
  const { injectTypes: f } = c;
128
125
  f({
@@ -149,17 +146,17 @@ function U(n) {
149
146
  }
150
147
  };
151
148
  }
152
- function E(n) {
149
+ function J(n) {
153
150
  return new S(n);
154
151
  }
155
152
  function M(n) {
156
- const { translationDir: e, rootDir: s = process.cwd(), disablePageLocales: o = !1 } = n, i = O(s, e);
157
- if (!R(i))
158
- return console.warn(`[i18n] Translation directory not found: ${i}`), { general: {}, routes: {} };
153
+ const { translationDir: e, rootDir: a = process.cwd(), disablePageLocales: o = !1 } = n, i = O(a, e);
154
+ if (!j(i))
155
+ return console.warn(`[i18n] Translation directory not found: ${i}`), { root: {}, routes: {} };
159
156
  const c = {}, f = {}, t = (l, r = "") => {
160
- if (!R(l)) return;
161
- const a = _(l);
162
- for (const u of a) {
157
+ if (!j(l)) return;
158
+ const s = _(l);
159
+ for (const u of s) {
163
160
  const h = q(l, u);
164
161
  if (C(h).isDirectory())
165
162
  u === "pages" && !o ? t(h, "") : r || o ? t(h, r) : t(h, u);
@@ -174,34 +171,36 @@ function M(n) {
174
171
  }
175
172
  }
176
173
  };
177
- return t(i), { general: c, routes: f };
174
+ return t(i), { root: c, routes: f };
178
175
  }
179
- function J(n, e) {
180
- const { general: s, routes: o } = M(e);
181
- for (const [i, c] of Object.entries(s))
176
+ function V(n, e) {
177
+ const { root: a, routes: o } = M(e);
178
+ for (const [i, c] of Object.entries(a))
182
179
  n.addTranslations(i, c, !1);
183
180
  for (const [i, c] of Object.entries(o))
184
- for (const [f, t] of Object.entries(c))
185
- n.addRouteTranslations(f, i, t, !1);
181
+ for (const [f, t] of Object.entries(c)) {
182
+ const l = a[f] || {};
183
+ n.addRouteTranslations(f, i, { ...l, ...t }, !1);
184
+ }
186
185
  }
187
- function V(n) {
186
+ function G(n) {
188
187
  const {
189
188
  i18n: e,
190
- // Это глобальный синглтон с кэшем
191
- defaultLocale: s,
189
+ // This is the global singleton with cache
190
+ defaultLocale: a,
192
191
  locales: o,
193
192
  localeObjects: i,
194
193
  autoDetect: c = !0,
195
194
  redirectToDefault: f = !1,
196
195
  routingStrategy: t
197
196
  } = n, l = t || v();
198
- return async (r, a) => {
197
+ return async (r, s) => {
199
198
  if (r.locals.locale && r.locals.i18n)
200
- return a();
199
+ return s();
201
200
  const u = r.url, h = u.pathname;
202
201
  if (!l) {
203
- const y = e.clone(s), B = h === "/" || h === "" ? "index" : h.split("/").filter(Boolean).join("-");
204
- return y.setRoute(B), r.locals.i18n = y, r.locals.locale = s, r.locals.defaultLocale = s, r.locals.locales = i || o.map((F) => ({ code: F })), r.locals.currentUrl = u, a();
202
+ const b = e.clone(a), B = h === "/" || h === "" ? "index" : h.split("/").filter(Boolean).join("-");
203
+ return b.setRoute(B), r.locals.i18n = b, r.locals.locale = a, r.locals.defaultLocale = a, r.locals.locales = i || o.map((F) => ({ code: F })), r.locals.currentUrl = u, s();
205
204
  }
206
205
  const d = {
207
206
  ...l,
@@ -212,14 +211,14 @@ function V(n) {
212
211
  })
213
212
  }, g = h.split("/").filter(Boolean)[0], p = g !== void 0 && o.includes(g);
214
213
  let P;
215
- p && g ? P = g : d.getLocaleFromPath ? P = d.getLocaleFromPath(h, s, o) : P = s;
216
- const L = e.clone(P), b = d.getRouteName ? d.getRouteName(h, o) : "general";
217
- return L.setRoute(b), r.locals.i18n = L, r.locals.locale = P, r.locals.defaultLocale = s, r.locals.locales = i || o.map((y) => ({ code: y })), r.locals.currentUrl = u, r.locals.routingStrategy = d, a();
214
+ p && g ? P = g : d.getLocaleFromPath ? P = d.getLocaleFromPath(h, a, o) : P = a;
215
+ const L = e.clone(P), y = d.getRouteName ? d.getRouteName(h, o) : "index";
216
+ return L.setRoute(y), r.locals.i18n = L, r.locals.locale = P, r.locals.defaultLocale = a, r.locals.locales = i || o.map((b) => ({ code: b })), r.locals.currentUrl = u, r.locals.routingStrategy = d, s();
218
217
  };
219
218
  }
220
219
  function W(n) {
221
- const e = [], s = n.split(",");
222
- for (const o of s) {
220
+ const e = [], a = n.split(",");
221
+ for (const o of a) {
223
222
  const [i, c = "1.0"] = o.trim().split(";q=");
224
223
  if (Number.parseFloat(c) > 0 && i) {
225
224
  const t = i.split("-")[0]?.toLowerCase();
@@ -228,7 +227,7 @@ function W(n) {
228
227
  }
229
228
  return e;
230
229
  }
231
- function K(n, e, s, o, i, c = "i18n-locale") {
230
+ function K(n, e, a, o, i, c = "i18n-locale") {
232
231
  const f = v();
233
232
  let t = o;
234
233
  if (f?.getLocaleFromPath)
@@ -243,12 +242,12 @@ function K(n, e, s, o, i, c = "i18n-locale") {
243
242
  }
244
243
  if (t === o)
245
244
  try {
246
- const l = s.get("accept-language");
245
+ const l = a.get("accept-language");
247
246
  if (l) {
248
247
  const r = W(l);
249
- for (const a of r)
250
- if (i.includes(a)) {
251
- t = a;
248
+ for (const s of r)
249
+ if (i.includes(s)) {
250
+ t = s;
252
251
  break;
253
252
  }
254
253
  }
@@ -256,50 +255,50 @@ function K(n, e, s, o, i, c = "i18n-locale") {
256
255
  }
257
256
  return t;
258
257
  }
259
- function Q(n, e, s) {
260
- const o = n.map((a) => a.code), i = (a, u = []) => {
261
- const h = a.replace(/^\//, "").replace(/\/$/, "");
258
+ function Q(n, e, a) {
259
+ const o = n.map((s) => s.code), i = (s, u = []) => {
260
+ const h = s.replace(/^\//, "").replace(/\/$/, "");
262
261
  if (!h)
263
262
  return "index";
264
263
  const d = h.split("/").filter(Boolean), m = d[0];
265
264
  return m && u.includes(m) && d.shift(), d.length === 0 ? "index" : d.join("-");
266
- }, c = (a, u = "en", h = []) => {
267
- const m = a.split("/").filter(Boolean)[0];
265
+ }, c = (s, u = "en", h = []) => {
266
+ const m = s.split("/").filter(Boolean)[0];
268
267
  return m && h.includes(m) ? m : u;
269
- }, f = (a, u, h = [], d) => {
270
- const m = a.split("/").filter(Boolean), g = m[0];
268
+ }, f = (s, u, h = [], d) => {
269
+ const m = s.split("/").filter(Boolean), g = m[0];
271
270
  return g && h.includes(g) && m.shift(), (u !== d || d === void 0) && m.unshift(u), `/${m.join("/")}`;
272
- }, t = (a, u, h = [], d) => {
273
- const g = (a.replace(/^\//, "").replace(/\/$/, "") || "").split("/").filter(Boolean), p = g[0];
271
+ }, t = (s, u, h = [], d) => {
272
+ const g = (s.replace(/^\//, "").replace(/\/$/, "") || "").split("/").filter(Boolean), p = g[0];
274
273
  return p && h.includes(p) && g.shift(), (u !== d || d === void 0) && g.unshift(u), `/${g.join("/")}`;
275
274
  };
276
275
  return {
277
- getCurrentPath: () => s ? s().pathname : typeof window < "u" ? window.location.pathname : "/",
276
+ getCurrentPath: () => a ? a().pathname : typeof window < "u" ? window.location.pathname : "/",
278
277
  getRouteName: i,
279
278
  getLocaleFromPath: c,
280
279
  switchLocalePath: f,
281
280
  localizePath: t,
282
- removeLocaleFromPath: (a, u = []) => {
283
- const h = a.split("/").filter(Boolean), d = h[0];
281
+ removeLocaleFromPath: (s, u = []) => {
282
+ const h = s.split("/").filter(Boolean), d = h[0];
284
283
  return d && u.includes(d) && h.shift(), `/${h.join("/")}`;
285
284
  },
286
- resolvePath: (a, u) => {
287
- const h = typeof a == "string" ? a : a.path || "/";
285
+ resolvePath: (s, u) => {
286
+ const h = typeof s == "string" ? s : s.path || "/";
288
287
  return t(h, u, o, e);
289
288
  },
290
289
  getRoute: () => {
291
- if (s) {
292
- const a = s();
290
+ if (a) {
291
+ const s = a();
293
292
  return {
294
- fullPath: a.pathname + a.search,
295
- query: Object.fromEntries(a.searchParams)
293
+ fullPath: s.pathname + s.search,
294
+ query: Object.fromEntries(s.searchParams)
296
295
  };
297
296
  }
298
297
  if (typeof window < "u") {
299
- const a = new URL(window.location.href);
298
+ const s = new URL(window.location.href);
300
299
  return {
301
- fullPath: a.pathname + a.search,
302
- query: Object.fromEntries(a.searchParams)
300
+ fullPath: s.pathname + s.search,
301
+ query: Object.fromEntries(s.searchParams)
303
302
  };
304
303
  }
305
304
  return {
@@ -308,36 +307,36 @@ function Q(n, e, s) {
308
307
  };
309
308
  },
310
309
  // Optional: client-side navigation for islands
311
- push: (a) => {
312
- typeof window < "u" && (window.location.href = a.path);
310
+ push: (s) => {
311
+ typeof window < "u" && (window.location.href = s.path);
313
312
  },
314
- replace: (a) => {
315
- typeof window < "u" && window.location.replace(a.path);
313
+ replace: (s) => {
314
+ typeof window < "u" && window.location.replace(s.path);
316
315
  }
317
316
  };
318
317
  }
319
318
  function X(n, e = []) {
320
- const s = n.replace(/^\//, "").replace(/\/$/, "");
321
- if (!s)
319
+ const a = n.replace(/^\//, "").replace(/\/$/, "");
320
+ if (!a)
322
321
  return "index";
323
- const o = s.split("/").filter(Boolean), i = o[0];
322
+ const o = a.split("/").filter(Boolean), i = o[0];
324
323
  return i && e.includes(i) && o.shift(), o.length === 0 ? "index" : o.join("-");
325
324
  }
326
- function Y(n, e = "en", s = []) {
325
+ function Y(n, e = "en", a = []) {
327
326
  const i = n.split("/").filter(Boolean)[0];
328
- return i && s.includes(i) ? i : e;
327
+ return i && a.includes(i) ? i : e;
329
328
  }
330
- function Z(n, e, s = [], o) {
329
+ function Z(n, e, a = [], o) {
331
330
  const i = n.split("/").filter(Boolean), c = i[0];
332
- return c && s.includes(c) && i.shift(), (e !== o || o === void 0) && i.unshift(e), `/${i.join("/")}`;
331
+ return c && a.includes(c) && i.shift(), (e !== o || o === void 0) && i.unshift(e), `/${i.join("/")}`;
333
332
  }
334
- function x(n, e, s = [], o) {
333
+ function x(n, e, a = [], o) {
335
334
  const c = (n.replace(/^\//, "").replace(/\/$/, "") || "").split("/").filter(Boolean), f = c[0];
336
- return f && s.includes(f) && c.shift(), (e !== o || o === void 0) && c.unshift(e), `/${c.join("/")}`;
335
+ return f && a.includes(f) && c.shift(), (e !== o || o === void 0) && c.unshift(e), `/${c.join("/")}`;
337
336
  }
338
337
  function ee(n, e = []) {
339
- const s = n.split("/").filter(Boolean), o = s[0];
340
- return o && e.includes(o) && s.shift(), `/${s.join("/")}`;
338
+ const a = n.split("/").filter(Boolean), o = a[0];
339
+ return o && e.includes(o) && a.shift(), `/${a.join("/")}`;
341
340
  }
342
341
  function k(n) {
343
342
  const e = n.locals.i18n;
@@ -345,10 +344,10 @@ function k(n) {
345
344
  throw new Error("i18n instance not found. Make sure i18n middleware is configured.");
346
345
  return e;
347
346
  }
348
- function T(n) {
347
+ function w(n) {
349
348
  return n.locals.locale || "en";
350
349
  }
351
- function w(n) {
350
+ function R(n) {
352
351
  return n.locals.defaultLocale || "en";
353
352
  }
354
353
  function $(n) {
@@ -358,15 +357,15 @@ function D(n) {
358
357
  return n.locals.routingStrategy || null;
359
358
  }
360
359
  function te(n) {
361
- const e = k(n), s = T(n), o = w(n), i = $(n), c = i.map((t) => t.code), f = D(n);
360
+ const e = k(n), a = w(n), o = R(n), i = $(n), c = i.map((t) => t.code), f = D(n);
362
361
  return {
363
362
  // Current locale
364
- locale: s,
363
+ locale: a,
365
364
  defaultLocale: o,
366
365
  locales: i,
367
366
  // Translation methods
368
- t: (t, l, r, a) => e.t(t, l, r, a),
369
- ts: (t, l, r, a) => e.ts(t, l, r, a),
367
+ t: (t, l, r, s) => e.t(t, l, r, s),
368
+ ts: (t, l, r, s) => e.ts(t, l, r, s),
370
369
  tc: (t, l, r) => e.tc(t, l, r),
371
370
  tn: (t, l) => e.tn(t, l),
372
371
  td: (t, l) => e.td(t, l),
@@ -380,15 +379,15 @@ function te(n) {
380
379
  return f.getRouteName(l, c);
381
380
  const r = l.replace(/^\//, "").replace(/\/$/, "");
382
381
  if (!r) return "index";
383
- const a = r.split("/").filter(Boolean), u = a[0];
384
- return u && c.includes(u) && a.shift(), a.length === 0 ? "index" : a.join("-");
382
+ const s = r.split("/").filter(Boolean), u = s[0];
383
+ return u && c.includes(u) && s.shift(), s.length === 0 ? "index" : s.join("-");
385
384
  },
386
385
  getLocaleFromPath: (t) => {
387
386
  const l = t || n.url.pathname;
388
387
  if (f?.getLocaleFromPath)
389
388
  return f.getLocaleFromPath(l, o, c);
390
- const a = l.split("/").filter(Boolean)[0];
391
- return a && c.includes(a) ? a : o;
389
+ const s = l.split("/").filter(Boolean)[0];
390
+ return s && c.includes(s) ? s : o;
392
391
  },
393
392
  // Path utilities
394
393
  switchLocalePath: (t) => {
@@ -399,50 +398,47 @@ function te(n) {
399
398
  },
400
399
  localizePath: (t, l) => {
401
400
  if (f?.localizePath)
402
- return f.localizePath(t, l || s, c, o);
403
- const a = (t.replace(/^\//, "").replace(/\/$/, "") || "").split("/").filter(Boolean), u = a[0];
404
- return u && c.includes(u) && a.shift(), l && l !== o && a.unshift(l), `/${a.join("/")}`;
401
+ return f.localizePath(t, l || a, c, o);
402
+ const s = (t.replace(/^\//, "").replace(/\/$/, "") || "").split("/").filter(Boolean), u = s[0];
403
+ return u && c.includes(u) && s.shift(), l && l !== o && s.unshift(l), `/${s.join("/")}`;
405
404
  },
406
405
  // Get i18n instance
407
406
  getI18n: () => e,
408
407
  // Get base path without locale (for rewrite)
409
408
  getBasePath: (t) => {
410
- const a = (t || n.url).pathname.split("/").filter(Boolean), u = a[0];
411
- return u && c.includes(u) && a.shift(), a.length > 0 ? `/${a.join("/")}` : "/";
409
+ const s = (t || n.url).pathname.split("/").filter(Boolean), u = s[0];
410
+ return u && c.includes(u) && s.shift(), s.length > 0 ? `/${s.join("/")}` : "/";
412
411
  },
413
412
  // Translation management
414
413
  addTranslations: (t, l, r = !0) => {
415
414
  e.addTranslations(t, l, r);
416
415
  },
417
- addRouteTranslations: (t, l, r, a = !0) => {
418
- e.addRouteTranslations(t, l, r, a);
416
+ addRouteTranslations: (t, l, r, s = !0) => {
417
+ e.addRouteTranslations(t, l, r, s);
419
418
  },
420
419
  mergeTranslations: (t, l, r) => {
421
420
  e.mergeTranslations(t, l, r);
422
421
  },
423
- mergeGlobalTranslations: (t, l) => {
424
- e.mergeGlobalTranslations(t, l);
425
- },
426
422
  clearCache: () => {
427
423
  e.clearCache();
428
424
  }
429
425
  };
430
426
  }
431
427
  function ne(n, e = {}) {
432
- const { baseUrl: s = "/", addDirAttribute: o = !0, addSeoAttributes: i = !0 } = e, c = T(n), f = w(n), t = $(n), l = t.find((g) => g.code === c);
428
+ const { baseUrl: a = "/", addDirAttribute: o = !0, addSeoAttributes: i = !0 } = e, c = w(n), f = R(n), t = $(n), l = t.find((g) => g.code === c);
433
429
  if (!l)
434
430
  return { htmlAttrs: {}, link: [], meta: [] };
435
- const r = l.iso || c, a = l.dir || "auto", u = {
431
+ const r = l.iso || c, s = l.dir || "auto", u = {
436
432
  htmlAttrs: {
437
433
  lang: r,
438
- ...o ? { dir: a } : {}
434
+ ...o ? { dir: s } : {}
439
435
  },
440
436
  link: [],
441
437
  meta: []
442
438
  };
443
439
  if (!i)
444
440
  return u;
445
- const h = `${s}${n.url.pathname}`;
441
+ const h = `${a}${n.url.pathname}`;
446
442
  u.link.push({
447
443
  rel: "canonical",
448
444
  href: h
@@ -454,10 +450,10 @@ function ne(n, e = {}) {
454
450
  if (d?.switchLocalePath)
455
451
  p = d.switchLocalePath(n.url.pathname, g.code, m, f);
456
452
  else {
457
- const L = n.url.pathname.split("/").filter(Boolean), b = L[0];
458
- b && m.includes(b) && L.shift(), g.code !== f && L.unshift(g.code), p = `/${L.join("/")}`;
453
+ const L = n.url.pathname.split("/").filter(Boolean), y = L[0];
454
+ y && m.includes(y) && L.shift(), g.code !== f && L.unshift(g.code), p = `/${L.join("/")}`;
459
455
  }
460
- const P = `${s}${p}`;
456
+ const P = `${a}${p}`;
461
457
  u.link.push({
462
458
  rel: "alternate",
463
459
  href: P,
@@ -482,7 +478,7 @@ function ne(n, e = {}) {
482
478
  });
483
479
  return u;
484
480
  }
485
- function A(n, e, s) {
481
+ function A(n, e, a) {
486
482
  const o = e.split(".");
487
483
  let i = n;
488
484
  for (let f = 0; f < o.length - 1; f++) {
@@ -490,19 +486,19 @@ function A(n, e, s) {
490
486
  i[t] || (i[t] = {}), i = i[t];
491
487
  }
492
488
  const c = o[o.length - 1];
493
- c !== void 0 && (i[c] = s);
489
+ c !== void 0 && (i[c] = a);
494
490
  }
495
491
  function oe(n, e) {
496
- const s = k(n), o = T(n), i = w(n), c = s.getRoute(), f = {};
492
+ const a = k(n), o = w(n), i = R(n), c = a.getRoute(), f = {};
497
493
  if (e && e.length > 0) {
498
494
  const t = {};
499
495
  for (const l of e) {
500
- const r = s.t(l, void 0, void 0, c);
496
+ const r = a.t(l, void 0, void 0, c);
501
497
  r != null && r !== l && A(t, l, r);
502
498
  }
503
499
  Object.keys(t).length > 0 && (f[c] = t);
504
500
  } else {
505
- const t = s.getRouteTranslations(o, c);
501
+ const t = a.getRouteTranslations(o, c);
506
502
  t && (f[c] = t);
507
503
  }
508
504
  return {
@@ -516,21 +512,21 @@ export {
516
512
  S as AstroI18n,
517
513
  le as FormatService,
518
514
  Q as createAstroRouterAdapter,
519
- E as createI18n,
520
- V as createI18nMiddleware,
515
+ J as createI18n,
516
+ G as createI18nMiddleware,
521
517
  re as defaultPlural,
522
518
  K as detectLocale,
523
- w as getDefaultLocale,
519
+ R as getDefaultLocale,
524
520
  k as getI18n,
525
521
  oe as getI18nProps,
526
- T as getLocale,
522
+ w as getLocale,
527
523
  Y as getLocaleFromPath,
528
524
  $ as getLocales,
529
525
  X as getRouteName,
530
- U as i18nIntegration,
526
+ E as i18nIntegration,
531
527
  ie as interpolate,
532
528
  M as loadTranslationsFromDir,
533
- J as loadTranslationsIntoI18n,
529
+ V as loadTranslationsIntoI18n,
534
530
  x as localizePath,
535
531
  ee as removeLocaleFromPath,
536
532
  Z as switchLocalePath,
@@ -21,7 +21,7 @@ export interface LoadTranslationsOptions {
21
21
  disablePageLocales?: boolean;
22
22
  }
23
23
  export interface LoadedTranslations {
24
- general: Record<string, Translations>;
24
+ root: Record<string, Translations>;
25
25
  routes: Record<string, Record<string, Translations>>;
26
26
  }
27
27
  /**
package/dist/utils.d.ts CHANGED
@@ -42,7 +42,6 @@ export declare function useI18n(astro: AstroGlobal): {
42
42
  addTranslations: (locale: string, translations: Record<string, unknown>, merge?: boolean) => void;
43
43
  addRouteTranslations: (locale: string, routeName: string, translations: Record<string, unknown>, merge?: boolean) => void;
44
44
  mergeTranslations: (locale: string, routeName: string, translations: Record<string, unknown>) => void;
45
- mergeGlobalTranslations: (locale: string, translations: Record<string, unknown>) => void;
46
45
  clearCache: () => void;
47
46
  };
48
47
  /**
@@ -70,7 +69,7 @@ export interface LocaleHeadResult {
70
69
  }
71
70
  export declare function useLocaleHead(astro: AstroGlobal, options?: LocaleHeadOptions): LocaleHeadResult;
72
71
  /**
73
- * Props для передачи в клиентские острова (Vue, React, Svelte, Preact)
72
+ * Props to pass to client islands (Vue, React, Svelte, Preact)
74
73
  */
75
74
  export interface I18nClientProps {
76
75
  locale: string;
@@ -79,9 +78,9 @@ export interface I18nClientProps {
79
78
  currentRoute: string;
80
79
  }
81
80
  /**
82
- * Подготавливает пропсы для передачи в клиентский остров.
83
- * Принимает список ключей, которые нужно передать в остров.
84
- * Использует методы i18n для правильной работы с routesLocaleLinks.
85
- * currentRoute уже нормализован через middleware (getRouteName), поэтому используем его напрямую.
81
+ * Prepares props to pass to a client island.
82
+ * Accepts a list of keys to pass to the island.
83
+ * Uses i18n methods for proper routesLocaleLinks support.
84
+ * currentRoute is already normalized via middleware (getRouteName), so we use it directly.
86
85
  */
87
86
  export declare function getI18nProps(astro: AstroGlobal, keys?: string[]): I18nClientProps;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@i18n-micro/astro",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -51,9 +51,9 @@
51
51
  "access": "public"
52
52
  },
53
53
  "dependencies": {
54
- "@i18n-micro/core": "1.1.2",
55
- "@i18n-micro/types": "1.1.1",
56
- "@i18n-micro/node": "1.1.1"
54
+ "@i18n-micro/core": "1.2.0",
55
+ "@i18n-micro/types": "1.1.5",
56
+ "@i18n-micro/node": "1.2.0"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "astro": "^5.16.5",
@@ -2,7 +2,7 @@ import { interpolate } from '@i18n-micro/core'
2
2
  import type { Params, Translations } from '@i18n-micro/types'
3
3
 
4
4
  /**
5
- * Состояние i18n для клиентских островов
5
+ * I18n state for client islands
6
6
  */
7
7
  export interface I18nState {
8
8
  locale: string
@@ -11,7 +11,7 @@ export interface I18nState {
11
11
  currentRoute: string
12
12
  }
13
13
 
14
- // Вспомогательная функция для поиска перевода в объекте Translations
14
+ // Helper function to find a translation in a Translations object
15
15
  // Returns the value as-is, including objects (for nested translations)
16
16
  function findTranslation<T = unknown>(translations: Translations | null, key: string): T | null {
17
17
  if (translations === null || typeof key !== 'string') {
@@ -20,11 +20,11 @@ function findTranslation<T = unknown>(translations: Translations | null, key: st
20
20
 
21
21
  let value: string | number | boolean | Translations | unknown | null = translations
22
22
 
23
- // Прямой доступ к ключу
23
+ // Direct key access
24
24
  if (translations[key]) {
25
25
  value = translations[key]
26
26
  } else {
27
- // Поиск по вложенным ключам (например, "nested.message")
27
+ // Search by nested keys (e.g. "nested.message")
28
28
  const parts = key.toString().split('.')
29
29
  for (const part of parts) {
30
30
  if (value && typeof value === 'object' && value !== null && part in value) {
@@ -41,7 +41,7 @@ function findTranslation<T = unknown>(translations: Translations | null, key: st
41
41
  }
42
42
 
43
43
  /**
44
- * Чистая функция для получения перевода из состояния
44
+ * Pure function to get a translation from state
45
45
  *
46
46
  * Note: This is a simplified version optimized for client-side islands.
47
47
  * It supports basic translation lookup, interpolation, and fallback to general translations.
@@ -62,40 +62,40 @@ export function translate(
62
62
  const route = routeName || state.currentRoute
63
63
  let value: string | number | boolean | Translations | null = null
64
64
 
65
- // 1. Ищем в route-specific переводах
65
+ // 1. Look in route-specific translations
66
66
  if (state.translations[route]) {
67
67
  value = findTranslation<string | number | boolean | Translations>(state.translations[route], key)
68
68
  }
69
69
 
70
- // 2. Fallback на general переводы
70
+ // 2. Fallback to general translations
71
71
  if (!value && state.translations.general) {
72
72
  value = findTranslation<string | number | boolean | Translations>(state.translations.general, key)
73
73
  }
74
74
 
75
- // 3. Если не найдено, используем defaultValue или key
75
+ // 3. If not found, use defaultValue or key
76
76
  if (!value) {
77
77
  value = defaultValue === undefined ? key : defaultValue || key
78
78
  }
79
79
 
80
- // 4. Интерполяция параметров (только для строк)
80
+ // 4. Parameter interpolation (strings only)
81
81
  if (typeof value === 'string' && params) {
82
82
  return interpolate(value, params)
83
83
  }
84
84
 
85
- // 5. Возвращаем value как есть (может быть string, number, boolean, object, или null)
86
- // Это соответствует типу CleanTranslation
85
+ // 5. Return value as-is (can be string, number, boolean, object, or null)
86
+ // This matches the CleanTranslation type
87
87
  return value
88
88
  }
89
89
 
90
90
  /**
91
- * Проверяет наличие перевода в состоянии
91
+ * Checks if a translation exists in the state
92
92
  */
93
93
  export function hasTranslation(state: I18nState, key: string, routeName?: string): boolean {
94
94
  const route = routeName || state.currentRoute
95
95
  const routeTranslations = state.translations[route]
96
96
  const generalTranslations = state.translations.general
97
97
 
98
- // Проверяем в route-specific переводах
98
+ // Check in route-specific translations
99
99
  if (routeTranslations) {
100
100
  const value = findTranslation(routeTranslations, key)
101
101
  if (value !== null && typeof value !== 'object') {
@@ -103,7 +103,7 @@ export function hasTranslation(state: I18nState, key: string, routeName?: string
103
103
  }
104
104
  }
105
105
 
106
- // Проверяем в general переводах
106
+ // Check in general translations
107
107
  if (generalTranslations) {
108
108
  const value = findTranslation(generalTranslations, key)
109
109
  if (value !== null && typeof value !== 'object') {
@@ -1,4 +1,4 @@
1
- // Core utilities (чистые функции)
1
+ // Core utilities (pure functions)
2
2
 
3
3
  export type { I18nState } from './core'
4
4
  export { hasTranslation, translate } from './core'
@@ -7,7 +7,7 @@ import { hasTranslation, type I18nState, translate } from './core'
7
7
  const formatter = new FormatService()
8
8
 
9
9
  /**
10
- * Создает Svelte store для i18n состояния
10
+ * Creates a Svelte store for i18n state
11
11
  */
12
12
  export function createI18nStore(props: I18nClientProps): Writable<I18nState> {
13
13
  return writable<I18nState>({
@@ -19,8 +19,8 @@ export function createI18nStore(props: I18nClientProps): Writable<I18nState> {
19
19
  }
20
20
 
21
21
  /**
22
- * Хук для использования i18n в Svelte компонентах
23
- * Используйте в <script> блоке компонента
22
+ * Hook for using i18n in Svelte components
23
+ * Use in the component's <script> block
24
24
  */
25
25
  export function useAstroI18n(store: Writable<I18nState>) {
26
26
  const getState = () => get(store)
@@ -71,7 +71,7 @@ export function useAstroI18n(store: Writable<I18nState>) {
71
71
  }
72
72
 
73
73
  return {
74
- // Store для реактивности в шаблонах (используйте $i18nStore в шаблонах)
74
+ // Store for reactivity in templates (use $i18nStore in templates)
75
75
  store,
76
76
 
77
77
  // Translation methods
@@ -83,7 +83,7 @@ export function useAstroI18n(store: Writable<I18nState>) {
83
83
  tdr,
84
84
  has,
85
85
 
86
- // Геттеры для текущего состояния (для использования в скриптах)
86
+ // Getters for current state (for use in scripts)
87
87
  get locale() {
88
88
  return getState().locale
89
89
  },
package/src/client/vue.ts CHANGED
@@ -9,8 +9,8 @@ const formatter = new FormatService()
9
9
  const I18nSymbol = Symbol('i18n-astro') as InjectionKey<Ref<I18nState>>
10
10
 
11
11
  /**
12
- * Инициализирует i18n провайдер для Vue острова
13
- * Вызывайте в корневом компоненте острова
12
+ * Initializes the i18n provider for a Vue island
13
+ * Call in the island's root component
14
14
  */
15
15
  export function provideI18n(props: I18nClientProps): Ref<I18nState> {
16
16
  const state = ref<I18nState>({
@@ -24,7 +24,7 @@ export function provideI18n(props: I18nClientProps): Ref<I18nState> {
24
24
  }
25
25
 
26
26
  /**
27
- * Хук для использования i18n в Vue компонентах
27
+ * Hook for using i18n in Vue components
28
28
  */
29
29
  export function useAstroI18n() {
30
30
  const state = inject(I18nSymbol)
package/src/composer.ts CHANGED
@@ -35,7 +35,7 @@ export class AstroI18n extends BaseI18n {
35
35
  this.storage = storage
36
36
  this._locale = options.locale
37
37
  this._fallbackLocale = options.fallbackLocale || options.locale
38
- this._currentRoute = 'general'
38
+ this._currentRoute = 'index'
39
39
 
40
40
  if (options.messages) {
41
41
  this.initialMessages = { ...options.messages }
@@ -115,10 +115,6 @@ export class AstroI18n extends BaseI18n {
115
115
  this.helper.mergeTranslation(locale, routeName, translations, true)
116
116
  }
117
117
 
118
- public mergeGlobalTranslations(locale: string, translations: Translations): void {
119
- this.helper.mergeGlobalTranslation(locale, translations, true)
120
- }
121
-
122
118
  public override clearCache(): void {
123
119
  const initialMessages = { ...this.initialMessages }
124
120
 
@@ -39,7 +39,7 @@ export function i18nIntegration(options: I18nIntegrationOptions): AstroIntegrati
39
39
  return {
40
40
  name: '@i18n-micro/astro',
41
41
  hooks: {
42
- // 1. Настройка Vite (Виртуальный модуль) происходит здесь
42
+ // 1. Vite setup (virtual module) happens here
43
43
  'astro:config:setup': (params) => {
44
44
  const { updateConfig } = params as HookParameters<'astro:config:setup'>
45
45
 
@@ -79,7 +79,7 @@ export function i18nIntegration(options: I18nIntegrationOptions): AstroIntegrati
79
79
  })
80
80
  },
81
81
 
82
- // 2. Инъекция типов происходит здесь (согласно документации Astro)
82
+ // 2. Type injection happens here (per Astro documentation)
83
83
  'astro:config:done': (params) => {
84
84
  const { injectTypes } = params as HookParameters<'astro:config:done'>
85
85
 
@@ -26,7 +26,7 @@ export interface LoadTranslationsOptions {
26
26
  }
27
27
 
28
28
  export interface LoadedTranslations {
29
- general: Record<string, Translations>
29
+ root: Record<string, Translations>
30
30
  routes: Record<string, Record<string, Translations>>
31
31
  }
32
32
 
@@ -43,10 +43,10 @@ export function loadTranslationsFromDir(options: LoadTranslationsOptions): Loade
43
43
 
44
44
  if (!existsSync(fullTranslationDir)) {
45
45
  console.warn(`[i18n] Translation directory not found: ${fullTranslationDir}`)
46
- return { general: {}, routes: {} }
46
+ return { root: {}, routes: {} }
47
47
  }
48
48
 
49
- const general: Record<string, Translations> = {}
49
+ const root: Record<string, Translations> = {}
50
50
  const routes: Record<string, Record<string, Translations>> = {}
51
51
 
52
52
  /**
@@ -84,8 +84,8 @@ export function loadTranslationsFromDir(options: LoadTranslationsOptions): Loade
84
84
  }
85
85
  routes[routePrefix][locale] = translations
86
86
  } else {
87
- // General translation
88
- general[locale] = translations
87
+ // Root-level translation
88
+ root[locale] = translations
89
89
  }
90
90
  } catch (error) {
91
91
  console.error(`[i18n] Failed to load translation file: ${fullPath}`, error)
@@ -96,7 +96,7 @@ export function loadTranslationsFromDir(options: LoadTranslationsOptions): Loade
96
96
 
97
97
  loadFiles(fullTranslationDir)
98
98
 
99
- return { general, routes }
99
+ return { root, routes }
100
100
  }
101
101
 
102
102
  /**
@@ -112,17 +112,18 @@ export function loadTranslationsIntoI18n(
112
112
  },
113
113
  options: LoadTranslationsOptions,
114
114
  ): void {
115
- const { general, routes } = loadTranslationsFromDir(options)
115
+ const { root, routes } = loadTranslationsFromDir(options)
116
116
 
117
- // Load general translations
118
- for (const [locale, translations] of Object.entries(general)) {
117
+ // Load root-level translations as index
118
+ for (const [locale, translations] of Object.entries(root)) {
119
119
  i18n.addTranslations(locale, translations, false)
120
120
  }
121
121
 
122
- // Load route-specific translations
122
+ // Load route-specific translations (with root baked in)
123
123
  for (const [routeName, routeTranslations] of Object.entries(routes)) {
124
124
  for (const [locale, translations] of Object.entries(routeTranslations)) {
125
- i18n.addRouteTranslations(locale, routeName, translations, false)
125
+ const base = root[locale] || {}
126
+ i18n.addRouteTranslations(locale, routeName, { ...base, ...translations }, false)
126
127
  }
127
128
  }
128
129
  }
package/src/middleware.ts CHANGED
@@ -21,7 +21,7 @@ export interface I18nMiddlewareOptions {
21
21
  */
22
22
  export function createI18nMiddleware(options: I18nMiddlewareOptions): MiddlewareHandler {
23
23
  const {
24
- i18n: globalI18n, // Это глобальный синглтон с кэшем
24
+ i18n: globalI18n, // This is the global singleton with cache
25
25
  defaultLocale,
26
26
  locales,
27
27
  localeObjects,
@@ -93,7 +93,7 @@ export function createI18nMiddleware(options: I18nMiddlewareOptions): Middleware
93
93
  const requestI18n = globalI18n.clone(detectedLocale)
94
94
 
95
95
  // 4. Set route name using routing strategy
96
- const routeName = contextStrategy.getRouteName ? contextStrategy.getRouteName(pathname, locales) : 'general'
96
+ const routeName = contextStrategy.getRouteName ? contextStrategy.getRouteName(pathname, locales) : 'index'
97
97
  requestI18n.setRoute(routeName)
98
98
 
99
99
  // 5. Store in locals (Type-safe now thanks to astro-shim.d.ts)
package/src/routing.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * Extracts route name from path (e.g., /en/about -> about)
4
4
  */
5
5
  export function getRouteName(path: string, locales: string[] = []): string {
6
- // Нормализуем путь
6
+ // Normalize the path
7
7
  const cleanPath = path.replace(/^\//, '').replace(/\/$/, '')
8
8
 
9
9
  if (!cleanPath) {
package/src/utils.ts CHANGED
@@ -179,9 +179,6 @@ export function useI18n(astro: AstroGlobal) {
179
179
  mergeTranslations: (locale: string, routeName: string, translations: Record<string, unknown>): void => {
180
180
  i18n.mergeTranslations(locale, routeName, translations)
181
181
  },
182
- mergeGlobalTranslations: (locale: string, translations: Record<string, unknown>): void => {
183
- i18n.mergeGlobalTranslations(locale, translations)
184
- },
185
182
  clearCache: (): void => {
186
183
  i18n.clearCache()
187
184
  },
@@ -312,7 +309,7 @@ export function useLocaleHead(astro: AstroGlobal, options: LocaleHeadOptions = {
312
309
  }
313
310
 
314
311
  /**
315
- * Props для передачи в клиентские острова (Vue, React, Svelte, Preact)
312
+ * Props to pass to client islands (Vue, React, Svelte, Preact)
316
313
  */
317
314
  export interface I18nClientProps {
318
315
  locale: string
@@ -322,7 +319,7 @@ export interface I18nClientProps {
322
319
  }
323
320
 
324
321
  /**
325
- * Создает вложенную структуру из ключа (например, 'islands.vue.title' -> { islands: { vue: { title: value } } })
322
+ * Creates a nested structure from a key (e.g. 'islands.vue.title' -> { islands: { vue: { title: value } } })
326
323
  */
327
324
  function setNestedValue(obj: Translations, key: string, value: unknown): void {
328
325
  const parts = key.split('.')
@@ -341,30 +338,30 @@ function setNestedValue(obj: Translations, key: string, value: unknown): void {
341
338
  }
342
339
 
343
340
  /**
344
- * Подготавливает пропсы для передачи в клиентский остров.
345
- * Принимает список ключей, которые нужно передать в остров.
346
- * Использует методы i18n для правильной работы с routesLocaleLinks.
347
- * currentRoute уже нормализован через middleware (getRouteName), поэтому используем его напрямую.
341
+ * Prepares props to pass to a client island.
342
+ * Accepts a list of keys to pass to the island.
343
+ * Uses i18n methods for proper routesLocaleLinks support.
344
+ * currentRoute is already normalized via middleware (getRouteName), so we use it directly.
348
345
  */
349
346
  export function getI18nProps(astro: AstroGlobal, keys?: string[]): I18nClientProps {
350
347
  const i18n = getI18n(astro)
351
348
  const locale = getLocale(astro)
352
349
  const fallbackLocale = getDefaultLocale(astro)
353
- // currentRoute уже нормализован через middleware.setRoute(getRouteName(...))
354
- // getRouteName учитывает routesLocaleLinks, если они настроены
350
+ // currentRoute is already normalized via middleware.setRoute(getRouteName(...))
351
+ // getRouteName takes routesLocaleLinks into account if configured
355
352
  const currentRoute = i18n.getRoute()
356
353
 
357
354
  const translations: Record<string, Translations> = {}
358
355
 
359
- // Если указаны ключи, извлекаем только их из кэша
356
+ // If keys are specified, extract only those from cache
360
357
  if (keys && keys.length > 0) {
361
358
  const extracted: Translations = {}
362
359
 
363
- // Используем методы i18n для получения переводов
364
- // Это гарантирует правильную работу с routesLocaleLinks
360
+ // Use i18n methods to get translations
361
+ // This guarantees proper routesLocaleLinks support
365
362
  for (const key of keys) {
366
- // Используем i18n.t() который использует helper.getTranslation внутри
367
- // и правильно резолвит ключ кэша с учетом routesLocaleLinks
363
+ // Use i18n.t() which internally uses helper.getTranslation
364
+ // and properly resolves the cache key considering routesLocaleLinks
368
365
  const value = i18n.t(key, undefined, undefined, currentRoute)
369
366
  if (value !== null && value !== undefined && value !== key) {
370
367
  setNestedValue(extracted, key, value)
@@ -375,8 +372,8 @@ export function getI18nProps(astro: AstroGlobal, keys?: string[]): I18nClientPro
375
372
  translations[currentRoute] = extracted
376
373
  }
377
374
  } else {
378
- // Если ключи не указаны, берем route-specific переводы
379
- // Используем публичный метод getRouteTranslations для безопасного доступа
375
+ // If no keys specified, get route-specific translations
376
+ // Use the public getRouteTranslations method for safe access
380
377
  const routeTrans = i18n.getRouteTranslations(locale, currentRoute)
381
378
  if (routeTrans) {
382
379
  translations[currentRoute] = routeTrans