@stacksjs/i18n 0.70.73 → 0.70.75

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.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- var GG=import.meta.require;function JG(G,J){return KG(J)(G)}function KG(G){let J=G.split("-")[0].toLowerCase(),K=j[J];if(K)return K;return()=>"other"}function M(G,J="|"){let K=G.split(J).map((U)=>U.trim()),Z=new Map;if(K.length===1)Z.set("other",K[0]);else if(K.length===2)Z.set("one",K[0]),Z.set("other",K[1]);else if(K.length===3)Z.set("zero",K[0]),Z.set("one",K[1]),Z.set("other",K[2]);else if(K.length>=4){let U=["zero","one","two","few","many","other"];for(let _=0;_<K.length&&_<U.length;_++)Z.set(U[_],K[_])}return Z}function S(G,J,K){let Z=JG(J,K);if(G.has(Z))return G.get(Z);if(G.has("other"))return G.get("other");let U=G.values().next();return U.done?"":U.value}var j={en:(G)=>G===1?"one":"other",de:(G)=>G===1?"one":"other",nl:(G)=>G===1?"one":"other",es:(G)=>G===1?"one":"other",it:(G)=>G===1?"one":"other",pt:(G)=>G===1?"one":"other",sv:(G)=>G===1?"one":"other",da:(G)=>G===1?"one":"other",no:(G)=>G===1?"one":"other",fi:(G)=>G===1?"one":"other",el:(G)=>G===1?"one":"other",he:(G)=>G===1?"one":"other",hu:(G)=>G===1?"one":"other",tr:(G)=>G===1?"one":"other",zh:()=>"other",ja:()=>"other",ko:()=>"other",vi:()=>"other",th:()=>"other",id:()=>"other",ms:()=>"other",fr:(G)=>G===0||G===1?"one":"other",ru:(G)=>{let J=G%10,K=G%100;if(J===1&&K!==11)return"one";if(J>=2&&J<=4&&(K<12||K>14))return"few";if(J===0||J>=5&&J<=9||K>=11&&K<=14)return"many";return"other"},uk:(G)=>{let J=G%10,K=G%100;if(J===1&&K!==11)return"one";if(J>=2&&J<=4&&(K<12||K>14))return"few";if(J===0||J>=5&&J<=9||K>=11&&K<=14)return"many";return"other"},pl:(G)=>{let J=G%10,K=G%100;if(G===1)return"one";if(J>=2&&J<=4&&(K<12||K>14))return"few";if(J===0||J===1||J>=5&&J<=9||K>=12&&K<=14)return"many";return"other"},cs:(G)=>{if(G===1)return"one";if(G>=2&&G<=4)return"few";return"other"},sk:(G)=>{if(G===1)return"one";if(G>=2&&G<=4)return"few";return"other"},ar:(G)=>{if(G===0)return"zero";if(G===1)return"one";if(G===2)return"two";let J=G%100;if(J>=3&&J<=10)return"few";if(J>=11&&J<=99)return"many";return"other"},cy:(G)=>{if(G===0)return"zero";if(G===1)return"one";if(G===2)return"two";if(G===3)return"few";if(G===6)return"many";return"other"},sl:(G)=>{let J=G%100;if(J===1)return"one";if(J===2)return"two";if(J===3||J===4)return"few";return"other"},ga:(G)=>{if(G===1)return"one";if(G===2)return"two";if(G>=3&&G<=6)return"few";if(G>=7&&G<=10)return"many";return"other"},lt:(G)=>{let J=G%10,K=G%100;if(J===1&&(K<11||K>19))return"one";if(J>=2&&J<=9&&(K<11||K>19))return"few";return"other"},lv:(G)=>{let J=G%10,K=G%100;if(G===0)return"zero";if(J===1&&K!==11)return"one";return"other"},ro:(G)=>{if(G===1)return"one";let J=G%100;if(G===0||J>=1&&J<=19)return"few";return"other"},hi:(G)=>G===0||G===1?"one":"other"};function bG(){return Object.keys(j)}function vG(G){return G.split("-")[0].toLowerCase()in j}function hG(G,J){let K=G.split("-")[0].toLowerCase();j[K]=J}var g={locale:"en",fallbackLocale:"en",availableLocales:["en"],messages:{},warnMissing:!0,escapeValues:!1,keySeparator:".",pluralSeparator:"|"},H="en",R="en",N={},z={...g};class D{_locale;_fallbackLocale;_messages;_config;constructor(G={}){this._config={...g,...G},this._locale=this._config.locale,this._fallbackLocale=this._config.fallbackLocale,this._messages=this._config.messages||{}}get locale(){return this._locale}set locale(G){this._locale=G}get fallbackLocale(){return this._fallbackLocale}get availableLocales(){return Object.keys(this._messages)}t=(G,J,K)=>{return this.translate(G,J,K)};tc=(G,J,K,Z)=>{return this.translatePlural(G,J,K,Z)};te=(G,J)=>{return this.hasTranslation(G,J)};tm=(G,J)=>{return this.getMessage(G,J)};setLocale=(G)=>{this._locale=G};addTranslations=(G,J)=>{if(!this._messages[G])this._messages[G]={};this._messages[G]=P(this._messages[G],J)};d=(G,J)=>{let K=typeof G==="number"?new Date(G):G,Z=this._config.dateTimeFormats?.[this._locale]?.[J||"short"]||{};return new Intl.DateTimeFormat(this._locale,Z).format(K)};n=(G,J)=>{let K=this._config.numberFormats?.[this._locale]?.[J||"decimal"]||{};return new Intl.NumberFormat(this._locale,K).format(G)};translate(G,J,K){let Z=K||this._locale,U=this.getMessage(G,Z);if(U===void 0){let _=Z.indexOf("-");if(_>0){let B=Z.slice(0,_);if(B!==this._fallbackLocale)U=this.getMessage(G,B)}}if(U===void 0&&Z!==this._fallbackLocale)U=this.getMessage(G,this._fallbackLocale);if(U===void 0){if(this._config.warnMissing)console.warn(`[i18n] Missing translation: "${G}" for locale "${Z}"`);if(this._config.missingHandler){let _=this._config.missingHandler(Z,G);if(_!==void 0)return _}return G}if(typeof U!=="string")return G;return this.interpolate(U,J)}translatePlural(G,J,K,Z){let U=Z||this._locale,_=this.getMessage(G,U);if(_===void 0&&U!==this._fallbackLocale)_=this.getMessage(G,this._fallbackLocale);if(_===void 0||typeof _!=="string"){if(this._config.warnMissing)console.warn(`[i18n] Missing plural translation: "${G}" for locale "${U}"`);return G}let B=M(_,this._config.pluralSeparator),$=S(B,J,U),X={count:J,n:J,...K};return this.interpolate($,X)}hasTranslation(G,J){let K=J||this._locale;return this.getMessage(G,K)!==void 0}getMessage(G,J){let K=this._messages[J];if(!K)return;let Z=G.split(this._config.keySeparator||"."),U=K;for(let _ of Z){if(U===void 0||typeof U==="string")return;U=U[_]}return U}interpolate(G,J){if(!J)return G;return G.replace(/\{(\w+)\}/g,(K,Z)=>{let U=J[Z];if(U===void 0||U===null)return K;let _=String(U);return this._config.escapeValues?XG(_):_})}}function x(G){H=G}function E(){return H}function yG(G){R=G}function V(){return Object.keys(N)}function q(G,J){if(!N[G])N[G]={};N[G]=P(N[G],J)}function F(G){for(let[J,K]of Object.entries(G))q(J,K)}function p(G,J){return T(G,J||H)!==void 0}function T(G,J){let K=N[J];if(!K)return;let Z=G.split(z.keySeparator||"."),U=K;for(let _ of Z){if(U===void 0||typeof U==="string")return;U=U[_]}return U}function m(G,J){if(!J)return G;return G.replace(/\{(\w+)\}/g,(K,Z)=>{let U=J[Z];if(U===void 0||U===null)return K;return String(U)})}function d(G,J,K){let Z=K||H,U=T(G,Z);if(U===void 0&&Z!==R)U=T(G,R);if(U===void 0){if(z.warnMissing)console.warn(`[i18n] Missing translation: "${G}" for locale "${Z}"`);return G}if(typeof U!=="string")return G;return m(U,J)}var UG=d;function ZG(G,J,K,Z){let U=Z||H,_=T(G,U);if(_===void 0&&U!==R)_=T(G,R);if(_===void 0||typeof _!=="string")return G;let B=M(_,z.pluralSeparator),$=S(B,J,U),X={count:J,n:J,...K};return m($,X)}function _G(G,J){return p(G,J)}function $G(G,J){return T(G,J||H)}function u(G={}){return new D(G)}function BG(G={}){return u(G)}function i(G){if(z={...z,...G},G.locale)H=G.locale;if(G.fallbackLocale)R=G.fallbackLocale;if(G.messages)F(G.messages)}function P(G,J){let K={...G};for(let Z of Object.keys(J)){let U=J[Z],_=K[Z];if(typeof U==="object"&&U!==null&&typeof _==="object"&&_!==null)K[Z]=P(_,U);else K[Z]=U}return K}function XG(G){let J={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"};return G.replace(/[&<>"']/g,(K)=>J[K]||K)}function EG(G,J="medium",K){let Z=K||E(),U=O(G),_=typeof J==="string"?{dateStyle:J}:J;return new Intl.DateTimeFormat(Z,_).format(U)}function QG(G,J="short",K){let Z=K||E(),U=O(G),_=typeof J==="string"?{timeStyle:J}:J;return new Intl.DateTimeFormat(Z,_).format(U)}function WG(G,J="medium",K="short",Z){let U=Z||E(),_=O(G);return new Intl.DateTimeFormat(U,{dateStyle:J,timeStyle:K}).format(_)}function YG(G,J=new Date,K="long",Z){let U=Z||E(),_=O(G),B=O(J),$=_.getTime()-B.getTime(),X=Math.round($/1000),Q=Math.round(X/60),W=Math.round(Q/60),A=Math.round(W/24),Y=Math.round(A/7),I=Math.round(A/30),C=Math.round(A/365),w=new Intl.RelativeTimeFormat(U,{style:K});if(Math.abs(X)<60)return w.format(X,"second");if(Math.abs(Q)<60)return w.format(Q,"minute");if(Math.abs(W)<24)return w.format(W,"hour");if(Math.abs(A)<7)return w.format(A,"day");if(Math.abs(Y)<4)return w.format(Y,"week");if(Math.abs(I)<12)return w.format(I,"month");return w.format(C,"year")}function AG(G,J={},K){let Z=K||E(),U={...J};if(J.compact)U.notation="compact",U.compactDisplay="short";if(J.precision!==void 0)U.minimumFractionDigits=J.precision,U.maximumFractionDigits=J.precision;return new Intl.NumberFormat(Z,U).format(G)}function IG(G,J="USD",K={},Z){let U=Z||E();return new Intl.NumberFormat(U,{style:"currency",currency:J,...K}).format(G)}function wG(G,J={},K){let Z=K||E();return new Intl.NumberFormat(Z,{style:"percent",...J}).format(G)}function mG(G,J,K="short",Z){let U=Z||E();return new Intl.NumberFormat(U,{style:"unit",unit:J,unitDisplay:K}).format(G)}function dG(G,J=2,K){let Z=K||E();if(G===0)return"0 B";let U=1024,_=["B","KB","MB","GB","TB","PB","EB","ZB","YB"],B=Math.floor(Math.log(G)/Math.log(U)),$=G/Math.pow(U,B);return`${new Intl.NumberFormat(Z,{minimumFractionDigits:0,maximumFractionDigits:J}).format($)} ${_[B]}`}function HG(G,J="conjunction",K="long",Z){let U=Z||E();return new Intl.ListFormat(U,{type:J,style:K}).format(G)}function c(G,J){let K=J||E();return new Intl.PluralRules(K).select(G)}function uG(G,J,K){let Z=c(G,K);return J[Z]??J.other}function b(G,J="language",K="long",Z){let U=Z||E();try{return new Intl.DisplayNames(U,{type:J,style:K}).of(G)}catch{return}}function iG(G,J){return b(G,"language","long",J)}function cG(G,J){return b(G,"region","long",J)}function nG(G,J){return b(G,"currency","long",J)}function rG(G,J,K={},Z){let U=Z||E();return new Intl.Collator(U,K).compare(G,J)}function sG(G,J={},K){let Z=K||E(),U=new Intl.Collator(Z,J);return[...G].sort((_,B)=>U.compare(_,B))}function O(G){if(G instanceof Date)return G;if(typeof G==="number")return new Date(G);return new Date(G)}function NG(G){let K=(G||E()).split("-")[0].toLowerCase();return["ar","he","fa","ur","yi","ps","sd","ug","ku","ckb"].includes(K)?"rtl":"ltr"}function tG(G){return NG(G)==="rtl"}import{existsSync as RG,readdirSync as n}from"fs";import{readFile as TG}from"fs/promises";import{basename as r,extname as v,join as h}from"path";import{log as zG}from"@stacksjs/logging";async function L(G){let{directory:J,extensions:K=[".json",".yaml",".yml",".ts",".js"],recursive:Z=!1,namespaceSeparator:U="."}=G,_={};if(!RG(J))return zG.warn(`[i18n] Translation directory not found: ${J}`),_;return await s(J,_,K,Z,U,""),F(_),_}async function s(G,J,K,Z,U,_){let B=n(G,{withFileTypes:!0});for(let $ of B){let X=h(G,$.name);if($.isDirectory()){let Q=$.name;if(jG(Q))await FG(X,Q,J,K);else if(Z){let W=_?`${_}${U}${$.name}`:$.name;await s(X,J,K,Z,U,W)}}else if($.isFile()){let Q=v($.name);if(K.includes(Q)){let W=r($.name,Q);if(!J[W])J[W]={};let A=await k(X);if(_)VG(J[W],_,A,U);else J[W]=f(J[W],A)}}}}async function FG(G,J,K,Z){if(!K[J])K[J]={};let U=n(G,{withFileTypes:!0});for(let _ of U){if(!_.isFile())continue;let B=v(_.name);if(!Z.includes(B))continue;let $=h(G,_.name),X=r(_.name,B),Q=await k($);if(X===J)K[J]=f(K[J],Q);else K[J][X]=Q}}async function k(G){let J=v(G).toLowerCase(),K=await TG(G,"utf8");switch(J){case".json":try{return JSON.parse(K)}catch{throw Error(`Invalid JSON in translation file: ${G}`)}case".yaml":case".yml":return CG(K);case".ts":case".js":{let Z=await import(G);return Z.default||Z}default:throw Error(`Unsupported file type: ${J}`)}}async function OG(G,J){let K=await k(J);return q(G,K),K}function CG(G){try{let J=Bun.YAML.parse(G);if(!J||typeof J!=="object"||Array.isArray(J))throw Error("YAML content must be an object at the root level");return J}catch(J){let K=J instanceof Error?J.message:"Unknown YAML parse error";throw Error(`Invalid YAML translation file: ${K}`)}}function jG(G){return/^[a-z]{2}(?:-[A-Z]{2})?(?:-[A-Za-z]+)?$/i.test(G)}function VG(G,J,K,Z){let U=J.split(Z),_=G;for(let $=0;$<U.length-1;$++){let X=U[$];if(!_[X]||typeof _[X]==="string")_[X]={};_=_[X]}let B=U[U.length-1];_[B]=K}function f(G,J){let K={...G};for(let Z of Object.keys(J)){let U=J[Z],_=K[Z];if(typeof U==="object"&&U!==null&&typeof _==="object"&&_!==null)K[Z]=f(_,U);else K[Z]=U}return K}function KJ(G,J={}){return{load:()=>L({directory:G,...J}),loadLocale:(K,Z)=>OG(K,h(G,Z))}}async function UJ(G){let{loadTranslations:J}=await import("@stacksjs/ts-i18n"),K=await J(G);return F(K),K}var _J={en:{code:"en",name:"English",nativeName:"English",direction:"ltr"},"en-US":{code:"en-US",name:"English (US)",nativeName:"English (US)",direction:"ltr",region:"US"},"en-GB":{code:"en-GB",name:"English (UK)",nativeName:"English (UK)",direction:"ltr",region:"GB"},es:{code:"es",name:"Spanish",nativeName:"Espa\xF1ol",direction:"ltr"},fr:{code:"fr",name:"French",nativeName:"Fran\xE7ais",direction:"ltr"},de:{code:"de",name:"German",nativeName:"Deutsch",direction:"ltr"},it:{code:"it",name:"Italian",nativeName:"Italiano",direction:"ltr"},pt:{code:"pt",name:"Portuguese",nativeName:"Portugu\xEAs",direction:"ltr"},"pt-BR":{code:"pt-BR",name:"Portuguese (Brazil)",nativeName:"Portugu\xEAs (Brasil)",direction:"ltr",region:"BR"},zh:{code:"zh",name:"Chinese",nativeName:"\u4E2D\u6587",direction:"ltr"},"zh-CN":{code:"zh-CN",name:"Chinese (Simplified)",nativeName:"\u7B80\u4F53\u4E2D\u6587",direction:"ltr",region:"CN"},"zh-TW":{code:"zh-TW",name:"Chinese (Traditional)",nativeName:"\u7E41\u9AD4\u4E2D\u6587",direction:"ltr",region:"TW"},ja:{code:"ja",name:"Japanese",nativeName:"\u65E5\u672C\u8A9E",direction:"ltr"},ko:{code:"ko",name:"Korean",nativeName:"\uD55C\uAD6D\uC5B4",direction:"ltr"},ar:{code:"ar",name:"Arabic",nativeName:"\u0627\u0644\u0639\u0631\u0628\u064A\u0629",direction:"rtl"},he:{code:"he",name:"Hebrew",nativeName:"\u05E2\u05D1\u05E8\u05D9\u05EA",direction:"rtl"},ru:{code:"ru",name:"Russian",nativeName:"\u0420\u0443\u0441\u0441\u043A\u0438\u0439",direction:"ltr"},nl:{code:"nl",name:"Dutch",nativeName:"Nederlands",direction:"ltr"},pl:{code:"pl",name:"Polish",nativeName:"Polski",direction:"ltr"},tr:{code:"tr",name:"Turkish",nativeName:"T\xFCrk\xE7e",direction:"ltr"},vi:{code:"vi",name:"Vietnamese",nativeName:"Ti\u1EBFng Vi\u1EC7t",direction:"ltr"},th:{code:"th",name:"Thai",nativeName:"\u0E44\u0E17\u0E22",direction:"ltr"},hi:{code:"hi",name:"Hindi",nativeName:"\u0939\u093F\u0928\u094D\u0926\u0940",direction:"ltr"}};import{loadTranslations as FJ,generateTypes as OJ,generateTypesFromModule as CJ,generateSampleConfig as jJ,writeOutputs as VJ,createTranslator as qJ}from"@stacksjs/ts-i18n";import{existsSync as qG}from"fs";import{config as y}from"@stacksjs/config";import{projectPath as MG}from"@stacksjs/path";var t=!1;async function o(){if(t)return;let G=MG("locales");if(qG(G))await L({directory:G,extensions:[".yaml",".yml",".json"]});let J=y.app,K=J?.locale??"en",Z=J?.fallbackLocale??K,U=V();i({locale:K,fallbackLocale:Z,availableLocales:U.length>0?U:[K],warnMissing:(J?.env??process.env.APP_ENV)!=="production"}),t=!0}function l(G){let K=y.app?.locale??"en",Z=new Set(V());if(!Z.size)Z.add(K);let U=(Y)=>{let I=Y.trim().toLowerCase();if(Z.has(I))return I;let C=I.split("-")[0];if(Z.has(C))return C;return K};if(!G)return K;let _=new URL(G.url),B=_.pathname,$=G.headers.get("x-stx-locale");if($)return U($);for(let Y of Z){if(Y===K)continue;if(B===`/${Y}`||B===`/${Y}/`)return U(Y);if(B.startsWith(`/${Y}/`))return U(Y)}let X=_.searchParams.get("locale");if(X)return U(X);let W=(G.headers.get("cookie")??"").match(/(?:^|;\s*)locale=([a-z]{2}(?:-[a-z]{2})?)(?:;|$)/i);if(W?.[1])return U(W[1]);let A=G.headers.get("accept-language")??"";for(let Y of A.split(",")){let I=Y.split(";")[0]?.trim();if(I)return U(I)}return K}async function SG(G){await o();let J=l(G);return x(J),J}function DG(G,J){let K=G.trim().toLowerCase();if(J.locales.includes(K))return K;let Z=K.split("-")[0];if(J.locales.includes(Z))return Z;return null}function a(G,J){for(let K of J){if(G===`/${K}`||G===`/${K}/`)return{locale:K,path:"/"};if(G.startsWith(`/${K}/`))return{locale:K,path:G.slice(K.length+1)}}return{locale:null,path:G}}function e(G,J,K){if(J===K)return G;if(G==="/")return`/${J}/`;if(G==="/404.html")return`/${J}/404.html`;return`/${J}${G}`}function xG(G,J,K){let Z=DG(J,K);if(!Z)return Response.redirect(new URL("/",G.url).href,302);let U=G.headers.get("referer"),_="/";if(U)try{let Q=new URL(U);if(!Q.pathname.startsWith("/locale/"))_=a(Q.pathname,K.locales).path||"/"}catch{_="/"}let B=e(_,Z,K.defaultLocale),$=new URL(B,G.url),X=Response.redirect($.href,302);return X.headers.append("Set-Cookie",`locale=${Z}; Path=/; Max-Age=31536000; SameSite=Lax`),X}export{VJ as writeI18nOutputs,BG as useI18n,UG as trans,$G as tm,_G as te,ZG as tc,d as t,a as stripLocalePrefix,sG as sortStrings,x as setLocale,yG as setFallbackLocale,S as selectPluralForm,uG as selectPlural,l as resolveRequestLocale,M as parsePluralForms,e as localizePath,FJ as loadTranslationsFromDisk,F as loadTranslations,OG as loadLocale,UJ as loadFromTsI18n,L as loadFromDirectory,k as loadFile,tG as isRTL,p as hasTranslation,vG as hasPluralRule,NG as getTextDirection,bG as getSupportedPluralLocales,cG as getRegionName,KG as getPluralRule,JG as getPluralCategory,E as getLocale,iG as getLanguageName,b as getDisplayName,nG as getCurrencyName,V as getAvailableLocales,CJ as generateI18nTypesFromModule,OJ as generateI18nTypes,jJ as generateI18nSampleConfig,mG as formatUnit,QG as formatTime,YG as formatRelativeTime,c as formatPlural,wG as formatPercent,AG as formatNumber,HG as formatList,WG as formatDateTime,EG as formatDate,IG as formatCurrency,dG as formatBytes,o as ensureLocalesLoaded,qJ as createSimpleTranslator,xG as createLocaleSwitchResponse,KJ as createLoader,u as createI18n,i as configure,rG as compareStrings,SG as applyRequestLocale,q as addTranslations,hG as addPluralRule,_J as LOCALE_INFO,D as I18n};
2
+ var GG=import.meta.require;function JG(G,J){return KG(J)(G)}function KG(G){let J=G.split("-")[0]?.toLowerCase()??G.toLowerCase(),K=j[J];if(K)return K;return()=>"other"}function M(G,J="|"){let K=G.split(J).map((U)=>U.trim()),Z=new Map;if(K.length===1)Z.set("other",K[0]??"");else if(K.length===2)Z.set("one",K[0]??""),Z.set("other",K[1]??"");else if(K.length===3)Z.set("zero",K[0]??""),Z.set("one",K[1]??""),Z.set("other",K[2]??"");else if(K.length>=4){let U=["zero","one","two","few","many","other"];for(let _=0;_<K.length&&_<U.length;_++){let $=U[_],B=K[_];if($!==void 0&&B!==void 0)Z.set($,B)}}return Z}function S(G,J,K){let Z=JG(J,K);if(G.has(Z))return G.get(Z);if(G.has("other"))return G.get("other");let U=G.values().next();return U.done?"":U.value}var j={en:(G)=>G===1?"one":"other",de:(G)=>G===1?"one":"other",nl:(G)=>G===1?"one":"other",es:(G)=>G===1?"one":"other",it:(G)=>G===1?"one":"other",pt:(G)=>G===1?"one":"other",sv:(G)=>G===1?"one":"other",da:(G)=>G===1?"one":"other",no:(G)=>G===1?"one":"other",fi:(G)=>G===1?"one":"other",el:(G)=>G===1?"one":"other",he:(G)=>G===1?"one":"other",hu:(G)=>G===1?"one":"other",tr:(G)=>G===1?"one":"other",zh:()=>"other",ja:()=>"other",ko:()=>"other",vi:()=>"other",th:()=>"other",id:()=>"other",ms:()=>"other",fr:(G)=>G===0||G===1?"one":"other",ru:(G)=>{let J=G%10,K=G%100;if(J===1&&K!==11)return"one";if(J>=2&&J<=4&&(K<12||K>14))return"few";if(J===0||J>=5&&J<=9||K>=11&&K<=14)return"many";return"other"},uk:(G)=>{let J=G%10,K=G%100;if(J===1&&K!==11)return"one";if(J>=2&&J<=4&&(K<12||K>14))return"few";if(J===0||J>=5&&J<=9||K>=11&&K<=14)return"many";return"other"},pl:(G)=>{let J=G%10,K=G%100;if(G===1)return"one";if(J>=2&&J<=4&&(K<12||K>14))return"few";if(J===0||J===1||J>=5&&J<=9||K>=12&&K<=14)return"many";return"other"},cs:(G)=>{if(G===1)return"one";if(G>=2&&G<=4)return"few";return"other"},sk:(G)=>{if(G===1)return"one";if(G>=2&&G<=4)return"few";return"other"},ar:(G)=>{if(G===0)return"zero";if(G===1)return"one";if(G===2)return"two";let J=G%100;if(J>=3&&J<=10)return"few";if(J>=11&&J<=99)return"many";return"other"},cy:(G)=>{if(G===0)return"zero";if(G===1)return"one";if(G===2)return"two";if(G===3)return"few";if(G===6)return"many";return"other"},sl:(G)=>{let J=G%100;if(J===1)return"one";if(J===2)return"two";if(J===3||J===4)return"few";return"other"},ga:(G)=>{if(G===1)return"one";if(G===2)return"two";if(G>=3&&G<=6)return"few";if(G>=7&&G<=10)return"many";return"other"},lt:(G)=>{let J=G%10,K=G%100;if(J===1&&(K<11||K>19))return"one";if(J>=2&&J<=9&&(K<11||K>19))return"few";return"other"},lv:(G)=>{let J=G%10,K=G%100;if(G===0)return"zero";if(J===1&&K!==11)return"one";return"other"},ro:(G)=>{if(G===1)return"one";let J=G%100;if(G===0||J>=1&&J<=19)return"few";return"other"},hi:(G)=>G===0||G===1?"one":"other"};function bG(){return Object.keys(j)}function vG(G){return(G.split("-")[0]?.toLowerCase()??G.toLowerCase())in j}function hG(G,J){let K=G.split("-")[0]?.toLowerCase()??G.toLowerCase();j[K]=J}var g={locale:"en",fallbackLocale:"en",availableLocales:["en"],messages:{},warnMissing:!0,escapeValues:!1,keySeparator:".",pluralSeparator:"|"},H="en",R="en",N={},F={...g};class D{_locale;_fallbackLocale;_messages;_config;constructor(G={}){this._config={...g,...G},this._locale=this._config.locale,this._fallbackLocale=this._config.fallbackLocale,this._messages=this._config.messages||{}}get locale(){return this._locale}set locale(G){this._locale=G}get fallbackLocale(){return this._fallbackLocale}get availableLocales(){return Object.keys(this._messages)}t=(G,J,K)=>{return this.translate(G,J,K)};tc=(G,J,K,Z)=>{return this.translatePlural(G,J,K,Z)};te=(G,J)=>{return this.hasTranslation(G,J)};tm=(G,J)=>{return this.getMessage(G,J??this.locale)};setLocale=(G)=>{this._locale=G};addTranslations=(G,J)=>{if(!this._messages[G])this._messages[G]={};this._messages[G]=P(this._messages[G],J)};d=(G,J)=>{let K=typeof G==="number"?new Date(G):G,Z=this._config.dateTimeFormats?.[this._locale]?.[J||"short"]||{};return new Intl.DateTimeFormat(this._locale,Z).format(K)};n=(G,J)=>{let K=this._config.numberFormats?.[this._locale]?.[J||"decimal"]||{};return new Intl.NumberFormat(this._locale,K).format(G)};translate(G,J,K){let Z=K||this._locale,U=this.getMessage(G,Z);if(U===void 0){let _=Z.indexOf("-");if(_>0){let $=Z.slice(0,_);if($!==this._fallbackLocale)U=this.getMessage(G,$)}}if(U===void 0&&Z!==this._fallbackLocale)U=this.getMessage(G,this._fallbackLocale);if(U===void 0){if(this._config.warnMissing)console.warn(`[i18n] Missing translation: "${G}" for locale "${Z}"`);if(this._config.missingHandler){let _=this._config.missingHandler(Z,G);if(_!==void 0)return _}return G}if(typeof U!=="string")return G;return this.interpolate(U,J)}translatePlural(G,J,K,Z){let U=Z||this._locale,_=this.getMessage(G,U);if(_===void 0&&U!==this._fallbackLocale)_=this.getMessage(G,this._fallbackLocale);if(_===void 0||typeof _!=="string"){if(this._config.warnMissing)console.warn(`[i18n] Missing plural translation: "${G}" for locale "${U}"`);return G}let $=M(_,this._config.pluralSeparator),B=S($,J,U),X={count:J,n:J,...K};return this.interpolate(B,X)}hasTranslation(G,J){let K=J||this._locale;return this.getMessage(G,K)!==void 0}getMessage(G,J){let K=this._messages[J];if(!K)return;let Z=G.split(this._config.keySeparator||"."),U=K;for(let _ of Z){if(U===void 0||typeof U==="string")return;U=U[_]}return U}interpolate(G,J){if(!J)return G;return G.replace(/\{(\w+)\}/g,(K,Z)=>{let U=J[Z];if(U===void 0||U===null)return K;let _=String(U);return this._config.escapeValues?XG(_):_})}}function x(G){H=G}function E(){return H}function yG(G){R=G}function V(){return Object.keys(N)}function q(G,J){if(!N[G])N[G]={};N[G]=P(N[G],J)}function O(G){for(let[J,K]of Object.entries(G))q(J,K)}function p(G,J){return T(G,J||H)!==void 0}function T(G,J){let K=N[J];if(!K)return;let Z=G.split(F.keySeparator||"."),U=K;for(let _ of Z){if(U===void 0||typeof U==="string")return;U=U[_]}return U}function m(G,J){if(!J)return G;return G.replace(/\{(\w+)\}/g,(K,Z)=>{let U=J[Z];if(U===void 0||U===null)return K;return String(U)})}function d(G,J,K){let Z=K||H,U=T(G,Z);if(U===void 0&&Z!==R)U=T(G,R);if(U===void 0){if(F.warnMissing)console.warn(`[i18n] Missing translation: "${G}" for locale "${Z}"`);return G}if(typeof U!=="string")return G;return m(U,J)}var UG=d;function ZG(G,J,K,Z){let U=Z||H,_=T(G,U);if(_===void 0&&U!==R)_=T(G,R);if(_===void 0||typeof _!=="string")return G;let $=M(_,F.pluralSeparator),B=S($,J,U),X={count:J,n:J,...K};return m(B,X)}function _G(G,J){return p(G,J)}function $G(G,J){return T(G,J||H)}function u(G={}){return new D(G)}function BG(G={}){return u(G)}function i(G){if(F={...F,...G},G.locale)H=G.locale;if(G.fallbackLocale)R=G.fallbackLocale;if(G.messages)O(G.messages)}function P(G,J){let K={...G};for(let Z of Object.keys(J)){let U=J[Z],_=K[Z];if(typeof U==="object"&&U!==null&&typeof _==="object"&&_!==null)K[Z]=P(_,U);else if(U!==void 0)K[Z]=U}return K}function XG(G){let J={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"};return G.replace(/[&<>"']/g,(K)=>J[K]||K)}function EG(G,J="medium",K){let Z=K||E(),U=C(G),_=typeof J==="string"?{dateStyle:J}:J;return new Intl.DateTimeFormat(Z,_).format(U)}function QG(G,J="short",K){let Z=K||E(),U=C(G),_=typeof J==="string"?{timeStyle:J}:J;return new Intl.DateTimeFormat(Z,_).format(U)}function WG(G,J="medium",K="short",Z){let U=Z||E(),_=C(G);return new Intl.DateTimeFormat(U,{dateStyle:J,timeStyle:K}).format(_)}function YG(G,J=new Date,K="long",Z){let U=Z||E(),_=C(G),$=C(J),B=_.getTime()-$.getTime(),X=Math.round(B/1000),Q=Math.round(X/60),W=Math.round(Q/60),A=Math.round(W/24),Y=Math.round(A/7),I=Math.round(A/30),z=Math.round(A/365),w=new Intl.RelativeTimeFormat(U,{style:K});if(Math.abs(X)<60)return w.format(X,"second");if(Math.abs(Q)<60)return w.format(Q,"minute");if(Math.abs(W)<24)return w.format(W,"hour");if(Math.abs(A)<7)return w.format(A,"day");if(Math.abs(Y)<4)return w.format(Y,"week");if(Math.abs(I)<12)return w.format(I,"month");return w.format(z,"year")}function AG(G,J={},K){let Z=K||E(),U={...J};if(J.compact)U.notation="compact",U.compactDisplay="short";if(J.precision!==void 0)U.minimumFractionDigits=J.precision,U.maximumFractionDigits=J.precision;return new Intl.NumberFormat(Z,U).format(G)}function IG(G,J="USD",K={},Z){let U=Z||E();return new Intl.NumberFormat(U,{style:"currency",currency:J,...K}).format(G)}function wG(G,J={},K){let Z=K||E();return new Intl.NumberFormat(Z,{style:"percent",...J}).format(G)}function mG(G,J,K="short",Z){let U=Z||E();return new Intl.NumberFormat(U,{style:"unit",unit:J,unitDisplay:K}).format(G)}function dG(G,J=2,K){let Z=K||E();if(G===0)return"0 B";let U=1024,_=["B","KB","MB","GB","TB","PB","EB","ZB","YB"],$=Math.floor(Math.log(G)/Math.log(U)),B=G/Math.pow(U,$);return`${new Intl.NumberFormat(Z,{minimumFractionDigits:0,maximumFractionDigits:J}).format(B)} ${_[$]}`}function HG(G,J="conjunction",K="long",Z){let U=Z||E();return new Intl.ListFormat(U,{type:J,style:K}).format(G)}function n(G,J){let K=J||E();return new Intl.PluralRules(K).select(G)}function uG(G,J,K){let Z=n(G,K);return J[Z]??J.other}function b(G,J="language",K="long",Z){let U=Z||E();try{return new Intl.DisplayNames(U,{type:J,style:K}).of(G)}catch{return}}function iG(G,J){return b(G,"language","long",J)}function nG(G,J){return b(G,"region","long",J)}function cG(G,J){return b(G,"currency","long",J)}function rG(G,J,K={},Z){let U=Z||E();return new Intl.Collator(U,K).compare(G,J)}function sG(G,J={},K){let Z=K||E(),U=new Intl.Collator(Z,J);return[...G].sort((_,$)=>U.compare(_,$))}function C(G){if(G instanceof Date)return G;if(typeof G==="number")return new Date(G);return new Date(G)}function NG(G){let J=G||E(),K=(J.split("-")[0]??J).toLowerCase();return["ar","he","fa","ur","yi","ps","sd","ug","ku","ckb"].includes(K)?"rtl":"ltr"}function tG(G){return NG(G)==="rtl"}import{existsSync as RG,readdirSync as c}from"fs";import{readFile as TG}from"fs/promises";import{basename as r,extname as v,join as h}from"path";import{log as zG}from"@stacksjs/logging";async function L(G){let{directory:J,extensions:K=[".json",".yaml",".yml",".ts",".js"],recursive:Z=!1,namespaceSeparator:U="."}=G,_={};if(!RG(J))return zG.warn(`[i18n] Translation directory not found: ${J}`),_;return await s(J,_,K,Z,U,""),O(_),_}async function s(G,J,K,Z,U,_){let $=c(G,{withFileTypes:!0});for(let B of $){let X=h(G,B.name);if(B.isDirectory()){let Q=B.name;if(jG(Q))await FG(X,Q,J,K);else if(Z){let W=_?`${_}${U}${B.name}`:B.name;await s(X,J,K,Z,U,W)}}else if(B.isFile()){let Q=v(B.name);if(K.includes(Q)){let W=r(B.name,Q);if(!J[W])J[W]={};let A=await k(X);if(_)VG(J[W],_,A,U);else J[W]=f(J[W],A)}}}}async function FG(G,J,K,Z){if(!K[J])K[J]={};let U=c(G,{withFileTypes:!0});for(let _ of U){if(!_.isFile())continue;let $=v(_.name);if(!Z.includes($))continue;let B=h(G,_.name),X=r(_.name,$),Q=await k(B);if(X===J)K[J]=f(K[J],Q);else K[J][X]=Q}}async function k(G){let J=v(G).toLowerCase(),K=await TG(G,"utf8");switch(J){case".json":try{return JSON.parse(K)}catch{throw Error(`Invalid JSON in translation file: ${G}`)}case".yaml":case".yml":return CG(K);case".ts":case".js":{let Z=await import(G);return Z.default||Z}default:throw Error(`Unsupported file type: ${J}`)}}async function OG(G,J){let K=await k(J);return q(G,K),K}function CG(G){try{let J=Bun.YAML.parse(G);if(!J||typeof J!=="object"||Array.isArray(J))throw Error("YAML content must be an object at the root level");return J}catch(J){let K=J instanceof Error?J.message:"Unknown YAML parse error";throw Error(`Invalid YAML translation file: ${K}`)}}function jG(G){return/^[a-z]{2}(?:-[A-Z]{2})?(?:-[A-Za-z]+)?$/i.test(G)}function VG(G,J,K,Z){let U=J.split(Z),_=G;for(let B=0;B<U.length-1;B++){let X=U[B];if(!X)continue;if(!_[X]||typeof _[X]==="string")_[X]={};_=_[X]}let $=U[U.length-1];if($)_[$]=K}function f(G,J){let K={...G};for(let Z of Object.keys(J)){let U=J[Z];if(U===void 0)continue;let _=K[Z];if(typeof U==="object"&&U!==null&&typeof _==="object"&&_!==null)K[Z]=f(_,U);else K[Z]=U}return K}function KJ(G,J={}){return{load:()=>L({directory:G,...J}),loadLocale:(K,Z)=>OG(K,h(G,Z))}}async function UJ(G){let{loadTranslations:J}=await import("@stacksjs/ts-i18n"),K=await J(G);return O(K),K}var _J={en:{code:"en",name:"English",nativeName:"English",direction:"ltr"},"en-US":{code:"en-US",name:"English (US)",nativeName:"English (US)",direction:"ltr",region:"US"},"en-GB":{code:"en-GB",name:"English (UK)",nativeName:"English (UK)",direction:"ltr",region:"GB"},es:{code:"es",name:"Spanish",nativeName:"Espa\xF1ol",direction:"ltr"},fr:{code:"fr",name:"French",nativeName:"Fran\xE7ais",direction:"ltr"},de:{code:"de",name:"German",nativeName:"Deutsch",direction:"ltr"},it:{code:"it",name:"Italian",nativeName:"Italiano",direction:"ltr"},pt:{code:"pt",name:"Portuguese",nativeName:"Portugu\xEAs",direction:"ltr"},"pt-BR":{code:"pt-BR",name:"Portuguese (Brazil)",nativeName:"Portugu\xEAs (Brasil)",direction:"ltr",region:"BR"},zh:{code:"zh",name:"Chinese",nativeName:"\u4E2D\u6587",direction:"ltr"},"zh-CN":{code:"zh-CN",name:"Chinese (Simplified)",nativeName:"\u7B80\u4F53\u4E2D\u6587",direction:"ltr",region:"CN"},"zh-TW":{code:"zh-TW",name:"Chinese (Traditional)",nativeName:"\u7E41\u9AD4\u4E2D\u6587",direction:"ltr",region:"TW"},ja:{code:"ja",name:"Japanese",nativeName:"\u65E5\u672C\u8A9E",direction:"ltr"},ko:{code:"ko",name:"Korean",nativeName:"\uD55C\uAD6D\uC5B4",direction:"ltr"},ar:{code:"ar",name:"Arabic",nativeName:"\u0627\u0644\u0639\u0631\u0628\u064A\u0629",direction:"rtl"},he:{code:"he",name:"Hebrew",nativeName:"\u05E2\u05D1\u05E8\u05D9\u05EA",direction:"rtl"},ru:{code:"ru",name:"Russian",nativeName:"\u0420\u0443\u0441\u0441\u043A\u0438\u0439",direction:"ltr"},nl:{code:"nl",name:"Dutch",nativeName:"Nederlands",direction:"ltr"},pl:{code:"pl",name:"Polish",nativeName:"Polski",direction:"ltr"},tr:{code:"tr",name:"Turkish",nativeName:"T\xFCrk\xE7e",direction:"ltr"},vi:{code:"vi",name:"Vietnamese",nativeName:"Ti\u1EBFng Vi\u1EC7t",direction:"ltr"},th:{code:"th",name:"Thai",nativeName:"\u0E44\u0E17\u0E22",direction:"ltr"},hi:{code:"hi",name:"Hindi",nativeName:"\u0939\u093F\u0928\u094D\u0926\u0940",direction:"ltr"}};import{loadTranslations as FJ,generateTypes as OJ,generateTypesFromModule as CJ,generateSampleConfig as jJ,writeOutputs as VJ,createTranslator as qJ}from"@stacksjs/ts-i18n";import{existsSync as qG}from"fs";import{config as y}from"@stacksjs/config";import{projectPath as MG}from"@stacksjs/path";var t=!1;async function o(){if(t)return;let G=MG("locales");if(qG(G))await L({directory:G,extensions:[".yaml",".yml",".json"]});let J=y.app,K=J?.locale??"en",Z=J?.fallbackLocale??K,U=V();i({locale:K,fallbackLocale:Z,availableLocales:U.length>0?U:[K],warnMissing:(J?.env??process.env.APP_ENV)!=="production"}),t=!0}function l(G){let K=y.app?.locale??"en",Z=new Set(V());if(!Z.size)Z.add(K);let U=(Y)=>{let I=Y.trim().toLowerCase();if(Z.has(I))return I;let z=I.split("-")[0];if(z&&Z.has(z))return z;return K};if(!G)return K;let _=new URL(G.url),$=_.pathname,B=G.headers.get("x-stx-locale");if(B)return U(B);for(let Y of Z){if(Y===K)continue;if($===`/${Y}`||$===`/${Y}/`)return U(Y);if($.startsWith(`/${Y}/`))return U(Y)}let X=_.searchParams.get("locale");if(X)return U(X);let W=(G.headers.get("cookie")??"").match(/(?:^|;\s*)locale=([a-z]{2}(?:-[a-z]{2})?)(?:;|$)/i);if(W?.[1])return U(W[1]);let A=G.headers.get("accept-language")??"";for(let Y of A.split(",")){let I=Y.split(";")[0]?.trim();if(I)return U(I)}return K}async function SG(G){await o();let J=l(G);return x(J),J}function DG(G,J){let K=G.trim().toLowerCase();if(J.locales.includes(K))return K;let Z=K.split("-")[0];if(Z&&J.locales.includes(Z))return Z;return null}function a(G,J){for(let K of J){if(G===`/${K}`||G===`/${K}/`)return{locale:K,path:"/"};if(G.startsWith(`/${K}/`))return{locale:K,path:G.slice(K.length+1)}}return{locale:null,path:G}}function e(G,J,K){if(J===K)return G;if(G==="/")return`/${J}/`;if(G==="/404.html")return`/${J}/404.html`;return`/${J}${G}`}function xG(G,J,K){let Z=DG(J,K);if(!Z)return Response.redirect(new URL("/",G.url).href,302);let U=G.headers.get("referer"),_="/";if(U)try{let Q=new URL(U);if(!Q.pathname.startsWith("/locale/"))_=a(Q.pathname,K.locales).path||"/"}catch{_="/"}let $=e(_,Z,K.defaultLocale),B=new URL($,G.url),X=Response.redirect(B.href,302);return X.headers.append("Set-Cookie",`locale=${Z}; Path=/; Max-Age=31536000; SameSite=Lax`),X}export{VJ as writeI18nOutputs,BG as useI18n,UG as trans,$G as tm,_G as te,ZG as tc,d as t,a as stripLocalePrefix,sG as sortStrings,x as setLocale,yG as setFallbackLocale,S as selectPluralForm,uG as selectPlural,l as resolveRequestLocale,M as parsePluralForms,e as localizePath,FJ as loadTranslationsFromDisk,O as loadTranslations,OG as loadLocale,UJ as loadFromTsI18n,L as loadFromDirectory,k as loadFile,tG as isRTL,p as hasTranslation,vG as hasPluralRule,NG as getTextDirection,bG as getSupportedPluralLocales,nG as getRegionName,KG as getPluralRule,JG as getPluralCategory,E as getLocale,iG as getLanguageName,b as getDisplayName,cG as getCurrencyName,V as getAvailableLocales,CJ as generateI18nTypesFromModule,OJ as generateI18nTypes,jJ as generateI18nSampleConfig,mG as formatUnit,QG as formatTime,YG as formatRelativeTime,n as formatPlural,wG as formatPercent,AG as formatNumber,HG as formatList,WG as formatDateTime,EG as formatDate,IG as formatCurrency,dG as formatBytes,o as ensureLocalesLoaded,qJ as createSimpleTranslator,xG as createLocaleSwitchResponse,KJ as createLoader,u as createI18n,i as configure,rG as compareStrings,SG as applyRequestLocale,q as addTranslations,hG as addPluralRule,_J as LOCALE_INFO,D as I18n};
package/dist/loader.d.ts CHANGED
@@ -14,7 +14,10 @@ export declare function loadLocale(locale: string, path: string): Promise<Transl
14
14
  /**
15
15
  * Create a translation loader for a specific directory
16
16
  */
17
- export declare function createLoader(directory: string, options?: Partial<Omit<LoaderOptions, 'directory'>>): void;
17
+ export declare function createLoader(directory: string, options?: Partial<Omit<LoaderOptions, 'directory'>>): {
18
+ load: () => unknown;
19
+ loadLocale: (locale: string, filename: string) => unknown
20
+ };
18
21
  /**
19
22
  * Load translations via `@stacksjs/ts-i18n` and register them with the
20
23
  * global translator. This is the recommended path for new projects —
package/dist/types.d.ts CHANGED
@@ -1,151 +1,110 @@
1
1
  /**
2
2
  * Common locales with metadata
3
+ * @defaultValue
4
+ * ```ts
5
+ * {
6
+ * en: { code: 'en', name: 'English', nativeName: 'English', direction: 'ltr' },
7
+ * 'en-US': {
8
+ * code: 'en-US',
9
+ * name: 'English (US)',
10
+ * nativeName: 'English (US)',
11
+ * direction: 'ltr',
12
+ * region: 'US'
13
+ * },
14
+ * 'en-GB': {
15
+ * code: 'en-GB',
16
+ * name: 'English (UK)',
17
+ * nativeName: 'English (UK)',
18
+ * direction: 'ltr',
19
+ * region: 'GB'
20
+ * },
21
+ * es: { code: 'es', name: 'Spanish', nativeName: 'Espa\u00f1ol', direction: 'ltr' },
22
+ * fr: { code: 'fr', name: 'French', nativeName: 'Fran\u00e7ais', direction: 'ltr' },
23
+ * de: { code: 'de', name: 'German', nativeName: 'Deutsch', direction: 'ltr' },
24
+ * it: { code: 'it', name: 'Italian', nativeName: 'Italiano', direction: 'ltr' },
25
+ * pt: {
26
+ * code: 'pt',
27
+ * name: 'Portuguese',
28
+ * nativeName: 'Portugu\u00eas',
29
+ * direction: 'ltr'
30
+ * },
31
+ * 'pt-BR': {
32
+ * code: 'pt-BR',
33
+ * name: 'Portuguese (Brazil)',
34
+ * nativeName: 'Portugu\u00eas (Brasil)',
35
+ * direction: 'ltr',
36
+ * region: 'BR'
37
+ * },
38
+ * zh: { code: 'zh', name: 'Chinese', nativeName: '\u4e2d\u6587', direction: 'ltr' },
39
+ * 'zh-CN': {
40
+ * code: 'zh-CN',
41
+ * name: 'Chinese (Simplified)',
42
+ * nativeName: '\u7b80\u4f53\u4e2d\u6587',
43
+ * direction: 'ltr',
44
+ * region: 'CN'
45
+ * },
46
+ * 'zh-TW': {
47
+ * code: 'zh-TW',
48
+ * name: 'Chinese (Traditional)',
49
+ * nativeName: '\u7e41\u9ad4\u4e2d\u6587',
50
+ * direction: 'ltr',
51
+ * region: 'TW'
52
+ * },
53
+ * ja: {
54
+ * code: 'ja',
55
+ * name: 'Japanese',
56
+ * nativeName: '\u65e5\u672c\u8a9e',
57
+ * direction: 'ltr'
58
+ * },
59
+ * ko: {
60
+ * code: 'ko',
61
+ * name: 'Korean',
62
+ * nativeName: '\ud55c\uad6d\uc5b4',
63
+ * direction: 'ltr'
64
+ * },
65
+ * ar: {
66
+ * code: 'ar',
67
+ * name: 'Arabic',
68
+ * nativeName: '\u0627\u0644\u0639\u0631\u0628\u064a\u0629',
69
+ * direction: 'rtl'
70
+ * },
71
+ * he: {
72
+ * code: 'he',
73
+ * name: 'Hebrew',
74
+ * nativeName: '\u05e2\u05d1\u05e8\u05d9\u05ea',
75
+ * direction: 'rtl'
76
+ * },
77
+ * ru: {
78
+ * code: 'ru',
79
+ * name: 'Russian',
80
+ * nativeName: '\u0420\u0443\u0441\u0441\u043a\u0438\u0439',
81
+ * direction: 'ltr'
82
+ * },
83
+ * nl: { code: 'nl', name: 'Dutch', nativeName: 'Nederlands', direction: 'ltr' },
84
+ * pl: { code: 'pl', name: 'Polish', nativeName: 'Polski', direction: 'ltr' },
85
+ * tr: {
86
+ * code: 'tr',
87
+ * name: 'Turkish',
88
+ * nativeName: 'T\u00fcrk\u00e7e',
89
+ * direction: 'ltr'
90
+ * },
91
+ * vi: {
92
+ * code: 'vi',
93
+ * name: 'Vietnamese',
94
+ * nativeName: 'Ti\u1ebfng Vi\u1ec7t',
95
+ * direction: 'ltr'
96
+ * },
97
+ * th: { code: 'th', name: 'Thai', nativeName: '\u0e44\u0e17\u0e22', direction: 'ltr' },
98
+ * hi: {
99
+ * code: 'hi',
100
+ * name: 'Hindi',
101
+ * nativeName: '\u0939\u093f\u0928\u094d\u0926\u0940',
102
+ * direction: 'ltr'
103
+ * }
104
+ * }
105
+ * ```
3
106
  */
4
- export declare const LOCALE_INFO: {
5
- en: {
6
- code: 'en';
7
- name: 'English';
8
- nativeName: 'English';
9
- direction: 'ltr'
10
- };
11
- 'en-US': {
12
- code: 'en-US';
13
- name: 'English (US)';
14
- nativeName: 'English (US)';
15
- direction: 'ltr';
16
- region: 'US'
17
- };
18
- 'en-GB': {
19
- code: 'en-GB';
20
- name: 'English (UK)';
21
- nativeName: 'English (UK)';
22
- direction: 'ltr';
23
- region: 'GB'
24
- };
25
- es: {
26
- code: 'es';
27
- name: 'Spanish';
28
- nativeName: 'Espa\u00f1ol';
29
- direction: 'ltr'
30
- };
31
- fr: {
32
- code: 'fr';
33
- name: 'French';
34
- nativeName: 'Fran\u00e7ais';
35
- direction: 'ltr'
36
- };
37
- de: {
38
- code: 'de';
39
- name: 'German';
40
- nativeName: 'Deutsch';
41
- direction: 'ltr'
42
- };
43
- it: {
44
- code: 'it';
45
- name: 'Italian';
46
- nativeName: 'Italiano';
47
- direction: 'ltr'
48
- };
49
- pt: {
50
- code: 'pt';
51
- name: 'Portuguese';
52
- nativeName: 'Portugu\u00eas';
53
- direction: 'ltr'
54
- };
55
- 'pt-BR': {
56
- code: 'pt-BR';
57
- name: 'Portuguese (Brazil)';
58
- nativeName: 'Portugu\u00eas (Brasil)';
59
- direction: 'ltr';
60
- region: 'BR'
61
- };
62
- zh: {
63
- code: 'zh';
64
- name: 'Chinese';
65
- nativeName: '\u4e2d\u6587';
66
- direction: 'ltr'
67
- };
68
- 'zh-CN': {
69
- code: 'zh-CN';
70
- name: 'Chinese (Simplified)';
71
- nativeName: '\u7b80\u4f53\u4e2d\u6587';
72
- direction: 'ltr';
73
- region: 'CN'
74
- };
75
- 'zh-TW': {
76
- code: 'zh-TW';
77
- name: 'Chinese (Traditional)';
78
- nativeName: '\u7e41\u9ad4\u4e2d\u6587';
79
- direction: 'ltr';
80
- region: 'TW'
81
- };
82
- ja: {
83
- code: 'ja';
84
- name: 'Japanese';
85
- nativeName: '\u65e5\u672c\u8a9e';
86
- direction: 'ltr'
87
- };
88
- ko: {
89
- code: 'ko';
90
- name: 'Korean';
91
- nativeName: '\ud55c\uad6d\uc5b4';
92
- direction: 'ltr'
93
- };
94
- ar: {
95
- code: 'ar';
96
- name: 'Arabic';
97
- nativeName: '\u0627\u0644\u0639\u0631\u0628\u064a\u0629';
98
- direction: 'rtl'
99
- };
100
- he: {
101
- code: 'he';
102
- name: 'Hebrew';
103
- nativeName: '\u05e2\u05d1\u05e8\u05d9\u05ea';
104
- direction: 'rtl'
105
- };
106
- ru: {
107
- code: 'ru';
108
- name: 'Russian';
109
- nativeName: '\u0420\u0443\u0441\u0441\u043a\u0438\u0439';
110
- direction: 'ltr'
111
- };
112
- nl: {
113
- code: 'nl';
114
- name: 'Dutch';
115
- nativeName: 'Nederlands';
116
- direction: 'ltr'
117
- };
118
- pl: {
119
- code: 'pl';
120
- name: 'Polish';
121
- nativeName: 'Polski';
122
- direction: 'ltr'
123
- };
124
- tr: {
125
- code: 'tr';
126
- name: 'Turkish';
127
- nativeName: 'T\u00fcrk\u00e7e';
128
- direction: 'ltr'
129
- };
130
- vi: {
131
- code: 'vi';
132
- name: 'Vietnamese';
133
- nativeName: 'Ti\u1ebfng Vi\u1ec7t';
134
- direction: 'ltr'
135
- };
136
- th: {
137
- code: 'th';
138
- name: 'Thai';
139
- nativeName: '\u0e44\u0e17\u0e22';
140
- direction: 'ltr'
141
- };
142
- hi: {
143
- code: 'hi';
144
- name: 'Hindi';
145
- nativeName: '\u0939\u093f\u0928\u094d\u0926\u0940';
146
- direction: 'ltr'
147
- }
148
- };
107
+ export declare const LOCALE_INFO: Record<string, LocaleInfo>;
149
108
  /**
150
109
  * Plural forms for a translation
151
110
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stacksjs/i18n",
3
- "version": "0.70.73",
3
+ "version": "0.70.75",
4
4
  "description": "Internationalization system for Stacks.js applications",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -40,6 +40,6 @@
40
40
  "@stacksjs/ts-i18n": "^0.1.9"
41
41
  },
42
42
  "devDependencies": {
43
- "@stacksjs/development": "0.70.73"
43
+ "@stacksjs/development": "0.70.75"
44
44
  }
45
45
  }
package/src/bootstrap.ts CHANGED
@@ -54,7 +54,7 @@ export function resolveRequestLocale(request?: Request): string {
54
54
  if (available.has(normalized))
55
55
  return normalized
56
56
  const short = normalized.split('-')[0]
57
- if (available.has(short))
57
+ if (short && available.has(short))
58
58
  return short
59
59
  return defaultLocale
60
60
  }
package/src/formatter.ts CHANGED
@@ -383,7 +383,7 @@ function toDate(date: Date | number | string): Date {
383
383
  */
384
384
  export function getTextDirection(locale?: string): 'ltr' | 'rtl' {
385
385
  const targetLocale = locale || getLocale()
386
- const lang = targetLocale.split('-')[0].toLowerCase()
386
+ const lang = (targetLocale.split('-')[0] ?? targetLocale).toLowerCase()
387
387
 
388
388
  // RTL languages
389
389
  const rtlLanguages = ['ar', 'he', 'fa', 'ur', 'yi', 'ps', 'sd', 'ug', 'ku', 'ckb']
package/src/loader.ts CHANGED
@@ -250,6 +250,7 @@ function setNested(
250
250
 
251
251
  for (let i = 0; i < parts.length - 1; i++) {
252
252
  const part = parts[i]
253
+ if (!part) continue
253
254
  if (!current[part] || typeof current[part] === 'string') {
254
255
  current[part] = {}
255
256
  }
@@ -257,7 +258,7 @@ function setNested(
257
258
  }
258
259
 
259
260
  const lastPart = parts[parts.length - 1]
260
- current[lastPart] = value
261
+ if (lastPart) current[lastPart] = value
261
262
  }
262
263
 
263
264
  /**
@@ -268,6 +269,7 @@ function deepMerge(target: TranslationMessages, source: TranslationMessages): Tr
268
269
 
269
270
  for (const key of Object.keys(source)) {
270
271
  const sourceValue = source[key]
272
+ if (sourceValue === undefined) continue
271
273
  const targetValue = result[key]
272
274
 
273
275
  if (
@@ -14,7 +14,7 @@ function normalizeLocale(code: string, config: LocaleSwitchConfig): string | nul
14
14
  if (config.locales.includes(normalized))
15
15
  return normalized
16
16
  const short = normalized.split('-')[0]
17
- if (config.locales.includes(short))
17
+ if (short && config.locales.includes(short))
18
18
  return short
19
19
  return null
20
20
  }
@@ -22,7 +22,7 @@ export function getPluralCategory(n: number, locale: string): PluralCategory {
22
22
  */
23
23
  export function getPluralRule(locale: string): PluralRule {
24
24
  // Get base language code
25
- const lang = locale.split('-')[0].toLowerCase()
25
+ const lang = locale.split('-')[0]?.toLowerCase() ?? locale.toLowerCase()
26
26
 
27
27
  const rule = pluralRules[lang]
28
28
  if (rule) return rule
@@ -48,22 +48,25 @@ export function parsePluralForms(
48
48
  const forms = new Map<PluralCategory, string>()
49
49
 
50
50
  if (parts.length === 1) {
51
- forms.set('other', parts[0])
51
+ forms.set('other', parts[0] ?? '')
52
52
  }
53
53
  else if (parts.length === 2) {
54
- forms.set('one', parts[0])
55
- forms.set('other', parts[1])
54
+ forms.set('one', parts[0] ?? '')
55
+ forms.set('other', parts[1] ?? '')
56
56
  }
57
57
  else if (parts.length === 3) {
58
- forms.set('zero', parts[0])
59
- forms.set('one', parts[1])
60
- forms.set('other', parts[2])
58
+ forms.set('zero', parts[0] ?? '')
59
+ forms.set('one', parts[1] ?? '')
60
+ forms.set('other', parts[2] ?? '')
61
61
  }
62
62
  else if (parts.length >= 4) {
63
63
  // Extended format: zero | one | two | few | many | other
64
64
  const categories: PluralCategory[] = ['zero', 'one', 'two', 'few', 'many', 'other']
65
65
  for (let i = 0; i < parts.length && i < categories.length; i++) {
66
- forms.set(categories[i], parts[i])
66
+ const category = categories[i]
67
+ const part = parts[i]
68
+ if (category !== undefined && part !== undefined)
69
+ forms.set(category, part)
67
70
  }
68
71
  }
69
72
 
@@ -274,7 +277,7 @@ export function getSupportedPluralLocales(): string[] {
274
277
  * Check if a locale has custom plural rules
275
278
  */
276
279
  export function hasPluralRule(locale: string): boolean {
277
- const lang = locale.split('-')[0].toLowerCase()
280
+ const lang = locale.split('-')[0]?.toLowerCase() ?? locale.toLowerCase()
278
281
  return lang in pluralRules
279
282
  }
280
283
 
@@ -282,6 +285,6 @@ export function hasPluralRule(locale: string): boolean {
282
285
  * Add a custom plural rule for a locale
283
286
  */
284
287
  export function addPluralRule(locale: string, rule: PluralRule): void {
285
- const lang = locale.split('-')[0].toLowerCase()
288
+ const lang = locale.split('-')[0]?.toLowerCase() ?? locale.toLowerCase()
286
289
  pluralRules[lang] = rule
287
290
  }
package/src/translator.ts CHANGED
@@ -92,7 +92,7 @@ export class I18n implements I18nInstance {
92
92
  * Get translation message
93
93
  */
94
94
  tm = (key: string, locale?: string): TranslationMessages | string | undefined => {
95
- return this.getMessage(key, locale)
95
+ return this.getMessage(key, locale ?? this.locale)
96
96
  }
97
97
 
98
98
  /**
@@ -478,7 +478,8 @@ function deepMerge(target: TranslationMessages, source: TranslationMessages): Tr
478
478
  )
479
479
  }
480
480
  else {
481
- result[key] = sourceValue
481
+ if (sourceValue !== undefined)
482
+ result[key] = sourceValue
482
483
  }
483
484
  }
484
485