@i18n-micro/astro 1.3.8 → 1.3.10

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/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const T=require("@i18n-micro/core"),E=require("node:async_hooks"),w=require("node:fs"),R=require("node:path"),B=require("@i18n-micro/utils/parse-path"),z=require("@i18n-micro/utils/accept-language");class j extends T.BaseI18n{constructor(e){const o=e._storage||{translations:new Map};if(super({storage:o,plural:e.plural,missingWarn:e.missingWarn,missingHandler:e.missingHandler,numberFormats:e.numberFormats,datetimeFormats:e.datetimeFormats}),this.initialMessages={},this.storage=o,this._locale=e.locale,this._fallbackLocale=e.fallbackLocale||e.locale,this._currentRoute="index",e.messages){this.initialMessages={...e.messages};for(const[a,i]of Object.entries(e.messages))this.helper.loadTranslations(a,i)}}cloneStorage(e){const o=new Map;for(const[a,i]of e.translations)o.set(a,{...i});return{translations:o}}clone(e){const o=this.cloneStorage(this.storage);return new j({locale:e||this._locale,fallbackLocale:this._fallbackLocale,plural:this.pluralFunc,missingWarn:this.missingWarn,missingHandler:this.missingHandler,numberFormats:this.formatter.getNumberFormats(),datetimeFormats:this.formatter.getDateTimeFormats(),_storage:o})}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,o){const a=`${e}:${o}`;return this.storage.translations.get(a)??null}addTranslations(e,o,a=!0){super.loadTranslationsCore(e,o,a)}addRouteTranslations(e,o,a,i=!0){super.loadRouteTranslationsCore(e,o,a,i)}mergeTranslations(e,o,a){this.helper.mergeTranslation(e,o,a,!0)}clearCache(){const e={...this.initialMessages};if(super.clearCache(),Object.keys(e).length>0)for(const[o,a]of Object.entries(e))this.helper.loadTranslations(o,a)}}let N=null;const q=new E.AsyncLocalStorage;function D(){const t=q.getStore();return t!==void 0?t:N}function A(t){N=t}function M(t,e){return q.run(t,e)}function x(t){const{locale:e,fallbackLocale:o,translationDir:a,routingStrategy:i}=t;return A(i||null),{name:"@i18n-micro/astro",hooks:{"astro:config:setup":c=>{const{updateConfig:u}=c,s="virtual:i18n-micro/config",r=`\0${s}`,l={defaultLocale:e,fallbackLocale:o||e,locales:t.locales||[],localeCodes:(t.locales||[]).map(n=>n.code),translationDir:a||null,autoDetect:t.autoDetect??!0,redirectToDefault:t.redirectToDefault??!1,localeCookie:t.localeCookie===null?null:t.localeCookie||"i18n-locale",missingWarn:t.missingWarn??!1};u({vite:{plugins:[{name:"vite-plugin-i18n-micro-config",resolveId(n){if(n===s)return r},load(n){if(n===r)return`export const config = ${JSON.stringify(l)}`}}]}})},"astro:config:done":c=>{const{injectTypes:u}=c;u({filename:"i18n-micro-env.d.ts",content:`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const j=require("@i18n-micro/core"),G=require("node:async_hooks"),R=require("node:fs"),$=require("node:path"),M=require("@i18n-micro/utils/parse-path"),V=require("@i18n-micro/utils/accept-language");class v extends j.BaseI18n{constructor(e){const r=e._storage||{translations:new Map};if(super({storage:r,plural:e.plural,missingWarn:e.missingWarn,missingHandler:e.missingHandler,numberFormats:e.numberFormats,datetimeFormats:e.datetimeFormats}),this.initialMessages={},this.storage=r,this._locale=e.locale,this._fallbackLocale=e.fallbackLocale||e.locale,this._currentRoute="index",e.messages){this.initialMessages={...e.messages};for(const[l,c]of Object.entries(e.messages))this.helper.loadTranslations(l,c)}}cloneStorage(e){const r=new Map;for(const[l,c]of e.translations)r.set(l,{...c});return{translations:r}}clone(e){const r=this.cloneStorage(this.storage);return new v({locale:e||this._locale,fallbackLocale:this._fallbackLocale,plural:this.pluralFunc,missingWarn:this.missingWarn,missingHandler:this.missingHandler,numberFormats:this.formatter.getNumberFormats(),datetimeFormats:this.formatter.getDateTimeFormats(),_storage:r})}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,r){const l=`${e}:${r}`;return this.storage.translations.get(l)??null}addTranslations(e,r,l=!0){super.loadTranslationsCore(e,r,l)}addRouteTranslations(e,r,l,c=!0){super.loadRouteTranslationsCore(e,r,l,c)}mergeTranslations(e,r,l){this.helper.mergeTranslation(e,r,l,!0)}clearCache(){const e={...this.initialMessages};if(super.clearCache(),Object.keys(e).length>0)for(const[r,l]of Object.entries(e))this.helper.loadTranslations(r,l)}}let q=null;const W=new G.AsyncLocalStorage;function _(){const t=W.getStore();return t!==void 0?t:q}function U(t){q=t}function H(t,e){return W.run(t,e)}function J(t){const{locale:e,fallbackLocale:r,translationDir:l,routingStrategy:c}=t;return U(c||null),{name:"@i18n-micro/astro",hooks:{"astro:config:setup":u=>{const{updateConfig:i}=u,o="virtual:i18n-micro/config",s=`\0${o}`,a={defaultLocale:e,fallbackLocale:r||e,locales:t.locales||[],localeCodes:(t.locales||[]).map(n=>n.code),translationDir:l||null,autoDetect:t.autoDetect??!0,redirectToDefault:t.redirectToDefault??!1,localeCookie:t.localeCookie===null?null:t.localeCookie||"i18n-locale",missingWarn:t.missingWarn??!1};i({vite:{plugins:[{name:"vite-plugin-i18n-micro-config",resolveId(n){if(n===o)return s},load(n){if(n===s)return`export const config = ${JSON.stringify(a)}`}}]}})},"astro:config:done":u=>{const{injectTypes:i}=u;i({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 G(t){return new j(t)}function W(t,e){if(w.existsSync(t))for(const o of w.readdirSync(t)){const a=R.join(t,o);if(w.statSync(a).isDirectory()){W(a,e);continue}o.endsWith(".json")&&e(a)}}function U(t){const{translationDir:e,rootDir:o=process.cwd(),disablePageLocales:a=!1}=t,i=R.resolve(o,e);if(!w.existsSync(i))return console.warn(`[i18n] Translation directory not found: ${i}`),{root:{},routes:{}};const c={root:{},routes:{}};return W(i,u=>{const s=R.relative(i,u).split(R.sep).join("/");try{const r=w.readFileSync(u,"utf-8"),l=JSON.parse(r);B.storeLoadedTranslationFile(c,s,l,a)}catch(r){console.error(`[i18n] Failed to load translation file: ${u}`,r)}}),c}function V(t,e){const{root:o,routes:a}=U(e);for(const[i,c]of Object.entries(o))t.addTranslations(i,c,!1);for(const[i,c]of Object.entries(a))for(const[u,s]of Object.entries(c))t.addRouteTranslations(u,i,B.mergeRouteTranslationsWithRoot(o[u],s),!1)}function J(t){const{i18n:e,defaultLocale:o,locales:a,localeObjects:i,autoDetect:c=!0,redirectToDefault:u=!1,routingStrategy:s}=t,r=s||D();return async(l,n)=>{if(l.locals.locale&&l.locals.i18n)return n();const f=l.url,d=f.pathname;if(!r){const b=e.clone(o),y=d==="/"||d===""?"index":d.split("/").filter(Boolean).join("-");return b.setRoute(y),l.locals.i18n=b,l.locals.locale=o,l.locals.defaultLocale=o,l.locals.locales=i||a.map(S=>({code:S})),l.locals.currentUrl=f,n()}const h={...r,getCurrentPath:()=>d,getRoute:()=>({fullPath:f.pathname+f.search,query:Object.fromEntries(f.searchParams)})},p=d.split("/").filter(Boolean)[0],P=p!==void 0&&a.includes(p);let L;P&&p?L=p:h.getLocaleFromPath?L=h.getLocaleFromPath(d,o,a):L=o;const k=e.clone(L),g=h.getRouteName?h.getRouteName(d,a):"index";return k.setRoute(g),l.locals.i18n=k,l.locals.locale=L,l.locals.defaultLocale=o,l.locals.locales=i||a.map(b=>({code:b})),l.locals.currentUrl=f,l.locals.routingStrategy=h,M(h,()=>n())}}function K(t,e,o,a,i,c="i18n-locale",u){const s=u??D();let r=a;if(s?.getLocaleFromPath)r=s.getLocaleFromPath(t,a,i);else{const n=t.split("/").filter(Boolean)[0];n&&i.includes(n)&&(r=n)}if(c!==null&&r===a&&e.get(c)){const l=e.get(c)?.value;l&&i.includes(l)&&(r=l)}if(r===a)try{const l=z.detectLocaleFromAcceptLanguage(o.get("accept-language")??void 0,i);l&&(r=l)}catch{}return r}function Z(t,e,o){const a=t.map(n=>n.code),i=(n,f=[])=>{const d=n.replace(/^\//,"").replace(/\/$/,"");if(!d)return"index";const h=d.split("/").filter(Boolean),m=h[0];return m&&f.includes(m)&&h.shift(),h.length===0?"index":h.join("-")},c=(n,f="en",d=[])=>{const m=n.split("/").filter(Boolean)[0];return m&&d.includes(m)?m:f},u=(n,f,d=[],h)=>{const m=n.split("/").filter(Boolean),p=m[0];return p&&d.includes(p)&&m.shift(),(f!==h||h===void 0)&&m.unshift(f),`/${m.join("/")}`},s=(n,f,d=[],h)=>{const p=(n.replace(/^\//,"").replace(/\/$/,"")||"").split("/").filter(Boolean),P=p[0];return P&&d.includes(P)&&p.shift(),(f!==h||h===void 0)&&p.unshift(f),`/${p.join("/")}`};return{getCurrentPath:()=>o?o().pathname:typeof window<"u"?window.location.pathname:"/",getRouteName:i,getLocaleFromPath:c,switchLocalePath:u,localizePath:s,removeLocaleFromPath:(n,f=[])=>{const d=n.split("/").filter(Boolean),h=d[0];return h&&f.includes(h)&&d.shift(),`/${d.join("/")}`},resolvePath:(n,f)=>{const d=typeof n=="string"?n:n.path||"/";return s(d,f,a,e)},getRoute:()=>{if(o){const n=o();return{fullPath:n.pathname+n.search,query:Object.fromEntries(n.searchParams)}}if(typeof window<"u"){const n=new URL(window.location.href);return{fullPath:n.pathname+n.search,query:Object.fromEntries(n.searchParams)}}return{fullPath:"/",query:{}}},push:n=>{typeof window<"u"&&(window.location.href=n.path)},replace:n=>{typeof window<"u"&&window.location.replace(n.path)}}}function F(t){const e=t.og?.trim();if(e)return e;const o=t.iso?.trim();if(!o)return null;const a=o.indexOf("_");if(a>0){const c=o.slice(0,a),u=o.slice(a+1);if(c.length>=2&&u.length===2)return`${c.toLowerCase()}_${u.toUpperCase()}`}const i=o.indexOf("-");if(i>0){const c=o.slice(0,i),u=o.slice(i+1);if(c.length>=2&&u.length===2&&/^[A-Za-z]{2}$/.test(u))return`${c.toLowerCase()}_${u.toUpperCase()}`}return null}function C(t,e={}){if(F(t)!==null||e.missingWarn===!1||process.env.NODE_ENV==="production")return;const o=e.tag??"og:locale",a=String(t.code??"unknown"),i=t.iso?`, iso: '${t.iso}'`:"";console.warn(`[i18n] Cannot derive ${o} for locale '${a}'${i}. Set locale.og (e.g. 'en_US') or use iso with a 2-letter region (e.g. 'en-US').`)}function I(t){const e=t.locals.i18n;if(!e)throw new Error("i18n instance not found. Make sure i18n middleware is configured.");return e}function v(t){return t.locals.locale||"en"}function $(t){return t.locals.defaultLocale||"en"}function _(t){return t.locals.locales||[]}function H(t){return t.locals.routingStrategy||null}function Q(t){const e=I(t),o=v(t),a=$(t),i=_(t),c=i.map(s=>s.code),u=H(t);return{locale:o,defaultLocale:a,locales:i,t:(s,r,l,n)=>e.t(s,r,l,n),ts:(s,r,l,n)=>e.ts(s,r,l,n),tc:(s,r,l)=>e.tc(s,r,l),tn:e.tn.bind(e),td:e.td.bind(e),tdr:e.tdr.bind(e),has:(s,r)=>e.has(s,r),getRoute:()=>e.getRoute(),getRouteName:s=>{const r=s||t.url.pathname;if(u?.getRouteName)return u.getRouteName(r,c);const l=r.replace(/^\//,"").replace(/\/$/,"");if(!l)return"index";const n=l.split("/").filter(Boolean),f=n[0];return f&&c.includes(f)&&n.shift(),n.length===0?"index":n.join("-")},getLocaleFromPath:s=>{const r=s||t.url.pathname;if(u?.getLocaleFromPath)return u.getLocaleFromPath(r,a,c);const n=r.split("/").filter(Boolean)[0];return n&&c.includes(n)?n:a},switchLocalePath:s=>{if(u?.switchLocalePath)return u.switchLocalePath(t.url.pathname,s,c,a);const r=t.url.pathname.split("/").filter(Boolean),l=r[0];return l&&c.includes(l)&&r.shift(),s!==a&&r.unshift(s),`/${r.join("/")}`},localizePath:(s,r)=>{if(u?.localizePath)return u.localizePath(s,r||o,c,a);const n=(s.replace(/^\//,"").replace(/\/$/,"")||"").split("/").filter(Boolean),f=n[0];return f&&c.includes(f)&&n.shift(),r&&r!==a&&n.unshift(r),`/${n.join("/")}`},getI18n:()=>e,getBasePath:s=>{const n=(s||t.url).pathname.split("/").filter(Boolean),f=n[0];return f&&c.includes(f)&&n.shift(),n.length>0?`/${n.join("/")}`:"/"},addTranslations:(s,r,l=!0)=>{e.addTranslations(s,r,l)},addRouteTranslations:(s,r,l,n=!0)=>{e.addRouteTranslations(s,r,l,n)},mergeTranslations:(s,r,l)=>{e.mergeTranslations(s,r,l)},clearCache:()=>{e.clearCache()}}}function X(t,e={}){const{baseUrl:o="/",addDirAttribute:a=!0,addSeoAttributes:i=!0}=e,c=v(t),u=$(t),r=_(t).filter(g=>!g.disabled),l=r.filter(g=>g.seo!==!1),n=r.find(g=>g.code===c);if(!n)return{htmlAttrs:{},link:[],meta:[]};const f=n.iso||c,d=F(n),h=n.dir||"auto",m={htmlAttrs:{lang:f,...a?{dir:h}:{}},link:[],meta:[]};if(!i)return m;const p=`${o}${t.url.pathname}`;m.link.push({rel:"canonical",href:p});const P=H(t),L=r.map(g=>g.code);for(const g of l){let b=t.url.pathname;if(P?.switchLocalePath)b=P.switchLocalePath(t.url.pathname,g.code,L,u);else{const S=t.url.pathname.split("/").filter(Boolean),O=S[0];O&&L.includes(O)&&S.shift(),g.code!==u&&S.unshift(g.code),b=`/${S.join("/")}`}const y=`${o}${b}`;m.link.push({rel:"alternate",href:y,hreflang:g.code}),g.iso&&g.iso!==g.code&&m.link.push({rel:"alternate",href:y,hreflang:g.iso})}if(r.find(g=>g.code===u)?.seo!==!1){let g=t.url.pathname;if(P?.switchLocalePath)g=P.switchLocalePath(t.url.pathname,u,L,u);else{const b=t.url.pathname.split("/").filter(Boolean),y=b[0];y&&L.includes(y)&&b.shift(),g=`/${b.join("/")}`}m.link.push({rel:"alternate",href:`${o}${g}`,hreflang:"x-default"})}d?m.meta.push({property:"og:locale",content:d}):C(n,{tag:"og:locale"}),m.meta.push({property:"og:url",content:p});for(const g of l){if(g.code===c)continue;const b=F(g);if(!b){C(g,{tag:"og:locale:alternate"});continue}m.meta.push({property:"og:locale:alternate",content:b})}return m}function Y(t,e,o){const a=e.split(".");let i=t;for(let u=0;u<a.length-1;u++){const s=a[u];i[s]||(i[s]={}),i=i[s]}const c=a[a.length-1];c!==void 0&&(i[c]=o)}function ee(t,e){const o=I(t),a=v(t),i=$(t),c=o.getRoute(),u={};if(e&&e.length>0){const s={};for(const r of e){const l=o.t(r,void 0,void 0,c);l!=null&&l!==r&&Y(s,r,l)}Object.keys(s).length>0&&(u[c]=s)}else{const s=o.getRouteTranslations(a,c);s&&(u[c]=s)}return{locale:a,fallbackLocale:i,currentRoute:c,translations:u}}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=G;exports.createI18nMiddleware=J;exports.detectLocale=K;exports.getDefaultLocale=$;exports.getGlobalRoutingStrategy=D;exports.getI18n=I;exports.getI18nProps=ee;exports.getLocale=v;exports.getLocales=_;exports.i18nIntegration=x;exports.loadTranslationsFromDir=U;exports.loadTranslationsIntoI18n=V;exports.runWithRoutingStrategy=M;exports.setGlobalRoutingStrategy=A;exports.useI18n=Q;exports.useLocaleHead=X;
17
+ `})}}}}function Z(t){return new v(t)}function z(t,e){if(R.existsSync(t))for(const r of R.readdirSync(t)){const l=$.join(t,r);if(R.statSync(l).isDirectory()){z(l,e);continue}r.endsWith(".json")&&e(l)}}function E(t){const{translationDir:e,rootDir:r=process.cwd(),disablePageLocales:l=!1}=t,c=$.resolve(r,e);if(!R.existsSync(c))return console.warn(`[i18n] Translation directory not found: ${c}`),{root:{},routes:{}};const u={root:{},routes:{}};return z(c,i=>{const o=$.relative(c,i).split($.sep).join("/");try{const s=R.readFileSync(i,"utf-8"),a=JSON.parse(s);M.storeLoadedTranslationFile(u,o,a,l)}catch(s){console.error(`[i18n] Failed to load translation file: ${i}`,s)}}),u}function K(t,e){const{root:r,routes:l}=E(e);for(const[c,u]of Object.entries(r))t.addTranslations(c,u,!1);for(const[c,u]of Object.entries(l))for(const[i,o]of Object.entries(u))t.addRouteTranslations(i,c,M.mergeRouteTranslationsWithRoot(r[i],o),!1)}function Q(t){const{i18n:e,defaultLocale:r,locales:l,localeObjects:c,autoDetect:u=!0,redirectToDefault:i=!1,routingStrategy:o}=t,s=o||_();return async(a,n)=>{if(a.locals.locale&&a.locals.i18n)return n();const f=a.url,d=f.pathname;if(!s){const w=e.clone(r),g=d==="/"||d===""?"index":d.split("/").filter(Boolean).join("-");return w.setRoute(g),a.locals.i18n=w,a.locals.locale=r,a.locals.defaultLocale=r,a.locals.locales=c||l.map(L=>({code:L})),a.locals.currentUrl=f,n()}const h={...s,getCurrentPath:()=>d,getRoute:()=>({fullPath:f.pathname+f.search,query:Object.fromEntries(f.searchParams)})},m=d.split("/").filter(Boolean)[0],P=m!==void 0&&l.includes(m);let S;P&&m?S=m:h.getLocaleFromPath?S=h.getLocaleFromPath(d,r,l):S=r;const y=e.clone(S),T=h.getRouteName?h.getRouteName(d,l):"index";return y.setRoute(T),a.locals.i18n=y,a.locals.locale=S,a.locals.defaultLocale=r,a.locals.locales=c||l.map(w=>({code:w})),a.locals.currentUrl=f,a.locals.routingStrategy=h,H(h,()=>n())}}function X(t,e,r,l,c,u="i18n-locale",i){const o=i??_();let s=l;if(o?.getLocaleFromPath)s=o.getLocaleFromPath(t,l,c);else{const n=t.split("/").filter(Boolean)[0];n&&c.includes(n)&&(s=n)}if(u!==null&&s===l&&e.get(u)){const a=e.get(u)?.value;a&&c.includes(a)&&(s=a)}if(s===l)try{const a=V.detectLocaleFromAcceptLanguage(r.get("accept-language")??void 0,c);a&&(s=a)}catch{}return s}function Y(t,e,r){const l=t.map(n=>n.code),c=(n,f=[])=>{const d=n.replace(/^\//,"").replace(/\/$/,"");if(!d)return"index";const h=d.split("/").filter(Boolean),p=h[0];return p&&f.includes(p)&&h.shift(),h.length===0?"index":h.join("-")},u=(n,f="en",d=[])=>{const p=n.split("/").filter(Boolean)[0];return p&&d.includes(p)?p:f},i=(n,f,d=[],h)=>{const p=n.split("/").filter(Boolean),m=p[0];return m&&d.includes(m)&&p.shift(),(f!==h||h===void 0)&&p.unshift(f),`/${p.join("/")}`},o=(n,f,d=[],h)=>{const m=(n.replace(/^\//,"").replace(/\/$/,"")||"").split("/").filter(Boolean),P=m[0];return P&&d.includes(P)&&m.shift(),(f!==h||h===void 0)&&m.unshift(f),`/${m.join("/")}`};return{getCurrentPath:()=>r?r().pathname:typeof window<"u"?window.location.pathname:"/",getRouteName:c,getLocaleFromPath:u,switchLocalePath:i,localizePath:o,removeLocaleFromPath:(n,f=[])=>{const d=n.split("/").filter(Boolean),h=d[0];return h&&f.includes(h)&&d.shift(),`/${d.join("/")}`},resolvePath:(n,f)=>{const d=typeof n=="string"?n:n.path||"/";return o(d,f,l,e)},getRoute:()=>{if(r){const n=r();return{fullPath:n.pathname+n.search,query:Object.fromEntries(n.searchParams)}}if(typeof window<"u"){const n=new URL(window.location.href);return{fullPath:n.pathname+n.search,query:Object.fromEntries(n.searchParams)}}return{fullPath:"/",query:{}}},push:n=>{typeof window<"u"&&(window.location.href=n.path)},replace:n=>{typeof window<"u"&&window.location.replace(n.path)}}}function D(t){return t.iso?.trim()||String(t.code)}function N(t){const e=t.match(/^([A-Za-z]{2,3})(?:[-_](.+))?$/);return e?{language:e[1],region:e[2]}:{language:t}}function ee(t,e={}){const{hreflangBaseLanguage:r=!1}=e;if(!r)return t.map(i=>{const o=String(i.code);return{hreflang:D(i),localeCode:o}});const l=new Map;for(const i of t){const o=String(i.code),s=D(i),{language:a,region:n}=N(s);a&&n&&!l.has(a)&&l.set(a,o),l.set(s,o)}const c=new Set,u=[];for(const i of t){const o=String(i.code),s=D(i),{language:a,region:n}=N(s);a&&n&&l.get(a)===o&&!c.has(a)&&(c.add(a),u.push({hreflang:a,localeCode:o})),c.has(s)||(c.add(s),u.push({hreflang:s,localeCode:o}))}return u}function I(t){const e=t.og?.trim();if(e)return e;const r=t.iso?.trim();if(!r)return null;const l=r.indexOf("_");if(l>0){const u=r.slice(0,l),i=r.slice(l+1);if(u.length>=2&&i.length===2)return`${u.toLowerCase()}_${i.toUpperCase()}`}const c=r.indexOf("-");if(c>0){const u=r.slice(0,c),i=r.slice(c+1);if(u.length>=2&&i.length===2&&/^[A-Za-z]{2}$/.test(i))return`${u.toLowerCase()}_${i.toUpperCase()}`}return null}function A(t,e={}){if(I(t)!==null||e.missingWarn===!1||process.env.NODE_ENV==="production")return;const r=e.tag??"og:locale",l=String(t.code??"unknown"),c=t.iso?`, iso: '${t.iso}'`:"";console.warn(`[i18n] Cannot derive ${r} for locale '${l}'${c}. Set locale.og (e.g. 'en_US') or use iso with a 2-letter region (e.g. 'en-US').`)}function O(t){const e=t.locals.i18n;if(!e)throw new Error("i18n instance not found. Make sure i18n middleware is configured.");return e}function F(t){return t.locals.locale||"en"}function k(t){return t.locals.defaultLocale||"en"}function C(t){return t.locals.locales||[]}function x(t){return t.locals.routingStrategy||null}function te(t){const e=O(t),r=F(t),l=k(t),c=C(t),u=c.map(o=>o.code),i=x(t);return{locale:r,defaultLocale:l,locales:c,t:(o,s,a,n)=>e.t(o,s,a,n),ts:(o,s,a,n)=>e.ts(o,s,a,n),tc:(o,s,a)=>e.tc(o,s,a),tn:e.tn.bind(e),td:e.td.bind(e),tdr:e.tdr.bind(e),has:(o,s)=>e.has(o,s),getRoute:()=>e.getRoute(),getRouteName:o=>{const s=o||t.url.pathname;if(i?.getRouteName)return i.getRouteName(s,u);const a=s.replace(/^\//,"").replace(/\/$/,"");if(!a)return"index";const n=a.split("/").filter(Boolean),f=n[0];return f&&u.includes(f)&&n.shift(),n.length===0?"index":n.join("-")},getLocaleFromPath:o=>{const s=o||t.url.pathname;if(i?.getLocaleFromPath)return i.getLocaleFromPath(s,l,u);const n=s.split("/").filter(Boolean)[0];return n&&u.includes(n)?n:l},switchLocalePath:o=>{if(i?.switchLocalePath)return i.switchLocalePath(t.url.pathname,o,u,l);const s=t.url.pathname.split("/").filter(Boolean),a=s[0];return a&&u.includes(a)&&s.shift(),o!==l&&s.unshift(o),`/${s.join("/")}`},localizePath:(o,s)=>{if(i?.localizePath)return i.localizePath(o,s||r,u,l);const n=(o.replace(/^\//,"").replace(/\/$/,"")||"").split("/").filter(Boolean),f=n[0];return f&&u.includes(f)&&n.shift(),s&&s!==l&&n.unshift(s),`/${n.join("/")}`},getI18n:()=>e,getBasePath:o=>{const n=(o||t.url).pathname.split("/").filter(Boolean),f=n[0];return f&&u.includes(f)&&n.shift(),n.length>0?`/${n.join("/")}`:"/"},addTranslations:(o,s,a=!0)=>{e.addTranslations(o,s,a)},addRouteTranslations:(o,s,a,n=!0)=>{e.addRouteTranslations(o,s,a,n)},mergeTranslations:(o,s,a)=>{e.mergeTranslations(o,s,a)},clearCache:()=>{e.clearCache()}}}function ne(t,e={}){const{baseUrl:r="/",addDirAttribute:l=!0,addSeoAttributes:c=!0,hreflangBaseLanguage:u=!1}=e,i=F(t),o=k(t),a=C(t).filter(g=>!g.disabled),n=a.filter(g=>g.seo!==!1),f=a.find(g=>g.code===i);if(!f)return{htmlAttrs:{},link:[],meta:[]};const d=f.iso||i,h=I(f),p=f.dir||"auto",m={htmlAttrs:{lang:d,...l?{dir:p}:{}},link:[],meta:[]};if(!c)return m;const P=`${r}${t.url.pathname}`;m.link.push({rel:"canonical",href:P});const S=x(t),y=a.map(g=>g.code),T=new Map;for(const g of n){let L=t.url.pathname;if(S?.switchLocalePath)L=S.switchLocalePath(t.url.pathname,g.code,y,o);else{const b=t.url.pathname.split("/").filter(Boolean),B=b[0];B&&y.includes(B)&&b.shift(),g.code!==o&&b.unshift(g.code),L=`/${b.join("/")}`}T.set(String(g.code),`${r}${L}`)}for(const{hreflang:g,localeCode:L}of ee(n,{hreflangBaseLanguage:u})){const b=T.get(L);b&&m.link.push({rel:"alternate",href:b,hreflang:g})}if(a.find(g=>g.code===o)?.seo!==!1){let g=t.url.pathname;if(S?.switchLocalePath)g=S.switchLocalePath(t.url.pathname,o,y,o);else{const L=t.url.pathname.split("/").filter(Boolean),b=L[0];b&&y.includes(b)&&L.shift(),g=`/${L.join("/")}`}m.link.push({rel:"alternate",href:`${r}${g}`,hreflang:"x-default"})}h?m.meta.push({property:"og:locale",content:h}):A(f,{tag:"og:locale"}),m.meta.push({property:"og:url",content:P});for(const g of n){if(g.code===i)continue;const L=I(g);if(!L){A(g,{tag:"og:locale:alternate"});continue}m.meta.push({property:"og:locale:alternate",content:L})}return m}function oe(t,e,r){const l=e.split(".");let c=t;for(let i=0;i<l.length-1;i++){const o=l[i];c[o]||(c[o]={}),c=c[o]}const u=l[l.length-1];u!==void 0&&(c[u]=r)}function ae(t,e){const r=O(t),l=F(t),c=k(t),u=r.getRoute(),i={};if(e&&e.length>0){const o={};for(const s of e){const a=r.t(s,void 0,void 0,u);a!=null&&a!==s&&oe(o,s,a)}Object.keys(o).length>0&&(i[u]=o)}else{const o=r.getRouteTranslations(l,u);o&&(i[u]=o)}return{locale:l,fallbackLocale:c,currentRoute:u,translations:i}}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=v;exports.createAstroRouterAdapter=Y;exports.createI18n=Z;exports.createI18nMiddleware=Q;exports.detectLocale=X;exports.getDefaultLocale=k;exports.getGlobalRoutingStrategy=_;exports.getI18n=O;exports.getI18nProps=ae;exports.getLocale=F;exports.getLocales=C;exports.i18nIntegration=J;exports.loadTranslationsFromDir=E;exports.loadTranslationsIntoI18n=K;exports.runWithRoutingStrategy=H;exports.setGlobalRoutingStrategy=U;exports.useI18n=te;exports.useLocaleHead=ne;
package/dist/index.mjs CHANGED
@@ -1,37 +1,37 @@
1
- import { BaseI18n as W } from "@i18n-micro/core";
2
- import { FormatService as pe, defaultPlural as Le, interpolate as Pe } from "@i18n-micro/core";
3
- import { AsyncLocalStorage as A } from "node:async_hooks";
4
- import { existsSync as F, readdirSync as M, statSync as U, readFileSync as q } from "node:fs";
5
- import { resolve as E, join as H, relative as x, sep as z } from "node:path";
6
- import { mergeRouteTranslationsWithRoot as V, storeLoadedTranslationFile as G } from "@i18n-micro/utils/parse-path";
7
- import { detectLocaleFromAcceptLanguage as J } from "@i18n-micro/utils/accept-language";
8
- class T extends W {
1
+ import { BaseI18n as U } from "@i18n-micro/core";
2
+ import { FormatService as be, defaultPlural as ye, interpolate as we } from "@i18n-micro/core";
3
+ import { AsyncLocalStorage as q } from "node:async_hooks";
4
+ import { existsSync as _, readdirSync as z, statSync as E, readFileSync as H } from "node:fs";
5
+ import { resolve as x, join as V, relative as G, sep as J } from "node:path";
6
+ import { mergeRouteTranslationsWithRoot as Z, storeLoadedTranslationFile as K } from "@i18n-micro/utils/parse-path";
7
+ import { detectLocaleFromAcceptLanguage as Q } from "@i18n-micro/utils/accept-language";
8
+ class v extends U {
9
9
  constructor(e) {
10
- const o = e._storage || {
10
+ const r = e._storage || {
11
11
  translations: /* @__PURE__ */ new Map()
12
12
  };
13
13
  if (super({
14
- storage: o,
14
+ storage: r,
15
15
  plural: e.plural,
16
16
  missingWarn: e.missingWarn,
17
17
  missingHandler: e.missingHandler,
18
18
  numberFormats: e.numberFormats,
19
19
  datetimeFormats: e.datetimeFormats
20
- }), this.initialMessages = {}, this.storage = o, this._locale = e.locale, this._fallbackLocale = e.fallbackLocale || e.locale, this._currentRoute = "index", e.messages) {
20
+ }), this.initialMessages = {}, this.storage = r, this._locale = e.locale, this._fallbackLocale = e.fallbackLocale || e.locale, this._currentRoute = "index", e.messages) {
21
21
  this.initialMessages = { ...e.messages };
22
- for (const [a, i] of Object.entries(e.messages))
23
- this.helper.loadTranslations(a, i);
22
+ for (const [l, c] of Object.entries(e.messages))
23
+ this.helper.loadTranslations(l, c);
24
24
  }
25
25
  }
26
26
  cloneStorage(e) {
27
- const o = /* @__PURE__ */ new Map();
28
- for (const [a, i] of e.translations)
29
- o.set(a, { ...i });
30
- return { translations: o };
27
+ const r = /* @__PURE__ */ new Map();
28
+ for (const [l, c] of e.translations)
29
+ r.set(l, { ...c });
30
+ return { translations: r };
31
31
  }
32
32
  clone(e) {
33
- const o = this.cloneStorage(this.storage);
34
- return new T({
33
+ const r = this.cloneStorage(this.storage);
34
+ return new v({
35
35
  locale: e || this._locale,
36
36
  fallbackLocale: this._fallbackLocale,
37
37
  plural: this.pluralFunc,
@@ -39,7 +39,7 @@ class T extends W {
39
39
  missingHandler: this.missingHandler,
40
40
  numberFormats: this.formatter.getNumberFormats(),
41
41
  datetimeFormats: this.formatter.getDateTimeFormats(),
42
- _storage: o
42
+ _storage: r
43
43
  });
44
44
  }
45
45
  get locale() {
@@ -66,68 +66,68 @@ class T extends W {
66
66
  getRoute() {
67
67
  return this._currentRoute;
68
68
  }
69
- getRouteTranslations(e, o) {
70
- const a = `${e}:${o}`;
71
- return this.storage.translations.get(a) ?? null;
69
+ getRouteTranslations(e, r) {
70
+ const l = `${e}:${r}`;
71
+ return this.storage.translations.get(l) ?? null;
72
72
  }
73
- addTranslations(e, o, a = !0) {
74
- super.loadTranslationsCore(e, o, a);
73
+ addTranslations(e, r, l = !0) {
74
+ super.loadTranslationsCore(e, r, l);
75
75
  }
76
- addRouteTranslations(e, o, a, i = !0) {
77
- super.loadRouteTranslationsCore(e, o, a, i);
76
+ addRouteTranslations(e, r, l, c = !0) {
77
+ super.loadRouteTranslationsCore(e, r, l, c);
78
78
  }
79
- mergeTranslations(e, o, a) {
80
- this.helper.mergeTranslation(e, o, a, !0);
79
+ mergeTranslations(e, r, l) {
80
+ this.helper.mergeTranslation(e, r, l, !0);
81
81
  }
82
82
  clearCache() {
83
83
  const e = { ...this.initialMessages };
84
84
  if (super.clearCache(), Object.keys(e).length > 0)
85
- for (const [o, a] of Object.entries(e))
86
- this.helper.loadTranslations(o, a);
85
+ for (const [r, l] of Object.entries(e))
86
+ this.helper.loadTranslations(r, l);
87
87
  }
88
88
  }
89
- let D = null;
90
- const _ = new A();
91
- function O() {
92
- const t = _.getStore();
93
- return t !== void 0 ? t : D;
89
+ let B = null;
90
+ const O = new q();
91
+ function I() {
92
+ const t = O.getStore();
93
+ return t !== void 0 ? t : B;
94
94
  }
95
- function K(t) {
96
- D = t;
95
+ function X(t) {
96
+ B = t;
97
97
  }
98
- function Z(t, e) {
99
- return _.run(t, e);
98
+ function Y(t, e) {
99
+ return O.run(t, e);
100
100
  }
101
- function le(t) {
102
- const { locale: e, fallbackLocale: o, translationDir: a, routingStrategy: i } = t;
103
- return K(i || null), {
101
+ function ce(t) {
102
+ const { locale: e, fallbackLocale: r, translationDir: l, routingStrategy: c } = t;
103
+ return X(c || null), {
104
104
  name: "@i18n-micro/astro",
105
105
  hooks: {
106
106
  // 1. Vite setup (virtual module) happens here
107
- "astro:config:setup": (c) => {
108
- const { updateConfig: u } = c, s = "virtual:i18n-micro/config", l = `\0${s}`, r = {
107
+ "astro:config:setup": (u) => {
108
+ const { updateConfig: i } = u, o = "virtual:i18n-micro/config", s = `\0${o}`, a = {
109
109
  defaultLocale: e,
110
- fallbackLocale: o || e,
110
+ fallbackLocale: r || e,
111
111
  locales: t.locales || [],
112
112
  localeCodes: (t.locales || []).map((n) => n.code),
113
- translationDir: a || null,
113
+ translationDir: l || null,
114
114
  autoDetect: t.autoDetect ?? !0,
115
115
  redirectToDefault: t.redirectToDefault ?? !1,
116
116
  localeCookie: t.localeCookie === null ? null : t.localeCookie || "i18n-locale",
117
117
  missingWarn: t.missingWarn ?? !1
118
118
  };
119
- u({
119
+ i({
120
120
  vite: {
121
121
  plugins: [
122
122
  {
123
123
  name: "vite-plugin-i18n-micro-config",
124
124
  resolveId(n) {
125
- if (n === s)
126
- return l;
125
+ if (n === o)
126
+ return s;
127
127
  },
128
128
  load(n) {
129
- if (n === l)
130
- return `export const config = ${JSON.stringify(r)}`;
129
+ if (n === s)
130
+ return `export const config = ${JSON.stringify(a)}`;
131
131
  }
132
132
  }
133
133
  ]
@@ -135,9 +135,9 @@ function le(t) {
135
135
  });
136
136
  },
137
137
  // 2. Type injection happens here (per Astro documentation)
138
- "astro:config:done": (c) => {
139
- const { injectTypes: u } = c;
140
- u({
138
+ "astro:config:done": (u) => {
139
+ const { injectTypes: i } = u;
140
+ i({
141
141
  filename: "i18n-micro-env.d.ts",
142
142
  content: `
143
143
  /// <reference types="@i18n-micro/astro/env" />
@@ -161,131 +161,131 @@ function le(t) {
161
161
  }
162
162
  };
163
163
  }
164
- function re(t) {
165
- return new T(t);
164
+ function ue(t) {
165
+ return new v(t);
166
166
  }
167
- function C(t, e) {
168
- if (F(t))
169
- for (const o of M(t)) {
170
- const a = H(t, o);
171
- if (U(a).isDirectory()) {
172
- C(a, e);
167
+ function N(t, e) {
168
+ if (_(t))
169
+ for (const r of z(t)) {
170
+ const l = V(t, r);
171
+ if (E(l).isDirectory()) {
172
+ N(l, e);
173
173
  continue;
174
174
  }
175
- o.endsWith(".json") && e(a);
175
+ r.endsWith(".json") && e(l);
176
176
  }
177
177
  }
178
- function Q(t) {
179
- const { translationDir: e, rootDir: o = process.cwd(), disablePageLocales: a = !1 } = t, i = E(o, e);
180
- if (!F(i))
181
- return console.warn(`[i18n] Translation directory not found: ${i}`), { root: {}, routes: {} };
182
- const c = { root: {}, routes: {} };
183
- return C(i, (u) => {
184
- const s = x(i, u).split(z).join("/");
178
+ function ee(t) {
179
+ const { translationDir: e, rootDir: r = process.cwd(), disablePageLocales: l = !1 } = t, c = x(r, e);
180
+ if (!_(c))
181
+ return console.warn(`[i18n] Translation directory not found: ${c}`), { root: {}, routes: {} };
182
+ const u = { root: {}, routes: {} };
183
+ return N(c, (i) => {
184
+ const o = G(c, i).split(J).join("/");
185
185
  try {
186
- const l = q(u, "utf-8"), r = JSON.parse(l);
187
- G(c, s, r, a);
188
- } catch (l) {
189
- console.error(`[i18n] Failed to load translation file: ${u}`, l);
186
+ const s = H(i, "utf-8"), a = JSON.parse(s);
187
+ K(u, o, a, l);
188
+ } catch (s) {
189
+ console.error(`[i18n] Failed to load translation file: ${i}`, s);
190
190
  }
191
- }), c;
191
+ }), u;
192
192
  }
193
- function se(t, e) {
194
- const { root: o, routes: a } = Q(e);
195
- for (const [i, c] of Object.entries(o))
196
- t.addTranslations(i, c, !1);
197
- for (const [i, c] of Object.entries(a))
198
- for (const [u, s] of Object.entries(c))
199
- t.addRouteTranslations(u, i, V(o[u], s), !1);
193
+ function fe(t, e) {
194
+ const { root: r, routes: l } = ee(e);
195
+ for (const [c, u] of Object.entries(r))
196
+ t.addTranslations(c, u, !1);
197
+ for (const [c, u] of Object.entries(l))
198
+ for (const [i, o] of Object.entries(u))
199
+ t.addRouteTranslations(i, c, Z(r[i], o), !1);
200
200
  }
201
- function ie(t) {
201
+ function ge(t) {
202
202
  const {
203
203
  i18n: e,
204
204
  // This is the global singleton with cache
205
- defaultLocale: o,
206
- locales: a,
207
- localeObjects: i,
208
- autoDetect: c = !0,
209
- redirectToDefault: u = !1,
210
- routingStrategy: s
211
- } = t, l = s || O();
212
- return async (r, n) => {
213
- if (r.locals.locale && r.locals.i18n)
205
+ defaultLocale: r,
206
+ locales: l,
207
+ localeObjects: c,
208
+ autoDetect: u = !0,
209
+ redirectToDefault: i = !1,
210
+ routingStrategy: o
211
+ } = t, s = o || I();
212
+ return async (a, n) => {
213
+ if (a.locals.locale && a.locals.i18n)
214
214
  return n();
215
- const f = r.url, h = f.pathname;
216
- if (!l) {
217
- const L = e.clone(o), S = h === "/" || h === "" ? "index" : h.split("/").filter(Boolean).join("-");
218
- return L.setRoute(S), r.locals.i18n = L, r.locals.locale = o, r.locals.defaultLocale = o, r.locals.locales = i || a.map((y) => ({ code: y })), r.locals.currentUrl = f, n();
215
+ const f = a.url, h = f.pathname;
216
+ if (!s) {
217
+ const w = e.clone(r), g = h === "/" || h === "" ? "index" : h.split("/").filter(Boolean).join("-");
218
+ return w.setRoute(g), a.locals.i18n = w, a.locals.locale = r, a.locals.defaultLocale = r, a.locals.locales = c || l.map((L) => ({ code: L })), a.locals.currentUrl = f, n();
219
219
  }
220
220
  const d = {
221
- ...l,
221
+ ...s,
222
222
  getCurrentPath: () => h,
223
223
  getRoute: () => ({
224
224
  fullPath: f.pathname + f.search,
225
225
  query: Object.fromEntries(f.searchParams)
226
226
  })
227
- }, p = h.split("/").filter(Boolean)[0], b = p !== void 0 && a.includes(p);
228
- let P;
229
- b && p ? P = p : d.getLocaleFromPath ? P = d.getLocaleFromPath(h, o, a) : P = o;
230
- const w = e.clone(P), g = d.getRouteName ? d.getRouteName(h, a) : "index";
231
- return w.setRoute(g), r.locals.i18n = w, r.locals.locale = P, r.locals.defaultLocale = o, r.locals.locales = i || a.map((L) => ({ code: L })), r.locals.currentUrl = f, r.locals.routingStrategy = d, Z(d, () => n());
227
+ }, m = h.split("/").filter(Boolean)[0], b = m !== void 0 && l.includes(m);
228
+ let S;
229
+ b && m ? S = m : d.getLocaleFromPath ? S = d.getLocaleFromPath(h, r, l) : S = r;
230
+ const y = e.clone(S), R = d.getRouteName ? d.getRouteName(h, l) : "index";
231
+ return y.setRoute(R), a.locals.i18n = y, a.locals.locale = S, a.locals.defaultLocale = r, a.locals.locales = c || l.map((w) => ({ code: w })), a.locals.currentUrl = f, a.locals.routingStrategy = d, Y(d, () => n());
232
232
  };
233
233
  }
234
- function ce(t, e, o, a, i, c = "i18n-locale", u) {
235
- const s = u ?? O();
236
- let l = a;
237
- if (s?.getLocaleFromPath)
238
- l = s.getLocaleFromPath(t, a, i);
234
+ function he(t, e, r, l, c, u = "i18n-locale", i) {
235
+ const o = i ?? I();
236
+ let s = l;
237
+ if (o?.getLocaleFromPath)
238
+ s = o.getLocaleFromPath(t, l, c);
239
239
  else {
240
240
  const n = t.split("/").filter(Boolean)[0];
241
- n && i.includes(n) && (l = n);
241
+ n && c.includes(n) && (s = n);
242
242
  }
243
- if (c !== null && l === a && e.get(c)) {
244
- const r = e.get(c)?.value;
245
- r && i.includes(r) && (l = r);
243
+ if (u !== null && s === l && e.get(u)) {
244
+ const a = e.get(u)?.value;
245
+ a && c.includes(a) && (s = a);
246
246
  }
247
- if (l === a)
247
+ if (s === l)
248
248
  try {
249
- const r = J(o.get("accept-language") ?? void 0, i);
250
- r && (l = r);
249
+ const a = Q(r.get("accept-language") ?? void 0, c);
250
+ a && (s = a);
251
251
  } catch {
252
252
  }
253
- return l;
253
+ return s;
254
254
  }
255
- function ue(t, e, o) {
256
- const a = t.map((n) => n.code), i = (n, f = []) => {
255
+ function de(t, e, r) {
256
+ const l = t.map((n) => n.code), c = (n, f = []) => {
257
257
  const h = n.replace(/^\//, "").replace(/\/$/, "");
258
258
  if (!h)
259
259
  return "index";
260
- const d = h.split("/").filter(Boolean), m = d[0];
261
- return m && f.includes(m) && d.shift(), d.length === 0 ? "index" : d.join("-");
262
- }, c = (n, f = "en", h = []) => {
263
- const m = n.split("/").filter(Boolean)[0];
264
- return m && h.includes(m) ? m : f;
265
- }, u = (n, f, h = [], d) => {
266
- const m = n.split("/").filter(Boolean), p = m[0];
267
- return p && h.includes(p) && m.shift(), (f !== d || d === void 0) && m.unshift(f), `/${m.join("/")}`;
268
- }, s = (n, f, h = [], d) => {
269
- const p = (n.replace(/^\//, "").replace(/\/$/, "") || "").split("/").filter(Boolean), b = p[0];
270
- return b && h.includes(b) && p.shift(), (f !== d || d === void 0) && p.unshift(f), `/${p.join("/")}`;
260
+ const d = h.split("/").filter(Boolean), p = d[0];
261
+ return p && f.includes(p) && d.shift(), d.length === 0 ? "index" : d.join("-");
262
+ }, u = (n, f = "en", h = []) => {
263
+ const p = n.split("/").filter(Boolean)[0];
264
+ return p && h.includes(p) ? p : f;
265
+ }, i = (n, f, h = [], d) => {
266
+ const p = n.split("/").filter(Boolean), m = p[0];
267
+ return m && h.includes(m) && p.shift(), (f !== d || d === void 0) && p.unshift(f), `/${p.join("/")}`;
268
+ }, o = (n, f, h = [], d) => {
269
+ const m = (n.replace(/^\//, "").replace(/\/$/, "") || "").split("/").filter(Boolean), b = m[0];
270
+ return b && h.includes(b) && m.shift(), (f !== d || d === void 0) && m.unshift(f), `/${m.join("/")}`;
271
271
  };
272
272
  return {
273
- getCurrentPath: () => o ? o().pathname : typeof window < "u" ? window.location.pathname : "/",
274
- getRouteName: i,
275
- getLocaleFromPath: c,
276
- switchLocalePath: u,
277
- localizePath: s,
273
+ getCurrentPath: () => r ? r().pathname : typeof window < "u" ? window.location.pathname : "/",
274
+ getRouteName: c,
275
+ getLocaleFromPath: u,
276
+ switchLocalePath: i,
277
+ localizePath: o,
278
278
  removeLocaleFromPath: (n, f = []) => {
279
279
  const h = n.split("/").filter(Boolean), d = h[0];
280
280
  return d && f.includes(d) && h.shift(), `/${h.join("/")}`;
281
281
  },
282
282
  resolvePath: (n, f) => {
283
283
  const h = typeof n == "string" ? n : n.path || "/";
284
- return s(h, f, a, e);
284
+ return o(h, f, l, e);
285
285
  },
286
286
  getRoute: () => {
287
- if (o) {
288
- const n = o();
287
+ if (r) {
288
+ const n = r();
289
289
  return {
290
290
  fullPath: n.pathname + n.search,
291
291
  query: Object.fromEntries(n.searchParams)
@@ -312,183 +312,208 @@ function ue(t, e, o) {
312
312
  }
313
313
  };
314
314
  }
315
- function R(t) {
315
+ function T(t) {
316
+ return t.iso?.trim() || String(t.code);
317
+ }
318
+ function D(t) {
319
+ const e = t.match(/^([A-Za-z]{2,3})(?:[-_](.+))?$/);
320
+ return e ? { language: e[1], region: e[2] } : { language: t };
321
+ }
322
+ function te(t, e = {}) {
323
+ const { hreflangBaseLanguage: r = !1 } = e;
324
+ if (!r)
325
+ return t.map((i) => {
326
+ const o = String(i.code);
327
+ return { hreflang: T(i), localeCode: o };
328
+ });
329
+ const l = /* @__PURE__ */ new Map();
330
+ for (const i of t) {
331
+ const o = String(i.code), s = T(i), { language: a, region: n } = D(s);
332
+ a && n && !l.has(a) && l.set(a, o), l.set(s, o);
333
+ }
334
+ const c = /* @__PURE__ */ new Set(), u = [];
335
+ for (const i of t) {
336
+ const o = String(i.code), s = T(i), { language: a, region: n } = D(s);
337
+ a && n && l.get(a) === o && !c.has(a) && (c.add(a), u.push({ hreflang: a, localeCode: o })), c.has(s) || (c.add(s), u.push({ hreflang: s, localeCode: o }));
338
+ }
339
+ return u;
340
+ }
341
+ function $(t) {
316
342
  const e = t.og?.trim();
317
343
  if (e) return e;
318
- const o = t.iso?.trim();
319
- if (!o) return null;
320
- const a = o.indexOf("_");
321
- if (a > 0) {
322
- const c = o.slice(0, a), u = o.slice(a + 1);
323
- if (c.length >= 2 && u.length === 2)
324
- return `${c.toLowerCase()}_${u.toUpperCase()}`;
344
+ const r = t.iso?.trim();
345
+ if (!r) return null;
346
+ const l = r.indexOf("_");
347
+ if (l > 0) {
348
+ const u = r.slice(0, l), i = r.slice(l + 1);
349
+ if (u.length >= 2 && i.length === 2)
350
+ return `${u.toLowerCase()}_${i.toUpperCase()}`;
325
351
  }
326
- const i = o.indexOf("-");
327
- if (i > 0) {
328
- const c = o.slice(0, i), u = o.slice(i + 1);
329
- if (c.length >= 2 && u.length === 2 && /^[A-Za-z]{2}$/.test(u))
330
- return `${c.toLowerCase()}_${u.toUpperCase()}`;
352
+ const c = r.indexOf("-");
353
+ if (c > 0) {
354
+ const u = r.slice(0, c), i = r.slice(c + 1);
355
+ if (u.length >= 2 && i.length === 2 && /^[A-Za-z]{2}$/.test(i))
356
+ return `${u.toLowerCase()}_${i.toUpperCase()}`;
331
357
  }
332
358
  return null;
333
359
  }
334
- function k(t, e = {}) {
335
- if (R(t) !== null || e.missingWarn === !1 || process.env.NODE_ENV === "production") return;
336
- const o = e.tag ?? "og:locale", a = String(t.code ?? "unknown"), i = t.iso ? `, iso: '${t.iso}'` : "";
360
+ function C(t, e = {}) {
361
+ if ($(t) !== null || e.missingWarn === !1 || process.env.NODE_ENV === "production") return;
362
+ const r = e.tag ?? "og:locale", l = String(t.code ?? "unknown"), c = t.iso ? `, iso: '${t.iso}'` : "";
337
363
  console.warn(
338
- `[i18n] Cannot derive ${o} for locale '${a}'${i}. Set locale.og (e.g. 'en_US') or use iso with a 2-letter region (e.g. 'en-US').`
364
+ `[i18n] Cannot derive ${r} for locale '${l}'${c}. Set locale.og (e.g. 'en_US') or use iso with a 2-letter region (e.g. 'en-US').`
339
365
  );
340
366
  }
341
- function B(t) {
367
+ function M(t) {
342
368
  const e = t.locals.i18n;
343
369
  if (!e)
344
370
  throw new Error("i18n instance not found. Make sure i18n middleware is configured.");
345
371
  return e;
346
372
  }
347
- function $(t) {
373
+ function j(t) {
348
374
  return t.locals.locale || "en";
349
375
  }
350
- function v(t) {
376
+ function k(t) {
351
377
  return t.locals.defaultLocale || "en";
352
378
  }
353
- function I(t) {
379
+ function A(t) {
354
380
  return t.locals.locales || [];
355
381
  }
356
- function N(t) {
382
+ function W(t) {
357
383
  return t.locals.routingStrategy || null;
358
384
  }
359
- function fe(t) {
360
- const e = B(t), o = $(t), a = v(t), i = I(t), c = i.map((s) => s.code), u = N(t);
385
+ function me(t) {
386
+ const e = M(t), r = j(t), l = k(t), c = A(t), u = c.map((o) => o.code), i = W(t);
361
387
  return {
362
388
  // Current locale
363
- locale: o,
364
- defaultLocale: a,
365
- locales: i,
389
+ locale: r,
390
+ defaultLocale: l,
391
+ locales: c,
366
392
  // Translation methods
367
- t: (s, l, r, n) => e.t(s, l, r, n),
368
- ts: (s, l, r, n) => e.ts(s, l, r, n),
369
- tc: (s, l, r) => e.tc(s, l, r),
393
+ t: (o, s, a, n) => e.t(o, s, a, n),
394
+ ts: (o, s, a, n) => e.ts(o, s, a, n),
395
+ tc: (o, s, a) => e.tc(o, s, a),
370
396
  tn: e.tn.bind(e),
371
397
  td: e.td.bind(e),
372
398
  tdr: e.tdr.bind(e),
373
- has: (s, l) => e.has(s, l),
399
+ has: (o, s) => e.has(o, s),
374
400
  // Route management
375
401
  getRoute: () => e.getRoute(),
376
- getRouteName: (s) => {
377
- const l = s || t.url.pathname;
378
- if (u?.getRouteName)
379
- return u.getRouteName(l, c);
380
- const r = l.replace(/^\//, "").replace(/\/$/, "");
381
- if (!r) return "index";
382
- const n = r.split("/").filter(Boolean), f = n[0];
383
- return f && c.includes(f) && n.shift(), n.length === 0 ? "index" : n.join("-");
402
+ getRouteName: (o) => {
403
+ const s = o || t.url.pathname;
404
+ if (i?.getRouteName)
405
+ return i.getRouteName(s, u);
406
+ const a = s.replace(/^\//, "").replace(/\/$/, "");
407
+ if (!a) return "index";
408
+ const n = a.split("/").filter(Boolean), f = n[0];
409
+ return f && u.includes(f) && n.shift(), n.length === 0 ? "index" : n.join("-");
384
410
  },
385
- getLocaleFromPath: (s) => {
386
- const l = s || t.url.pathname;
387
- if (u?.getLocaleFromPath)
388
- return u.getLocaleFromPath(l, a, c);
389
- const n = l.split("/").filter(Boolean)[0];
390
- return n && c.includes(n) ? n : a;
411
+ getLocaleFromPath: (o) => {
412
+ const s = o || t.url.pathname;
413
+ if (i?.getLocaleFromPath)
414
+ return i.getLocaleFromPath(s, l, u);
415
+ const n = s.split("/").filter(Boolean)[0];
416
+ return n && u.includes(n) ? n : l;
391
417
  },
392
418
  // Path utilities
393
- switchLocalePath: (s) => {
394
- if (u?.switchLocalePath)
395
- return u.switchLocalePath(t.url.pathname, s, c, a);
396
- const l = t.url.pathname.split("/").filter(Boolean), r = l[0];
397
- return r && c.includes(r) && l.shift(), s !== a && l.unshift(s), `/${l.join("/")}`;
419
+ switchLocalePath: (o) => {
420
+ if (i?.switchLocalePath)
421
+ return i.switchLocalePath(t.url.pathname, o, u, l);
422
+ const s = t.url.pathname.split("/").filter(Boolean), a = s[0];
423
+ return a && u.includes(a) && s.shift(), o !== l && s.unshift(o), `/${s.join("/")}`;
398
424
  },
399
- localizePath: (s, l) => {
400
- if (u?.localizePath)
401
- return u.localizePath(s, l || o, c, a);
402
- const n = (s.replace(/^\//, "").replace(/\/$/, "") || "").split("/").filter(Boolean), f = n[0];
403
- return f && c.includes(f) && n.shift(), l && l !== a && n.unshift(l), `/${n.join("/")}`;
425
+ localizePath: (o, s) => {
426
+ if (i?.localizePath)
427
+ return i.localizePath(o, s || r, u, l);
428
+ const n = (o.replace(/^\//, "").replace(/\/$/, "") || "").split("/").filter(Boolean), f = n[0];
429
+ return f && u.includes(f) && n.shift(), s && s !== l && n.unshift(s), `/${n.join("/")}`;
404
430
  },
405
431
  // Get i18n instance
406
432
  getI18n: () => e,
407
433
  // Get base path without locale (for rewrite)
408
- getBasePath: (s) => {
409
- const n = (s || t.url).pathname.split("/").filter(Boolean), f = n[0];
410
- return f && c.includes(f) && n.shift(), n.length > 0 ? `/${n.join("/")}` : "/";
434
+ getBasePath: (o) => {
435
+ const n = (o || t.url).pathname.split("/").filter(Boolean), f = n[0];
436
+ return f && u.includes(f) && n.shift(), n.length > 0 ? `/${n.join("/")}` : "/";
411
437
  },
412
438
  // Translation management
413
- addTranslations: (s, l, r = !0) => {
414
- e.addTranslations(s, l, r);
439
+ addTranslations: (o, s, a = !0) => {
440
+ e.addTranslations(o, s, a);
415
441
  },
416
- addRouteTranslations: (s, l, r, n = !0) => {
417
- e.addRouteTranslations(s, l, r, n);
442
+ addRouteTranslations: (o, s, a, n = !0) => {
443
+ e.addRouteTranslations(o, s, a, n);
418
444
  },
419
- mergeTranslations: (s, l, r) => {
420
- e.mergeTranslations(s, l, r);
445
+ mergeTranslations: (o, s, a) => {
446
+ e.mergeTranslations(o, s, a);
421
447
  },
422
448
  clearCache: () => {
423
449
  e.clearCache();
424
450
  }
425
451
  };
426
452
  }
427
- function ge(t, e = {}) {
428
- const { baseUrl: o = "/", addDirAttribute: a = !0, addSeoAttributes: i = !0 } = e, c = $(t), u = v(t), l = I(t).filter((g) => !g.disabled), r = l.filter((g) => g.seo !== !1), n = l.find((g) => g.code === c);
429
- if (!n)
453
+ function pe(t, e = {}) {
454
+ const { baseUrl: r = "/", addDirAttribute: l = !0, addSeoAttributes: c = !0, hreflangBaseLanguage: u = !1 } = e, i = j(t), o = k(t), a = A(t).filter((g) => !g.disabled), n = a.filter((g) => g.seo !== !1), f = a.find((g) => g.code === i);
455
+ if (!f)
430
456
  return { htmlAttrs: {}, link: [], meta: [] };
431
- const f = n.iso || c, h = R(n), d = n.dir || "auto", m = {
457
+ const h = f.iso || i, d = $(f), p = f.dir || "auto", m = {
432
458
  htmlAttrs: {
433
- lang: f,
434
- ...a ? { dir: d } : {}
459
+ lang: h,
460
+ ...l ? { dir: p } : {}
435
461
  },
436
462
  link: [],
437
463
  meta: []
438
464
  };
439
- if (!i)
465
+ if (!c)
440
466
  return m;
441
- const p = `${o}${t.url.pathname}`;
467
+ const b = `${r}${t.url.pathname}`;
442
468
  m.link.push({
443
469
  rel: "canonical",
444
- href: p
470
+ href: b
445
471
  });
446
- const b = N(t), P = l.map((g) => g.code);
447
- for (const g of r) {
472
+ const S = W(t), y = a.map((g) => g.code), R = /* @__PURE__ */ new Map();
473
+ for (const g of n) {
448
474
  let L = t.url.pathname;
449
- if (b?.switchLocalePath)
450
- L = b.switchLocalePath(t.url.pathname, g.code, P, u);
475
+ if (S?.switchLocalePath)
476
+ L = S.switchLocalePath(t.url.pathname, g.code, y, o);
451
477
  else {
452
- const y = t.url.pathname.split("/").filter(Boolean), j = y[0];
453
- j && P.includes(j) && y.shift(), g.code !== u && y.unshift(g.code), L = `/${y.join("/")}`;
478
+ const P = t.url.pathname.split("/").filter(Boolean), F = P[0];
479
+ F && y.includes(F) && P.shift(), g.code !== o && P.unshift(g.code), L = `/${P.join("/")}`;
454
480
  }
455
- const S = `${o}${L}`;
456
- m.link.push({
457
- rel: "alternate",
458
- href: S,
459
- hreflang: g.code
460
- }), g.iso && g.iso !== g.code && m.link.push({
481
+ R.set(String(g.code), `${r}${L}`);
482
+ }
483
+ for (const { hreflang: g, localeCode: L } of te(n, { hreflangBaseLanguage: u })) {
484
+ const P = R.get(L);
485
+ P && m.link.push({
461
486
  rel: "alternate",
462
- href: S,
463
- hreflang: g.iso
487
+ href: P,
488
+ hreflang: g
464
489
  });
465
490
  }
466
- if (l.find((g) => g.code === u)?.seo !== !1) {
491
+ if (a.find((g) => g.code === o)?.seo !== !1) {
467
492
  let g = t.url.pathname;
468
- if (b?.switchLocalePath)
469
- g = b.switchLocalePath(t.url.pathname, u, P, u);
493
+ if (S?.switchLocalePath)
494
+ g = S.switchLocalePath(t.url.pathname, o, y, o);
470
495
  else {
471
- const L = t.url.pathname.split("/").filter(Boolean), S = L[0];
472
- S && P.includes(S) && L.shift(), g = `/${L.join("/")}`;
496
+ const L = t.url.pathname.split("/").filter(Boolean), P = L[0];
497
+ P && y.includes(P) && L.shift(), g = `/${L.join("/")}`;
473
498
  }
474
499
  m.link.push({
475
500
  rel: "alternate",
476
- href: `${o}${g}`,
501
+ href: `${r}${g}`,
477
502
  hreflang: "x-default"
478
503
  });
479
504
  }
480
- h ? m.meta.push({
505
+ d ? m.meta.push({
481
506
  property: "og:locale",
482
- content: h
483
- }) : k(n, { tag: "og:locale" }), m.meta.push({
507
+ content: d
508
+ }) : C(f, { tag: "og:locale" }), m.meta.push({
484
509
  property: "og:url",
485
- content: p
510
+ content: b
486
511
  });
487
- for (const g of r) {
488
- if (g.code === c) continue;
489
- const L = R(g);
512
+ for (const g of n) {
513
+ if (g.code === i) continue;
514
+ const L = $(g);
490
515
  if (!L) {
491
- k(g, { tag: "og:locale:alternate" });
516
+ C(g, { tag: "og:locale:alternate" });
492
517
  continue;
493
518
  }
494
519
  m.meta.push({
@@ -498,56 +523,56 @@ function ge(t, e = {}) {
498
523
  }
499
524
  return m;
500
525
  }
501
- function X(t, e, o) {
502
- const a = e.split(".");
503
- let i = t;
504
- for (let u = 0; u < a.length - 1; u++) {
505
- const s = a[u];
506
- i[s] || (i[s] = {}), i = i[s];
526
+ function ne(t, e, r) {
527
+ const l = e.split(".");
528
+ let c = t;
529
+ for (let i = 0; i < l.length - 1; i++) {
530
+ const o = l[i];
531
+ c[o] || (c[o] = {}), c = c[o];
507
532
  }
508
- const c = a[a.length - 1];
509
- c !== void 0 && (i[c] = o);
533
+ const u = l[l.length - 1];
534
+ u !== void 0 && (c[u] = r);
510
535
  }
511
- function he(t, e) {
512
- const o = B(t), a = $(t), i = v(t), c = o.getRoute(), u = {};
536
+ function Le(t, e) {
537
+ const r = M(t), l = j(t), c = k(t), u = r.getRoute(), i = {};
513
538
  if (e && e.length > 0) {
514
- const s = {};
515
- for (const l of e) {
516
- const r = o.t(l, void 0, void 0, c);
517
- r != null && r !== l && X(s, l, r);
539
+ const o = {};
540
+ for (const s of e) {
541
+ const a = r.t(s, void 0, void 0, u);
542
+ a != null && a !== s && ne(o, s, a);
518
543
  }
519
- Object.keys(s).length > 0 && (u[c] = s);
544
+ Object.keys(o).length > 0 && (i[u] = o);
520
545
  } else {
521
- const s = o.getRouteTranslations(a, c);
522
- s && (u[c] = s);
546
+ const o = r.getRouteTranslations(l, u);
547
+ o && (i[u] = o);
523
548
  }
524
549
  return {
525
- locale: a,
526
- fallbackLocale: i,
527
- currentRoute: c,
528
- translations: u
550
+ locale: l,
551
+ fallbackLocale: c,
552
+ currentRoute: u,
553
+ translations: i
529
554
  };
530
555
  }
531
556
  export {
532
- T as AstroI18n,
533
- pe as FormatService,
534
- ue as createAstroRouterAdapter,
535
- re as createI18n,
536
- ie as createI18nMiddleware,
537
- Le as defaultPlural,
538
- ce as detectLocale,
539
- v as getDefaultLocale,
540
- O as getGlobalRoutingStrategy,
541
- B as getI18n,
542
- he as getI18nProps,
543
- $ as getLocale,
544
- I as getLocales,
545
- le as i18nIntegration,
546
- Pe as interpolate,
547
- Q as loadTranslationsFromDir,
548
- se as loadTranslationsIntoI18n,
549
- Z as runWithRoutingStrategy,
550
- K as setGlobalRoutingStrategy,
551
- fe as useI18n,
552
- ge as useLocaleHead
557
+ v as AstroI18n,
558
+ be as FormatService,
559
+ de as createAstroRouterAdapter,
560
+ ue as createI18n,
561
+ ge as createI18nMiddleware,
562
+ ye as defaultPlural,
563
+ he as detectLocale,
564
+ k as getDefaultLocale,
565
+ I as getGlobalRoutingStrategy,
566
+ M as getI18n,
567
+ Le as getI18nProps,
568
+ j as getLocale,
569
+ A as getLocales,
570
+ ce as i18nIntegration,
571
+ we as interpolate,
572
+ ee as loadTranslationsFromDir,
573
+ fe as loadTranslationsIntoI18n,
574
+ Y as runWithRoutingStrategy,
575
+ X as setGlobalRoutingStrategy,
576
+ me as useI18n,
577
+ pe as useLocaleHead
553
578
  };
package/dist/utils.d.ts CHANGED
@@ -59,6 +59,11 @@ export interface LocaleHeadOptions {
59
59
  baseUrl?: string;
60
60
  addDirAttribute?: boolean;
61
61
  addSeoAttributes?: boolean;
62
+ /**
63
+ * Also emit bare-language `hreflang` tags derived from `iso` (never from routing `code`).
64
+ * @default false
65
+ */
66
+ hreflangBaseLanguage?: boolean;
62
67
  }
63
68
  export interface LocaleHeadResult {
64
69
  htmlAttrs: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@i18n-micro/astro",
3
- "version": "1.3.8",
3
+ "version": "1.3.10",
4
4
  "description": "Astro integration for i18n-micro — translations, routing, and islands.",
5
5
  "keywords": [
6
6
  "astro",
@@ -77,10 +77,10 @@
77
77
  "access": "public"
78
78
  },
79
79
  "dependencies": {
80
- "@i18n-micro/types": "1.2.7",
81
- "@i18n-micro/core": "1.3.8",
82
- "@i18n-micro/utils": "1.0.8",
83
- "@i18n-micro/node": "1.2.4"
80
+ "@i18n-micro/node": "^1.2.6",
81
+ "@i18n-micro/core": "^1.3.11",
82
+ "@i18n-micro/utils": "^1.0.13",
83
+ "@i18n-micro/types": "^1.2.10"
84
84
  },
85
85
  "devDependencies": {
86
86
  "astro": "^5.0.0 || ^6.0.0 || ^7.0.0",
package/src/utils.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { CleanTranslation, Locale, Params, TranslationKey, Translations } from '@i18n-micro/types'
2
+ import { resolveHreflangAlternates } from '@i18n-micro/utils/resolve-hreflang'
2
3
  import { resolveOgLocale, warnUnresolvedOgLocale } from '@i18n-micro/utils/resolve-og-locale'
3
4
  import type { AstroGlobal } from 'astro'
4
5
  import type { AstroI18n } from './composer'
@@ -187,6 +188,11 @@ export interface LocaleHeadOptions {
187
188
  baseUrl?: string
188
189
  addDirAttribute?: boolean
189
190
  addSeoAttributes?: boolean
191
+ /**
192
+ * Also emit bare-language `hreflang` tags derived from `iso` (never from routing `code`).
193
+ * @default false
194
+ */
195
+ hreflangBaseLanguage?: boolean
190
196
  }
191
197
 
192
198
  export interface LocaleHeadResult {
@@ -206,7 +212,7 @@ export interface LocaleHeadResult {
206
212
  }
207
213
 
208
214
  export function useLocaleHead(astro: AstroGlobal, options: LocaleHeadOptions = {}): LocaleHeadResult {
209
- const { baseUrl = '/', addDirAttribute = true, addSeoAttributes = true } = options
215
+ const { baseUrl = '/', addDirAttribute = true, addSeoAttributes = true, hreflangBaseLanguage = false } = options
210
216
 
211
217
  const locale = getLocale(astro)
212
218
  const defaultLocale = getDefaultLocale(astro)
@@ -248,6 +254,7 @@ export function useLocaleHead(astro: AstroGlobal, options: LocaleHeadOptions = {
248
254
  const allLocaleCodes = locales.map((l) => l.code)
249
255
 
250
256
  // Alternate languages (includes current locale for self-referencing hreflang, per Google guidelines)
257
+ const hrefByCode = new Map<string, string>()
251
258
  for (const loc of localesForSeo) {
252
259
  let alternatePath = astro.url.pathname
253
260
  if (routingStrategy?.switchLocalePath) {
@@ -264,21 +271,17 @@ export function useLocaleHead(astro: AstroGlobal, options: LocaleHeadOptions = {
264
271
  }
265
272
  alternatePath = `/${segments.join('/')}`
266
273
  }
267
- const alternateUrl = `${baseUrl}${alternatePath}`
274
+ hrefByCode.set(String(loc.code), `${baseUrl}${alternatePath}`)
275
+ }
268
276
 
277
+ for (const { hreflang, localeCode } of resolveHreflangAlternates(localesForSeo, { hreflangBaseLanguage })) {
278
+ const alternateUrl = hrefByCode.get(localeCode)
279
+ if (!alternateUrl) continue
269
280
  result.link.push({
270
281
  rel: 'alternate',
271
282
  href: alternateUrl,
272
- hreflang: loc.code,
283
+ hreflang,
273
284
  })
274
-
275
- if (loc.iso && loc.iso !== loc.code) {
276
- result.link.push({
277
- rel: 'alternate',
278
- href: alternateUrl,
279
- hreflang: loc.iso,
280
- })
281
- }
282
285
  }
283
286
 
284
287
  // x-default hreflang — points to the default locale's URL.