@koine/i18n 2.0.0-beta.43 → 2.0.0-beta.44

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.
@@ -31,11 +31,11 @@ var t__namespace = /*#__PURE__*/_interopNamespace(t$4);
31
31
 
32
32
  const dataFsConfig={cwd:process.cwd(),ignore:[]};let r$4=(e={})=>utils.mergeObjects({...dataFsConfig},e),i$4=e=>{let{cwd:o,ignore:a}=e;return glob.glob.sync("*",{cwd:o,withFileTypes:!0,ignore:[...a,"node_modules/**"]}).filter(e=>e.isDirectory()).map(e=>e.relative()).sort((e,o)=>e.localeCompare(o))};let getDataFs=async a=>{let s=r$4(a),l=i$4(s),n=[];return await Promise.all(l.map(async a=>{let r=await glob.glob("**/*.json",{cwd:node_path.join(s.cwd,a),ignore:s.ignore});await Promise.all(r.map(async t=>{let r=node_path.join(s.cwd,a,t),i=await promises.readFile(r,"utf8");i&&n.push({path:t,data:JSON.parse(i),locale:a});}));})),{localesFolders:l,translationFiles:n}};
33
33
 
34
- const dataSourceRoutesConfig={translationJsonFileName:"~.json",tokens:{parentReference:"^",idDelimiter:".",pathnameWildcard:"*"}};let i$3=e=>e.replace(/\.index$/,""),o$4=e=>formatRoutePathname.formatRoutePathname(e.replace(/\*/g,"").replace(/[[{]{1,2}(.*?)[\]}]{1,2}/g,(e,t)=>`[${t.trim()}]`)),l$3=r=>utils.capitalize(utils.changeCaseCamel(r)),m$6=(e,t,r,a)=>{let n=t[a].pathnames[r];if(n.startsWith(`/${e.source.routes.tokens.parentReference}`)){let s=RegExp(`^\\/\\${e.source.routes.tokens.parentReference}`);n=n.replace(s,"");let i=a.split(".").slice(0,-1).join(".");if(i)n=m$6(e,t,r,i)+n;else throw Error("Used a parent route token reference without a matching parent route")}return n},p$3=(e,t)=>{utils.forin(t,(a,n)=>{utils.forin(n.pathnames,r=>{t[a].pathnames[r]=m$6(e,t,r,a);});});},c=e=>{let t=e.match(/\[.*?\]/g);if(t)return t.map(e=>e.slice(1,-1).trim()).reduce((e,t)=>(e[t]="stringOrNumber",e),{})},h$1=(e,t)=>{let{defaultLocale:r,locales:a}=e;for(let e in t){let s=t[e].pathnames,i=s[r],o={};for(let e in s){let t=s[e];t!==i&&(o[e]=t);}Object.keys(o).length===a.length-1||(Object.keys(o).length>=1?(o[r]=i,t[e].optimizedPathnames=utils.objectSortByKeysMatching(o,r)):t[e].optimizedPathnames=i);}};let getDataSourceRoutes=(e,{translationFiles:t})=>{let{defaultLocale:r}=e,s=[],m={};for(let p=0;p<t.length;p++){let{path:h,locale:u,data:d}=t[p];if(h===e.source.routes.translationJsonFileName){let t=utils.objectFlat(d,e.source.routes.tokens.idDelimiter);for(let a in t){let p=t[a],h=i$3(a);if(!m[h]){m[h]=m[h]||{};let t=l$3(h),r=c(h),a=p.includes(e.source.routes.tokens.pathnameWildcard);m[h].id=h,m[h].typeName=t,r&&(m[h].params=r),a&&(m[h].wildcard=!0,s.push(h));}m[h].pathnames=m[h].pathnames||{},m[h].pathnames[u]=o$4(p),m[h].pathnames=utils.objectSortByKeysMatching(m[h].pathnames,r);}}}if(p$3(e,m),h$1(e,m),s.length)for(let e in m)s.some(t=>e.startsWith(t)&&t!==e)&&(m[e].inWildcard=!0);return Object.fromEntries(Object.entries(m).sort())};
34
+ const dataSourceRoutesConfig={translationJsonFileName:"~.json",tokens:{parentReference:"^",idDelimiter:".",pathnameWildcard:"*"}};let i$3=e=>e.replace(/\.index$/,""),o$4=e=>formatRoutePathname.formatRoutePathname(e.replace(/\*/g,"").replace(/[[{]{1,2}(.*?)[\]}]{1,2}/g,(e,t)=>`[${t.trim()}]`)),l$3=r=>utils.capitalize(utils.changeCaseCamel(r)),m$6=(e,t,r,a)=>{let n=t[a].pathnames[r];if(n.startsWith(`/${e.source.routes.tokens.parentReference}`)){let s=RegExp(`^\\/\\${e.source.routes.tokens.parentReference}`);n=n.replace(s,"");let i=a.split(".").slice(0,-1).join(".");if(i)n=m$6(e,t,r,i)+n;else throw Error("Used a parent route token reference without a matching parent route")}return n},p$4=(e,t)=>{utils.forin(t,(a,n)=>{utils.forin(n.pathnames,r=>{t[a].pathnames[r]=m$6(e,t,r,a);});});},c=e=>{let t=e.match(/\[.*?\]/g);if(t)return t.map(e=>e.slice(1,-1).trim()).reduce((e,t)=>(e[t]="stringOrNumber",e),{})},h$2=(e,t)=>{let{defaultLocale:r,locales:a}=e;for(let e in t){let s=t[e].pathnames,i=s[r],o={};for(let e in s){let t=s[e];t!==i&&(o[e]=t);}Object.keys(o).length===a.length-1||(Object.keys(o).length>=1?(o[r]=i,t[e].optimizedPathnames=utils.objectSortByKeysMatching(o,r)):t[e].optimizedPathnames=i);}};let getDataSourceRoutes=(e,{translationFiles:t})=>{let{defaultLocale:r}=e,s=[],m={};for(let p=0;p<t.length;p++){let{path:h,locale:u,data:d}=t[p];if(h===e.source.routes.translationJsonFileName){let t=utils.objectFlat(d,e.source.routes.tokens.idDelimiter);for(let a in t){let p=t[a],h=i$3(a);if(!m[h]){m[h]=m[h]||{};let t=l$3(h),r=c(h),a=p.includes(e.source.routes.tokens.pathnameWildcard);m[h].id=h,m[h].typeName=t,r&&(m[h].params=r),a&&(m[h].wildcard=!0,s.push(h));}m[h].pathnames=m[h].pathnames||{},m[h].pathnames[u]=o$4(p),m[h].pathnames=utils.objectSortByKeysMatching(m[h].pathnames,r);}}}if(p$4(e,m),h$2(e,m),s.length)for(let e in m)s.some(t=>e.startsWith(t)&&t!==e)&&(m[e].inWildcard=!0);return Object.fromEntries(Object.entries(m).sort())};
35
35
 
36
36
  let s$2=["zero","one","two","few","many","other"],a$4="other";let isPluralSuffix=e=>s$2.includes(e)||utils.isNumericLiteral(e);let removePluralSuffix=e=>{let[l]=utils.splitReverse(e,"_");return l?e.replace(`_${l}`,""):e};let isPluralKey=e=>{let[l]=utils.splitReverse(e,"_");return isPluralSuffix(l)};let i$2=e=>{let l={};return e.forEach(e=>{let[r]=utils.split(e,"_");l[r]=l[r]||[],l[r].push(e);}),l};let transformKeysForPlurals=l=>{let r=l.filter(isPluralKey);if(r.length){let t=[...l];return utils.forin(i$2(r),(e,r)=>{l.includes(e)||t.push(e),r.forEach(e=>{l.includes(e)&&(t=t.filter(l=>l!==e));});}),t}return l};let hasPlurals=e=>Object.keys(e).includes(a$4);let hasOnlyPluralKeys=e=>!!hasPlurals(e)&&0===pickNonPluralKeys(e).length;let pickNonPluralKeys=e=>Object.keys(e).filter(e=>!isPluralSuffix(e));let pickNonPluralValue=e=>hasPlurals(e)?utils.objectPick(e,pickNonPluralKeys(e)):e;
37
37
 
38
- const dataSourceTranslationsConfig={dynamicDelimiters:{start:"{{",end:"}}"},fnsAsDataSources:!0,fnsPrefix:"",createArrayIndexBasedFns:!1};let u=RegExp(node_path.sep,"g"),p$2=e=>{let t=e.replace(/~/g,"$").replace(/-/g,"_").replace(u,"_").replace(/_+/g,"_").replace(/[^a-zA-Z0-9_$]/gi,"");return /^[0-9]/.test(t)?"$"+t:t},f$2=(e,t)=>{if(utils.isString(t)){let{start:r,end:a}=e.source.translations.dynamicDelimiters,l=RegExp(`${r}(.*?)${a}`,"gm"),s=t.match(l);if(s)return s.map(e=>e.replace(r,"").replace(a,"").trim()).reduce((e,t)=>(e[t]="stringOrNumber",e),{})}},m$5=(e,t)=>(Object.keys(t).filter(isPluralKey).forEach(e=>{let r=removePluralSuffix(e);t[r]&&(t[r].plural=!0),delete t[e];}),t),d$3=(e,t,r,a,l)=>{if(utils.isPrimitive(a)){let s=f$2(e,a);l[t]=l[t]||{},l[t].values=l[t].values||{},l[t].values[r]=a,l[t].typeValue="Primitive",s&&(l[t].params=s);}else {if(e.source.translations.fnsAsDataSources){let e=utils.isArray(a)?"Array":"Object";l[t]=l[t]||{},l[t].values=l[t].values||{},l[t].values[r]=a,l[t].typeValue=e;}if(utils.isArray(a)){if(e.source.translations.createArrayIndexBasedFns)for(let s=0;s<a.length;s++)d$3(e,t+"_"+s,r,a[s],l);}else for(let s in a)d$3(e,t+"_"+p$2(s),r,a[s],l);}return l},g=(l,s,n)=>{let{locale:i,path:o}=s,c=node_path.join(node_path.dirname(o),node_path.basename(o,node_path.extname(o)));for(let e in s.data){let t=s.data[e];d$3(l,p$2(c+(e?"_"+e:"")),i,t,n);}return n};let getDataSourceTranslations=(e,{translationFiles:t})=>{let r={};for(let a=0;a<t.length;a++)t[a].path!==e.source.routes.translationJsonFileName&&g(e,t[a],r);return Object.fromEntries(Object.entries(r=m$5(e,r)).sort())};
38
+ const dataSourceTranslationsConfig={dynamicDelimiters:{start:"{{",end:"}}"},fnsAsDataSources:!0,fnsPrefix:"",createArrayIndexBasedFns:!1};let u=RegExp(node_path.sep,"g"),p$3=e=>{let t=e.replace(/~/g,"$").replace(/-/g,"_").replace(u,"_").replace(/_+/g,"_").replace(/[^a-zA-Z0-9_$]/gi,"");return /^[0-9]/.test(t)?"$"+t:t},f$1=(e,t)=>{if(utils.isString(t)){let{start:r,end:a}=e.source.translations.dynamicDelimiters,l=RegExp(`${r}(.*?)${a}`,"gm"),s=t.match(l);if(s)return s.map(e=>e.replace(r,"").replace(a,"").trim()).reduce((e,t)=>(e[t]="stringOrNumber",e),{})}},m$5=(e,t)=>(Object.keys(t).filter(isPluralKey).forEach(e=>{let r=removePluralSuffix(e);t[r]&&(t[r].plural=!0),delete t[e];}),t),d$3=(e,t,r,a,l)=>{if(utils.isPrimitive(a)){let s=f$1(e,a);l[t]=l[t]||{},l[t].values=l[t].values||{},l[t].values[r]=a,l[t].typeValue="Primitive",s&&(l[t].params=s);}else {if(e.source.translations.fnsAsDataSources){let e=utils.isArray(a)?"Array":"Object";l[t]=l[t]||{},l[t].values=l[t].values||{},l[t].values[r]=a,l[t].typeValue=e;}if(utils.isArray(a)){if(e.source.translations.createArrayIndexBasedFns)for(let s=0;s<a.length;s++)d$3(e,t+"_"+s,r,a[s],l);}else for(let s in a)d$3(e,t+"_"+p$3(s),r,a[s],l);}return l},g$1=(l,s,n)=>{let{locale:i,path:o}=s,c=node_path.join(node_path.dirname(o),node_path.basename(o,node_path.extname(o)));for(let e in s.data){let t=s.data[e];d$3(l,p$3(c+(e?"_"+e:"")),i,t,n);}return n};let getDataSourceTranslations=(e,{translationFiles:t})=>{let r={};for(let a=0;a<t.length;a++)t[a].path!==e.source.routes.translationJsonFileName&&g$1(e,t[a],r);return Object.fromEntries(Object.entries(r=m$5(e,r)).sort())};
39
39
 
40
40
  const dataSourceConfig={routes:dataSourceRoutesConfig,translations:dataSourceTranslationsConfig};let getDataSource=(t,r)=>({routes:getDataSourceRoutes(t,r),translations:getDataSourceTranslations(t,r)});
41
41
 
@@ -192,7 +192,7 @@ export default routesSlim;
192
192
 
193
193
  let dataParamsToTsInterfaceBody=e=>Object.keys(e).reduce((r,t)=>{let a=e[t],s="";switch(a){case"number":s="number";break;case"string":s="string";break;default:s="string | number";}return r.push(`${t}: ${s};`),r},[]).join(" ");
194
194
 
195
- let i=e=>utils.isString(e)||utils.isNumber(e)?`"${e}"`:utils.isBoolean(e)?`${e}`:utils.isArray(e)?JSON.stringify(e):`(${JSON.stringify(e)})`,p$1=(t,r)=>utils.areEqual(t,r),m$3=(e,r)=>{let{defaultLocale:o}=e,l="";return utils.forin(r,(e,t)=>{e===o||p$1(t,r[o])||(l+=`locale === "${e}" ? ${i(t)} : `);}),l+=i(r[o])};var s$1 = (({config:e,data:r})=>{let o=`
195
+ let i=e=>utils.isString(e)||utils.isNumber(e)?`"${e}"`:utils.isBoolean(e)?`${e}`:utils.isArray(e)?JSON.stringify(e):`(${JSON.stringify(e)})`,p$2=(t,r)=>utils.areEqual(t,r),m$3=(e,r)=>{let{defaultLocale:o}=e,l="";return utils.forin(r,(e,t)=>{e===o||p$2(t,r[o])||(l+=`locale === "${e}" ? ${i(t)} : `);}),l+=i(r[o])};var s$1 = (({config:e,data:r})=>{let o=`
196
196
  /* eslint-disable @typescript-eslint/no-unused-vars */
197
197
  /* eslint-disable prefer-const */
198
198
  import type { I18n } from "./types";
@@ -201,7 +201,7 @@ import { tInterpolateParams } from "./tInterpolateParams";
201
201
  `;return utils.forin(r.source.translations,(t,{values:r,params:l,plural:s})=>{let p=`${e.source.translations.fnsPrefix}${t}`;l&&s&&(l.count="number");let $=[l?`params: { ${dataParamsToTsInterfaceBody(l)} }`:"","locale?: I18n.Locale"].filter(Boolean).join(", ");o+=`export let ${p} = (${$}) => `;let f="";utils.isPrimitive(r)?f+=i(r):f+=m$3(e,r),f=l?`tInterpolateParams(${f}, params);`:`${f};`,o+=f+`
202
202
  `;}),o});
203
203
 
204
- let e$3=e=>e.split("").map(e=>`\\${e}`).join("");var f$1 = (({config:a})=>{let{start:t,end:r}=a.source.translations.dynamicDelimiters;return `
204
+ let e$3=e=>e.split("").map(e=>`\\${e}`).join("");var f = (({config:a})=>{let{start:t,end:r}=a.source.translations.dynamicDelimiters;return `
205
205
  export let tInterpolateParams = (
206
206
  value: string,
207
207
  params?: object,
@@ -253,7 +253,7 @@ import type { I18n } from "./types";
253
253
  `;return utils.forin(a.source.routes,(t,{typeName:a,pathnames:n,params:p})=>{let s=`to_${utils.changeCaseCamel(t)}`,i=`I18n.RouteParams.${a}`,u=[p?`params: ${i}`:"",m?"":"locale?: I18n.Locale"].filter(Boolean).join(", "),c=m?'""':"locale",f=p?", params":"";$+=`export let ${s} = (${u}) => `,utils.isString(n)?$+=`toFormat(${c}, "${n}"${f});`:$+=`toFormat(${c}, ${r$2(l,n)}${f});`,$+=`
254
254
  `;}),$});
255
255
 
256
- var p = (({config:a})=>`
256
+ var p$1 = (({config:a})=>`
257
257
  export function toFormat(
258
258
  locale: string | undefined,
259
259
  pathname: string,
@@ -310,33 +310,377 @@ export function toFormat(
310
310
  export default toFormat;
311
311
  `);
312
312
 
313
- let d$1=(e,t)=>{if(!utils.isArray(t)&&utils.isObject(t)){if(hasOnlyPluralKeys(t))return `'${e}': string;`;if(hasPlurals(t))return `'${e}': string | ${f(pickNonPluralValue(t))}`}return `'${e}': ${f(t)}`},f=e=>{let t="",r="";if(utils.isBoolean(e)?r="boolean":utils.isString(e)&&(r="string"),r)t+=r+";";else if(e){if(utils.isArray(e)){let r=e[0];t+=`${f(r)}[];`;}else if(utils.isObject(e)){t+="{";let r=transformKeysForPlurals(Object.keys(e));for(let o=0;o<r.length;o++){let s=r[o],i=e[s]||"";t+=d$1(s,i);}t+="};";}}else t+="";return (t=t.replace(/;\[\];/g,"[];")).replace(/;+/g,";")},y=(e,t)=>{let{translationFiles:r}=t,{defaultLocale:o}=e,s=r.filter(e=>e.locale===o),i=`
313
+ let p=(t,n)=>{if(!utils.isArray(n)&&utils.isObject(n)){if(hasOnlyPluralKeys(n))return `'${t}': string;`;if(hasPlurals(n))return `'${t}': string | ${d$1(pickNonPluralValue(n))}`}return `'${t}': ${d$1(n)}`},d$1=t=>{let i="",r="";if(utils.isBoolean(t)?r="boolean":utils.isString(t)&&(r="string"),r)i+=r+";";else if(t){if(utils.isArray(t)){let e=t[0];i+=`${d$1(e)}[];`;}else if(utils.isObject(t)){i+="{";let e=transformKeysForPlurals(Object.keys(t));for(let n=0;n<e.length;n++){let s=e[n],a=t[s]||"";i+=p(s,a);}i+="};";}}else i+="";return (i=i.replace(/;\[\];/g,"[];")).replace(/;+/g,";")},h$1=(t,e)=>{let{translationFiles:n}=e,{defaultLocale:s}=t,a=n.filter(t=>t.locale===s),i=`
314
314
  export interface Translations {
315
- `;for(let e=0;e<s.length;e++){let{path:t,data:r}=s[e],o=t.replace(".json","");i+=`"${o}": ${f(r)}
315
+ `;for(let t=0;t<a.length;t++){let{path:e,data:n}=a[t],s=e.replace(".json","");i+=`"${s}": ${d$1(n)}
316
316
  `;}return i+`
317
317
  }
318
- `},m$2=e=>{let t="\n";return utils.forin(e,(e,{typeName:r,params:o})=>{o&&(t+=` export interface ${r} { ${dataParamsToTsInterfaceBody(o)} }
319
- `);}),t},$=(e,t)=>Object.keys(e).filter(r=>t(r,e[r])).sort().map(e=>`"${e}"`).join(" | ");var l$1 = (({config:r,data:o})=>{let s=m$2(o.source.routes),i=node_fs.readFileSync(node_path.join(__dirname,"../../types.ts"),"utf-8"),a=node_fs.readFileSync(node_path.join(__dirname,"../../types-utils.ts"),"utf-8"),n=$(o.source.routes,(e,t)=>!t.params),l=$(o.source.routes,(e,t)=>!!t.params);return `
318
+ `},m$2=e=>{let n="\n";return utils.forin(e,(t,{typeName:e,params:s})=>{s&&(n+=` export interface ${e} { ${dataParamsToTsInterfaceBody(s)} }
319
+ `);}),n},g=(t,e)=>Object.keys(t).filter(n=>e(n,t[n])).sort().map(t=>`"${t}"`).join(" | ");var l$1 = (({config:t,data:e})=>{let n=m$2(e.source.routes),s=g(e.source.routes,(t,e)=>!e.params),a=g(e.source.routes,(t,e)=>!!e.params);return `
320
320
  /* eslint-disable @typescript-eslint/no-namespace */
321
321
  /* eslint-disable @typescript-eslint/no-explicit-any */
322
322
  /* eslint-disable @typescript-eslint/ban-types */
323
323
 
324
324
  export namespace I18n {
325
- export type Locale = ${r.locales.map(e=>`"${e}"`).join(" | ")};
325
+ export type Locale = ${t.locales.map(t=>`"${t}"`).join(" | ")};
326
326
 
327
327
  export type LocalesMap<T = any> = Record<Locale, T>;
328
328
 
329
- ${y(r,o.fs)}
330
- ${i}
329
+ ${h$1(t,e.fs)}
330
+
331
+ // import type {
332
+ // // Get,
333
+ // Split,
334
+ // } from "@koine/utils";
335
+
336
+ type Split<
337
+ S extends string,
338
+ Delimiter extends string,
339
+ > = S extends \`\${infer Head}\${Delimiter}\${infer Tail}\`
340
+ ? [Head, ...Split<Tail, Delimiter>]
341
+ : S extends Delimiter
342
+ ? []
343
+ : [S];
344
+
345
+ /**
346
+ * @file
347
+ *
348
+ * Test this on TSplayground:
349
+ * https://www.typescriptlang.org/play?#code/PTAEFpK6dv4YpSBQJSgJYEYAcA7YAFwE8AHTfAcwGcA6AEzqJrTGQ86+7hRQYCmAYwA2AQwBOA0PjEBbATTJih0gJJ58oAN4oMlIgIkAzFdIAqEsfhriimAPY2dejKHQAiAH4eAXKGsSAG5XAF8+fXxDEzNQS2tbMXsnGgBhJ2NMKh1w8LYIHkKi4sh8rE1ickVmVnQS+obUFFIyaQApB0oAHgBlbAAaUB6AJgA+UABeIexQAQAPQ3wGGlAaIglKKlcAfiHh2YWBJZW1jepXDF2AAwASbT7QujuR0KuL0H8+1398AQA3IwhNAAKmBemBoAAIpghMlZBISKAHMYAiIRKAiAALaTrBJ2Rw2cHAFDzMgOCREDFVOJWGz4lLQ2EEySIqYaAh0eJ0pIEmhA4Cg8GgABy8kUylUJ0xDgAriIGKA5EkhJiMdjQJkREcxSsAO5GHG0xJwlaSaSCTK-BhEklzMkUqmtGl4pICUUKJSxKYAUQWVlhXQA1gISMjndyTYy4SzBqdNqN+YLQBCAErCGUSGiYAGKsRkVoKlo4hwHXGw1HomU0MQAIy1q3Wm1AyixK2MEgccgCGKN9PwQotlEwcKRNYAVsJKQAKKsysRoxHtzvdto9ADywo1mC1AEo6EK1JSq4o1dIrpRjEYrqAPI2hIGPBiS2tyTj1Q5x5OsFoFHJyYjrAVMgpABKIhQAbQvIdDG-NZrHsHknFmCQO0zABdKdMSIIgyBoXwQDg+8HABEwRAcXU6CETtgAAR2AAB2ABWRiAGZsBYgA2DjgGwABOFjcF4+iAAYdxtUlyUpItw2NXkAAUkkxGgulccwUHGKZdAwcCAGlv1AX0yyIIMQzDcxY0bahRjQ3x3nQeZRBlQRZjkMhSFHCdYRoQYBFc9zJCsEhvJctzETjahWDcOJdLQ0sjmWUA0yoiQGC6cKqEGX4SPGAAfGR-iMcDYryjxH12LKjDssApCEDMswBERESVWQqGkD9PJYd5-HMGK4uORLhHJVL0sGGV8EDfByPwcZdneDA8tubQdNedwwCxTAVg2p9QBraRa3rIgS2PU8b3ax8aGlClaqIObQDyjpuh0wYuVklIFNbLoep0tDRlGKqK3I5sNjkYdsxPckAhQsRETDYDMBB+wAUitxut6+ZFgS9K7pkGU5F2iRsZrBwHC1axsYAQShkg0ss7I8vwXH8cJ4nSem0BrjuZa3ii+y5kc5zAnW6hZhEGgBC6-KSJCUJwMM-1jODUMUXMhszioay+oSi8jFANd2d1j5JcBPgBTBZMBtqzMwdzfMBELakw3nStqzraQsZbJSNQ7LssWkXVpQO3tEMJc2GBhaMEToUBD1AY8Vl90Bz3wS8JGvW8NnvR9DobV8Tvar9KEVXz-wCJYgYKsDzcg5PoOkSg4KiTBg+Q1CaAwrCcLwgiiBUQNiKMYwyIoqi5FohjmLYzjuL4gShNE8S7Ukx0LCDk1ybRd7Pc01xwM3Qu5ZUBXTOV1feSjZkEQstXrP8LS3F0-SD4DRWzNPhlw4vkhd7Q-p0pv27eb82kL5UKHlJzBRAf5KmEC-JhVpsjKKL0+w0HPk4Fk380aHH6klIaNM1aZQKhIXKRsJBFWxqVfWFUJAAOqumK2DUmrWDEK1MBXlbrdTfjYVB8Iv7CjQpgjGKwcEpTwZsUa41Jq6jZtsW6UUFp3GFKEXwnMVroHWpteOJZdoBFdttY6CcPBnVWJdIg11ZFuHup0fAXQ9Lo3iicWm+tFqKOUUtFa-hnFKKuM9ThNBN7KSQcHFBH80EIgwd9X6NCAa6iBvDUGSMkQEwCtDJEKI4YIzBgglGMlkHcPQXwgR9jVZNnpozHWeUiYkwEGTPKlNAqiOFqUvG5Sdos2qdIxOCilEqO5lFVaBwgGlxIELbIAhRbiz6YbKhYRZZ+kPiZJWOSgl5LCXwq+8Y0LS1mUZBZr8XSRhCTw9ZVlYp2P6trAmetdh6x+IQxMZsIQAFV8AiEwMGfSVxfgLHALiCMAhrz6gCAwICYgaBZmFjQTs0hgJ2wEJaO2kMqC4yOCwAIkUIQXUktdNsEMR5-i0MdIQoLFC2XNuAROhjxweGvKC7smKroykpMYCGVxtCgCkEQDM+A1yfi8v4dYMppCvCFOSq4pUaWmmMVixlGoWVstMGiGsvd-AUOFcmBe9opLUkCXCHoJjrqTFOlS8hHh7lCgAIqCoRKwqSJYyRkDlK6E6HKuUIt+caBFWMDBGDJPiYWDAkhiCFBDMQSzdX6sZfudV5thRrnMN6fw-sjix3BdkUN9LTEyuUGCpsOqCRrjcrybaVwiBTh3NeLak1KQ0BlPmSSdshTmDXJCNc-LqTVkvGqLau1MRiD+I4DMqSTrrRSoi5FURQBTgcIWlIe4NVL2knmpwlqjCslcHlMag4rTrpxmiHdS78B6ulTdeaLhJngUVv4dKNlimNJxs0gmFS2nWBCFFUIZrzYFqLWGBO7q7DSGMGNJkSEQ1hoJEehlRAo0PJFHGhNoAk0EtTXSiNlJs3IYPQWk0xbS3lqwCsKtqxa2aobebJtLa21Og7W+btAhe39tlATH96oR0KkkEihQE6p0zpsHO5MxIJIOkXZwrDRaJg7s3XCygpHsYHog5mk92M75RRdRIblvKWDbH8JU1mr6+kKpEEq+8Wnb101M0VPTUULRiDlEQEz6VLOgHfSbJMEIdUAaAyOf2MJVTTujAuAIQhVBuW7LID0Eo9ptkwJmSk7Hx0nuBAJxeQntWrwEJ9d04pYhnISu5zLnpVDY0k-ChgGlVL5Yi5rFYeWdQRfeLsGr4WzCpXMBVswf0oocP2QISEcKbMiCIB+tzaWNSeYJMmYEuphzSkZaCGQtWzCDGHNEzRqwxlfkCEOhOTshR9rENufabt4H6SdvNprkpoNJc1cvMNPW+u2cNZ9NMnK1OGr-lOVweEwMpHXiIfx-RXA0RMweldl9XAOGByJnjrAdyTHGOYZ7XKhu3dG-gYDWhQRTaxHNsLWXVBLdi2iciq2xZanLE4RqJ1Tt7YO67IU6VNpaATlQMGWhccFYEJd2013hPdba6oFr-PgFYNy2loXGlQAqQwAjgQL2tBTHSupD7GAvsHr8YpAJviVlf1awt1QP0AcYCB11iMBJQckEN0iSH+z83Q5QLDiY8PEdqeR+51H6PQBkQcGLZegxs7HQp4iQu8MyBak4z3bDWIkgpsUEKBOJbryAbRyOSgYcapEEp1jmblJfh22FhNciWoGAsOY0dtWWAc8CDtui7asocKRvnSlp07mAAyxMfdTGV6sEzgRLfG++-gc3luIcm9etyu3DvxgOZczBgAQoiKi+BQIjg7Iyk8W7QYpEhnXJn6orheDoGOSF+BE-bh3ydMiRKtQ0GANoS-84BChGJObDsDgmUk0EBIKO5hMRbWklIGFMWCdQhREMkHNOsREXHLbdULdBFVfQwAAchr3v1eTFhBSxCgKnArgQ2m1AAYBIDCxhGbEkl5D40S25wXVS26wYBTDr2kCmDV38U+i10OXQW8A8B+mR0eXsFeXcgbgDTIl+Bu2znRnlgrEnSVDmAxF1BLC1AajwIEDckxFhxVG3AYCkC0HgNj3Nij2rR7gpD1FwOZ1Z1M2g0bV-w0Rj0A3RGzlaiknVBrRrC0JWEdi0DzFDxhGbjNDEB8joCoCjlO0cOcODRRFDQDQuiJkkAYFGmQ2Wyz1JRgyuCSLj2pEhFBUxEiJSloLXxWAYLSxoLoLSF-3lA0LXGMC6A8HCIyIcCiI8ATCFCSO5nIMEy1Wb3yOyMMCKLUNKPKNUh6H0NRRywcXwVUiq1M0NXcwKLX3UgmLGMWnMH6MkBYEeDuAuVAAADFos1gVjtA1iehBolg1V9ZNiYtsZFoTjtinh7gDiGAjj-B7JbADBwAw4XYtRwAvkiBwBXkhCAABIsGgIQDYNycARQb4ogYASacAMaY8BgcAP4SQZGcwOYu4BYgYmgHYtYi4ogI43YLE74EhfkdgRoYkkk8AMoHAAgYAOw8wGofIUk+k+oPgFom7AAcVlyRKmClziCFzGMazx2kA3SWCkytENWK2k2tFGC7xQIEHs1pkt3Zwi2ByF3tzh1ADkm9g2nS3cwyz1wEF+mR1BFjXjVBG6hLCVHeX+MBMwBCxkIkEDBkBhDGURGxCkFzHeRrVdJENkDpyriuCkEhREABDaGPwAFkHAGA5R-l25sJcJ8JgBdQEzmAqgASgSM9rB-DyQqBiAASMgsgABif0kmIM0M8MyMsSc2QuUMQdEtHMmufwo-JwU-LULnZkxfNYUAakw1KcMtVU7QUIWHWlNkogcwQkgoBk8c7gckioY8GkzqOoCchcjgJk5LSkQQUQM0d3EcGcstUff9XrUwWzEIZktc8QV0pPD3GcnU87YXQRW7cXKcBUswbqIXHcXc10IXQXXU+oukxc38hAKcykw6Wk+cv80C6AE2MAEPSgp0NlPkjnaI27KYmCUIL2ZcDwOgSoVoGgcACk-AU1PgaSNcF5EgRY+wIQT6MYv+WYoYndRadKR4cCO4eitCK4+i3peRbQeiugRizi2mUINCdixOHi5i1iviwSq4YSvigSnYEhfEkcgi6kIixqSEAg+QGECioY0zCXJEmi09Oivi7ipiqS0StWI4jiriyS0y6SvSyyzYfikyuy8S2y6gfi3pBrfEqhZHH4omFCYnUAcCMQfUSFBQH5EBf9L44cIwecH5ZM6MzuOMlnLEGUGsSiaiKgGbNETAIwYAQKxQKFUK1ycK3gqKkQGKrCgsugiQcAZQKwOQbCmq1Ncs8g6SJ+IgDogQBSWqgJSi2mCXLGTStSS4C2XBEaLS-Eh4sE54jaQ7d49GCK34y01MkEx4qICEhwKEhmNAuEhE0YzSxaNYgAfUWIpBllWOTh1k6vkHsrOpTgGm2Lcp0H8vtMLkuq7DyhfhRFavateuUjTDWBvnGKaSZnCAwHuLAFBKeJeJmo+PmukD+OTKtOBIhtWshOhK2vhMzF2pFxWH2vOoJiOoGNOt2LxrVMkCuusv1jZQvX0lepvSxiBp1hBsNj7KBGZMXQcDdzyL511NSnYO-LZu1QcFIphHasNSUpIoj3UsmPav5pXJu3MAcBUsIKEFFqmHFqVrUvIulroNlp50FsploEmB3XAgVuFpVroLQmNoVo1pBnNrX0GC+roJ+s+kVtUttpls2RnwaIVuvFxwVC0Njh4OHBICjhjhUGC0pAEGmx1nTU2HrDi3DywBRF0PwzpUlqECFADqgNAzFkXzYwkA4xRTVGj29QkF9VdHjhgLdqIM1W-WMDj3VCzrFG-xY2TOgMUGhRIK3w3Js0OiVDIqdiDzxqkCYHBCFG8vJA7F1BWACqCvysMEKtdAiuiGiv+LitjJAESsxGStStHnStlEyuytyuCoEAKtDyXuKqsFKv+KaquyXnPJHEOinDoBfvYy+xNo5rS0GBfs5AcANrblhzvi1EpCkHxBzCmFKj0zbMpHAiIFiimDfqBH0BRCnDfroC1GoCxEmAmCmBYkAfeEwBQbfvAmGDQnwcmVAZ5HAYxFQYLpoHAmwB-kRXodIb3AAPEFUCnA8GBA8EGFKh3EcyZpQrGR90IcnTQYwaoCwZwamGGHIbcEocRnoJoeIcYYEbCAiHZVly5S0bAYEGlggrHLArAoAsIBnIcGAqJOMZMYoIdAfvG3MZ7LvmgYxFFLFnMC4Z8HUYwFUy0GftfrodNP-snyfCnCIG-oCdoHUbyBAusb-PJMKt8hRR1k6LnKsbid-L4BcecMNTvmlAUBVWAF4dcFrDr0KdKcZShJoA8BQGcxQBcYbPwE2K1AUlagAAkoVcnXB7AM8BAVUOmFBmxmEBAam6mGnj9mmOrhnyYiYZVt4MAemtQVUZm68hnWpimMAFAe5b53hcdln8o1gEUtnQ1ccamMBQhLdUCiB-BwJ3hDFfgNm3BbwZCznQAf5XBIESBb4LmPnYEW8Nprn-LLa6mTyNzHzCt2QtA75S7TBCs1cz0nmfBKMBAwxnDHN8m+nl4wxGnJnWmBABn9H3gKnAWixsWJmz88WVnGU9M8hIhohYWV4bcUhRMt8+zanlzrtQUCChBNzxsczWgnHNG1FFBAWZzXBYmmgMAXHKQphtzvH+k3AAA9GRKVlIKSAltxgQDxjwDFjweV9AJVlV0AaVqlmVmPbV4lvVvTA1jAZVoVtaEV-wcx0uQsBwcV9JyV41tVjEdq3I81zxq191qKO11VmwI8NAhWzVhWsta1sAQ1+1jER1js9koNrgVwaV31tMP8GCBB3UfbSkakrho4Xhm8Lx2N4No16VglrNt-ZRwK-N5N4cotvCvh3V-VuN21mRdN712BmtwwYYP3TN4uftv3at4dgQAdjEU1vtid+BgIPN5bdUzsTUugJ2KcW5qKQtjwYtncS3DALd4tvhrxvdxt7Vw9m8Ntk9g9ltm8S13d1wMh8t+N7tsN0zP15TAIflLhjF3wRZkZ+9qKGsflAlrhv9vVk9oQL9jwYl3wY5ugU5gDtwBgflU1rhuDhDk9zFmE6N6D2ZogcD94YwflBwMJ311tqFAjzd-lId7NgQb9ijxDjATEYDqFGd0D4cLUSjtwTAaj8d4YLhmD9DsULj-QYYFDvDmd-jjwK5kTz3ajwoyTgTvD2T9AP47C0kScEE3ygmT5yGQKKUA+hUQjbRY8Q7d4OQcTuvItv5gFlTsANTkEu0TTowVCEKdyfOLyKVOUIzt-HaaQUzn0twCzqdvD6z0KWThwFjhQRT28Dj-9sINFL1sNp9ztvgUIIAA
350
+ *
351
+ * @see
352
+ * - https://stackoverflow.com/q/75531366/1938970: fix for Type instantiation is excessively deep and possibly infinite in Promise.all
353
+ *
354
+ * @notes
355
+ * I might take a better look at how things were done in [i18next](https://github.com/i18next/i18next/blob/master/index.d.ts)
356
+ */
357
+
358
+ type MergeDictionaries<T, K> = Omit<T, keyof K> & K;
359
+
360
+ type Join<A, Sep extends string = "", R extends string = ""> = A extends [
361
+ infer First,
362
+ ...infer Rest,
363
+ ]
364
+ ? Join<
365
+ Rest,
366
+ Sep,
367
+ R extends "" ? \`\${First & string}\` : \`\${R}\${Sep}\${First & string}\`
368
+ >
369
+ : R;
370
+
371
+ type BuildRecursiveJoin<TList, TSeparator extends string> = Exclude<
372
+ TList extends [...infer ButLast, unknown]
373
+ ? Join<ButLast, TSeparator> | BuildRecursiveJoin<ButLast, TSeparator>
374
+ : never,
375
+ ""
376
+ >;
377
+
378
+ type JoinObjectPath<S1, S2> = S1 extends string
379
+ ? S2 extends string
380
+ ? \`\${S1}.\${S2}\`
381
+ : S1
382
+ : never;
383
+
384
+ /**
385
+ * A very simplified version of \`type-fest\`'s \`Get\` type
386
+ */
387
+ type GetWithPath<BaseType, Keys extends readonly string[]> = Keys extends []
388
+ ? BaseType
389
+ : Keys extends readonly [infer Head, ...infer Tail]
390
+ ? GetWithPath<
391
+ Head extends keyof BaseType ? BaseType[Head] : unknown,
392
+ Extract<Tail, string[]>
393
+ >
394
+ : never;
395
+
396
+ /**
397
+ * A very simplified version of \`type-fest\`'s \`Get\` type, its implementation
398
+ * of square brackets doesn't match our convention as we always keep dots
399
+ * around the brackets and the brackets do not mean dynamic object access but
400
+ * just a dynamic portion of a route.
401
+ */
402
+ type Get<BaseType, Path extends string | readonly string[]> = GetWithPath<
403
+ BaseType,
404
+ Path extends string ? Split<Path, "."> : Path
405
+ >;
406
+
407
+ /**
408
+ *
409
+ */
410
+ type TranslationsDictionaryDefault = {
411
+ // "~": any;
412
+ "~": {
413
+ test: string;
414
+ static: string;
415
+ dynamic: {
416
+ static: string;
417
+ "[key]": {
418
+ index: string;
419
+ "[id]": string;
420
+ };
421
+ };
422
+ };
423
+ };
424
+
425
+ /**
426
+ * Dictionary of all the translations
427
+ *
428
+ * It must uses \`~\` as namespace for routes defintions to make the \`to\` and \`useTo\`
429
+ * working
430
+ */
431
+ export type TranslationsDictionary = MergeDictionaries<
432
+ TranslationsDictionaryDefault,
433
+ I18n.Translations
434
+ >;
435
+
436
+ /**
437
+ * Namespaces should match the filenames were translations are defined
438
+ */
439
+ export type TranslateNamespace = Extract<keyof TranslationsDictionary, string>;
440
+
441
+ /**
442
+ * Translation **value** found at a specific _path_ in the given _namespace_
443
+ *
444
+ * \`TPath\` can be any of all possible paths:
445
+ * - \`plusKey\` sub dictionaries within a namespace
446
+ * - \`plusKey.nested\` whatever nested level of nesting within a namespace
447
+ */
448
+ export type TranslationAtPathFromNamespace<
449
+ TNamespace extends TranslateNamespace,
450
+ TPath extends TranslationsPaths<TranslationsDictionary[TNamespace]>,
451
+ > = TNamespace extends TranslateNamespace
452
+ ? TPath extends string // TranslationsPaths<TranslationsDictionary[TNamespace]>
453
+ ? Get<TranslationsDictionary[TNamespace], TPath>
454
+ : TranslationsDictionary[TNamespace]
455
+ : never;
456
+
457
+ /**
458
+ * The generic type passed and to use with {@link TranslationAtPath} when you
459
+ * want to build a type extending that
460
+ */
461
+ export type TranslationAtPathGeneric =
462
+ | TranslateNamespace
463
+ | TranslationsAllPaths;
464
+
465
+ /**
466
+ * Translation **value** found at a _path_
467
+ *
468
+ * \`TPath\` can be any of all possible paths begininng with a namespace:
469
+ * - \`namespace\` only a namespace
470
+ * - \`namespace:plusKey\` sub dictionaries within a namespace
471
+ * - \`namespace:plusKey.nested\` whatever nested level of nesting
472
+ */
473
+ export type TranslationAtPath<TPath extends TranslationAtPathGeneric> =
474
+ TPath extends TranslateNamespace
475
+ ? TranslationsDictionary[TPath]
476
+ : TPath extends \`\${infer Namespace}:\${infer Path}\`
477
+ ? Namespace extends TranslateNamespace
478
+ ? Get<TranslationsDictionary[Namespace], Path>
479
+ : never
480
+ : never;
481
+
482
+ /**
483
+ * All translations paths from the given _path_
484
+ *
485
+ * \`TPath\` can be any of all possible paths begininng with a namespace:
486
+ * - \`namespace\` only a namespace
487
+ * - \`namespace:plusKey\` sub dictionaries within a namespace
488
+ * - \`namespace:plusKey.nested\` whatever nested level of nesting
489
+ */
490
+ export type TranslationPathsFrom<TPath extends TranslationAtPathGeneric> =
491
+ TPath extends TranslateNamespace
492
+ ? TranslationsPaths<TranslationsDictionary[TPath]>
493
+ : TPath extends \`\${infer Namespace}:\${infer Path}\`
494
+ ? Namespace extends TranslateNamespace
495
+ ? Get<TranslationsDictionary[Namespace], Path> extends object
496
+ ? TranslationsPaths<Get<TranslationsDictionary[Namespace], Path>>
497
+ : TranslationsPaths<TranslationsDictionary[Namespace]>
498
+ : never
499
+ : never;
500
+
501
+ /**
502
+ * All translations _paths_ of the given one, e.g. if \`TPath\` would be
503
+ * \`"dashboard.users.[id].edit"\` the generated type would be the union
504
+ * \`"dashboard.users.[id]" | "dashboard.users" | "dashboard"\`.
505
+ */
506
+ export type TranslationsPathsAncestors<
507
+ TPath extends string,
508
+ TSeparator extends string = ".",
509
+ > = BuildRecursiveJoin<Split<TPath, TSeparator>, TSeparator>;
510
+
511
+ /**
512
+ * Recursive mapped type to extract all usable string paths from a translation
513
+ * definition object (usually from a JSON file).
514
+ * It uses the \`infer\` "trick" to store the object in memory and prevent
515
+ * [infinite instantiation errors](https://stackoverflow.com/q/75531366/1938970)
516
+ */
517
+ export type TranslationsPaths<T, TAsObj extends boolean = true> = {
518
+ [K in Extract<keyof T, string>]: T[K] extends // exclude empty objects, empty arrays, empty strings
519
+ | Record<string, never>
520
+ | never[]
521
+ | ""
522
+ ? never
523
+ : // recursively manage objects
524
+ T[K] extends Record<string, unknown>
525
+ ?
526
+ | (TAsObj extends true ? \`\${K}\` : never) // this is to be able to use the "obj" shortcut
527
+ | JoinObjectPath<K, TranslationsPaths<T[K], TAsObj>>
528
+ : // allow primitives or array of primitives
529
+ // TODO: support array of objects recursively? For now we just stop at the array name
530
+ T[K] extends
531
+ | string
532
+ | number
533
+ | boolean
534
+ | Array<string | number | boolean | object>
535
+ ? \`\${K}\`
536
+ : // exclude anything else
537
+ never;
538
+ }[Extract<keyof T, string>] extends infer O
539
+ ? O
540
+ : never;
541
+
542
+ /**
543
+ * Recursive mapped type of all usable string paths from the whole translations
544
+ * dictionary. It uses the \`infer\` "trick" to store the object in memory and prevent
545
+ * [infinite instantiation errors](https://stackoverflow.com/q/75531366/1938970)
546
+ */
547
+ export type TranslationsAllPaths = {
548
+ [N in Extract<keyof TranslationsDictionary, string>]: {
549
+ [K in Extract<
550
+ keyof TranslationsDictionary[N],
551
+ string
552
+ >]: TranslationsDictionary[N][K] extends // exclude empty objects, empty arrays, empty strings
553
+ | Record<string, never>
554
+ | never[]
555
+ | ""
556
+ ? never
557
+ : // recursively manage objects
558
+ TranslationsDictionary[N][K] extends Record<string, unknown>
559
+ ?
560
+ | \`\${N}:\${K}\` // this is to be able to use the "obj" shortcut
561
+ | JoinObjectPath<
562
+ K extends string ? \`\${N}:\${K}\` : \`\${N}:\`,
563
+ TranslationsPaths<TranslationsDictionary[N][K]>
564
+ >
565
+ : // allow primitives or array of primitives
566
+ // TODO: support array of objects recursively? For now we just stop at the array name
567
+ TranslationsDictionary[N][K] extends
568
+ | string
569
+ | number
570
+ | boolean
571
+ | Array<string | number | boolean | object>
572
+ ? \`\${N}:\${K}\`
573
+ : // exclude anything else
574
+ never;
575
+ }[Extract<keyof TranslationsDictionary[N], string>];
576
+ }[Extract<keyof TranslationsDictionary, string>] extends infer O
577
+ ? O
578
+ : never;
579
+
580
+ /**
581
+ * Unlike in \`next-translate\` we allow passing some predefined arguments as
582
+ * shortcuts for common use cases:
583
+ * - \`"obj"\` as a shortcut for \`{ returnObjects: true }\`
584
+ * - \`""\` as a shortcut for \`{ fallback: "" }\`
585
+ *
586
+ */
587
+ export type TranslationShortcut = "obj" | "";
588
+
589
+ /**
590
+ * Query object to populate the returned translated string interpolating data
591
+ * or a TranslationShortcut.
592
+ *
593
+ * NOTE: when using a shortcut passing TranslationOptions to \`t()\` is not supported
594
+ * TODO: type safe this behaviour of the third argument (options).
595
+ */
596
+ export type TranslationQuery =
597
+ | undefined
598
+ | null
599
+ | TranslationShortcut
600
+ | {
601
+ [key: string]: string | number | boolean;
602
+ };
603
+
604
+ /**
605
+ * Opions of the translate function or a TranslationShortcut.
606
+ *
607
+ * NOTE: when using a shortcut passing TranslationOptions to \`t()\` is not supported
608
+ * TODO: type safe this behaviour of the third argument (options).
609
+ */
610
+ export type TranslationOptions =
611
+ | undefined
612
+ | TranslationShortcut
613
+ | {
614
+ returnObjects?: boolean;
615
+ fallback?: string | string[];
616
+ default?: string;
617
+ };
618
+
619
+ /**
620
+ * Translate function which optionally accept a namespace as first argument
621
+ */
622
+ export type Translate<
623
+ TNamespace extends TranslateNamespace | undefined = TranslateNamespace,
624
+ > = TNamespace extends TranslateNamespace
625
+ ? TranslateNamespaced<TNamespace>
626
+ : TranslateDefault;
627
+
628
+ /**
629
+ * Translate function **without** namespace, it allows to select any of the all
630
+ * available strings in _all_ namespaces.
631
+ */
632
+ export type TranslateDefault = <
633
+ TPath extends TranslationsAllPaths,
634
+ TReturn = TranslationAtPath<TPath>,
635
+ >(
636
+ s: TPath,
637
+ q?: TranslationQuery,
638
+ o?: TranslationOptions,
639
+ ) => TReturn;
640
+
641
+ /**
642
+ * Translate function **with** namespace, it allows to select all available
643
+ * strings _only_ in the given namespace.
644
+ */
645
+ export type TranslateNamespaced<TNamespace extends TranslateNamespace> = <
646
+ TPath extends TranslationsPaths<TranslationsDictionary[TNamespace]>,
647
+ TReturn = TranslationAtPathFromNamespace<TNamespace, TPath>,
648
+ >(
649
+ s: TPath,
650
+ q?: TranslationQuery,
651
+ o?: TranslationOptions,
652
+ ) => TReturn;
653
+
654
+ /**
655
+ * Translate function _loose_ type, to use only in implementations that uses
656
+ * the \`t\` function indirectly without needng knowledge of the string it needs
657
+ * to output.
658
+ */
659
+ export type TranslateLoose<TReturn = string> = (
660
+ s?: any,
661
+ q?: TranslationQuery,
662
+ o?: TranslationOptions,
663
+ ) => TReturn;
664
+
665
+ /**
666
+ * Translate function _loosest_ type it allows to return string or object or array
667
+ * or whatever basically.
668
+ */
669
+ export type TranslateLoosest<TReturn = any> = (
670
+ s?: any,
671
+ q?: TranslationQuery,
672
+ o?: TranslationOptions,
673
+ ) => TReturn;
674
+
331
675
 
332
676
  // export type RouteId = keyof typeof routesSlim;
333
677
  export type RouteId = RouteIdStatic | RouteIdDynamic;
334
678
 
335
679
  // export type RouteIdStatic = I18n.Utils.RouteStrictIdStatic<RouteId>;
336
- export type RouteIdStatic = ${n};
680
+ export type RouteIdStatic = ${s};
337
681
 
338
682
  // export type RouteIdDynamic = I18n.Utils.RouteStrictIdDynamic<RouteId>;
339
- export type RouteIdDynamic = ${l};
683
+ export type RouteIdDynamic = ${a};
340
684
 
341
685
  /**
342
686
  * Utility standalone type to extract all children routes that starts with the
@@ -355,14 +699,109 @@ ${i}
355
699
  > = T extends \`\${TStarts}.\${infer First}\` ? \`\${TStarts}.\${First}\` : never;
356
700
  }
357
701
 
358
- export namespace I18n.RouteParams {${s}}
702
+ export namespace I18n.RouteParams {${n}}
359
703
 
360
704
  export namespace I18n.Utils {
361
- ${a}
705
+ // import type { Trim } from "@koine/utils";
706
+ type Whitespace = \`\u{20}\`;
707
+ type TrimLeft<V extends string> = V extends \`\${Whitespace}\${infer R}\`
708
+ ? TrimLeft<R>
709
+ : V;
710
+ type TrimRight<V extends string> = V extends \`\${infer R}\${Whitespace}\`
711
+ ? TrimRight<R>
712
+ : V;
713
+ /** A simplified version of type-fest's Trim which creates too complex types */
714
+ type Trim<V extends string> = TrimLeft<TrimRight<V>>;
715
+
716
+ type WithDynamicPortion<F extends string, D extends GenericDelimiters> =
717
+ | \`\${string}\${F}\${D["start"]}\${string}\${D["end"]}\${F}\${string}\`
718
+ | \`\${string}\${F}\${D["start"]}\${string}\${D["end"]}\`
719
+ | \`\${D["start"]}\${string}\${D["end"]}\${F}\${string}\`
720
+ | \`\${D["start"]}\${string}\${D["end"]}\`;
721
+
722
+ type FilterOnlyStaticRouteId<
723
+ T extends string,
724
+ F extends string,
725
+ D extends GenericDelimiters,
726
+ > = T extends WithDynamicPortion<F, D> ? never : T;
727
+
728
+ type FilterOnlyDynamicRouteId<
729
+ T extends string,
730
+ F extends string,
731
+ D extends GenericDelimiters,
732
+ > = T extends WithDynamicPortion<F, D> ? T : never;
733
+
734
+ type InferredSeparator<T extends string> = T extends \`\${string}.\${string}\`
735
+ ? "."
736
+ : T extends \`\${string}/\${string}\`
737
+ ? "/"
738
+ : never;
739
+
740
+ type GenericDelimiters = {
741
+ start: string;
742
+ end: string;
743
+ };
744
+
745
+ type WithDelimitedString<Start extends string, End extends string> =
746
+ | \`\${string}\${Start}\${string}\${End}\${string}\`
747
+ | \`\${string}\${Start}\${string}\${End}\`
748
+ | \`\${Start}\${string}\${End}\${string}\`
749
+ | \`\${Start}\${string}\${End}\`;
750
+
751
+ type InferredDelimiters<T extends string> =
752
+ T extends WithDelimitedString<"[", "]">
753
+ ? { start: "["; end: "]" }
754
+ : T extends WithDelimitedString<"{{", "}}">
755
+ ? { start: "{{"; end: "}}" }
756
+ : T extends WithDelimitedString<"{", "}">
757
+ ? { start: "{"; end: "}" }
758
+ : never;
759
+
760
+ /**
761
+ * @borrows [awesome-template-literal-types](https://github.com/ghoullier/awesome-template-literal-types)
762
+ */
763
+ // prettier-ignore
764
+ export type DynamicParams<
765
+ T extends string,
766
+ Separator extends string = InferredSeparator<T>,
767
+ Delimiters extends GenericDelimiters = InferredDelimiters<T>,
768
+ > = string extends T
769
+ ? Record<string, string>
770
+ : T extends \`\${string}\${Delimiters["start"]}\${infer Param}\${Delimiters["end"]}\${Separator}\${infer Rest}\`
771
+ ? {
772
+ [k in Trim<Param> | keyof DynamicParams<Rest>]: string | number;
773
+ }
774
+ : T extends \`\${string}\${Delimiters["start"]}\${infer Param}\${Delimiters["end"]}\`
775
+ ? {
776
+ [k in Trim<Param>]: string | number;
777
+ }
778
+ // : T extends \`\${Delimiters["start"]}\${infer Param}\${Delimiters["end"]}\`
779
+ // ? {
780
+ // [k in Trim<Param>]: string | number;
781
+ // }
782
+ // eslint-disable-next-line @typescript-eslint/ban-types
783
+ : {};
784
+
785
+ export type RouteStrictIdStatic<T extends string> = FilterOnlyStaticRouteId<
786
+ T,
787
+ InferredSeparator<T>,
788
+ InferredDelimiters<T>
789
+ >;
790
+
791
+ export type RouteStrictIdDynamic<T extends string> = FilterOnlyDynamicRouteId<
792
+ T,
793
+ InferredSeparator<T>,
794
+ InferredDelimiters<T>
795
+ >;
796
+
797
+ export type RouteStrictId<T extends string> =
798
+ | RouteStrictIdStatic<T>
799
+ | RouteStrictIdDynamic<T>;
800
+
362
801
  }
363
802
  `});
364
803
 
365
- var e$2 = (()=>({files:[{name:"config",fn:e$4,ext:"ts",index:!0},{name:"defaultLocale",fn:t$3,ext:"ts",index:!0},{name:"deriveLocalisedPathnames",fn:o$2,ext:"ts",index:!0},{name:"isLocale",fn:n$4,ext:"ts",index:!0},{name:"locales",fn:m$4,ext:"ts",index:!0},{name:"pathnameToRouteId",fn:a$3,ext:"ts",index:!0},{name:"routes",fn:r$3,ext:"ts",index:!0},{name:"routesSlim",fn:i$1,ext:"ts",index:!0},{name:"tFns",fn:s$1,ext:"ts"},{name:"tInterpolateParams",fn:f$1,ext:"ts",index:!1},{name:"to",fn:x$1,ext:"ts",index:!0},{name:"toFns",fn:d$2,ext:"ts",index:!0},{name:"toFormat",fn:p,ext:"ts",index:!0},{name:"types",fn:l$1,ext:"ts",index:!0}]}));
804
+ var e$2 = (()=>({files:[{name:"config",fn:e$4,ext:"ts",index:!0},{name:"defaultLocale",fn:t$3,ext:"ts",index:!0},{name:"deriveLocalisedPathnames",fn:o$2,ext:"ts",index:!0},{name:"isLocale",fn:n$4,ext:"ts",index:!0},{name:"locales",fn:m$4,ext:"ts",index:!0},{name:"pathnameToRouteId",fn:a$3,ext:"ts",index:!0},{name:"routes",fn:r$3,ext:"ts",index:!0},{name:"routesSlim",fn:i$1,ext:"ts",index:!0},{name:"tFns",fn:s$1,ext:"ts"},{name:"tInterpolateParams",fn:f,ext:"ts",index:!1},{name:"to",fn:x$1,ext:"ts",index:!0},{name:"toFns",fn:d$2,ext:"ts",index:!0},{name:"toFormat",fn:p$1,ext:"ts",index:!0},{name:"types",fn:l$1,ext:"ts",index:!0}]}));
366
805
 
367
806
  var e$1 = (()=>`
368
807
  "use client";
@@ -4,16 +4,16 @@ import { join, dirname, basename, extname, sep } from 'node:path';
4
4
  import { glob } from 'glob';
5
5
  import { f as formatRoutePathname } from './formatRoutePathname.esm.js';
6
6
  import { fsWrite } from '@koine/node';
7
- import { readFileSync, rmSync } from 'node:fs';
7
+ import { rmSync } from 'node:fs';
8
8
  import * as t$4 from 'typescript';
9
9
 
10
10
  const dataFsConfig={cwd:process.cwd(),ignore:[]};let r$4=(e={})=>mergeObjects({...dataFsConfig},e),i$4=e=>{let{cwd:o,ignore:a}=e;return glob.sync("*",{cwd:o,withFileTypes:!0,ignore:[...a,"node_modules/**"]}).filter(e=>e.isDirectory()).map(e=>e.relative()).sort((e,o)=>e.localeCompare(o))};let getDataFs=async a=>{let s=r$4(a),l=i$4(s),n=[];return await Promise.all(l.map(async a=>{let r=await glob("**/*.json",{cwd:join(s.cwd,a),ignore:s.ignore});await Promise.all(r.map(async t=>{let r=join(s.cwd,a,t),i=await readFile(r,"utf8");i&&n.push({path:t,data:JSON.parse(i),locale:a});}));})),{localesFolders:l,translationFiles:n}};
11
11
 
12
- const dataSourceRoutesConfig={translationJsonFileName:"~.json",tokens:{parentReference:"^",idDelimiter:".",pathnameWildcard:"*"}};let i$3=e=>e.replace(/\.index$/,""),o$4=e=>formatRoutePathname(e.replace(/\*/g,"").replace(/[[{]{1,2}(.*?)[\]}]{1,2}/g,(e,t)=>`[${t.trim()}]`)),l$3=r=>capitalize(changeCaseCamel(r)),m$6=(e,t,r,a)=>{let n=t[a].pathnames[r];if(n.startsWith(`/${e.source.routes.tokens.parentReference}`)){let s=RegExp(`^\\/\\${e.source.routes.tokens.parentReference}`);n=n.replace(s,"");let i=a.split(".").slice(0,-1).join(".");if(i)n=m$6(e,t,r,i)+n;else throw Error("Used a parent route token reference without a matching parent route")}return n},p$3=(e,t)=>{forin(t,(a,n)=>{forin(n.pathnames,r=>{t[a].pathnames[r]=m$6(e,t,r,a);});});},c=e=>{let t=e.match(/\[.*?\]/g);if(t)return t.map(e=>e.slice(1,-1).trim()).reduce((e,t)=>(e[t]="stringOrNumber",e),{})},h$1=(e,t)=>{let{defaultLocale:r,locales:a}=e;for(let e in t){let s=t[e].pathnames,i=s[r],o={};for(let e in s){let t=s[e];t!==i&&(o[e]=t);}Object.keys(o).length===a.length-1||(Object.keys(o).length>=1?(o[r]=i,t[e].optimizedPathnames=objectSortByKeysMatching(o,r)):t[e].optimizedPathnames=i);}};let getDataSourceRoutes=(e,{translationFiles:t})=>{let{defaultLocale:r}=e,s=[],m={};for(let p=0;p<t.length;p++){let{path:h,locale:u,data:d}=t[p];if(h===e.source.routes.translationJsonFileName){let t=objectFlat(d,e.source.routes.tokens.idDelimiter);for(let a in t){let p=t[a],h=i$3(a);if(!m[h]){m[h]=m[h]||{};let t=l$3(h),r=c(h),a=p.includes(e.source.routes.tokens.pathnameWildcard);m[h].id=h,m[h].typeName=t,r&&(m[h].params=r),a&&(m[h].wildcard=!0,s.push(h));}m[h].pathnames=m[h].pathnames||{},m[h].pathnames[u]=o$4(p),m[h].pathnames=objectSortByKeysMatching(m[h].pathnames,r);}}}if(p$3(e,m),h$1(e,m),s.length)for(let e in m)s.some(t=>e.startsWith(t)&&t!==e)&&(m[e].inWildcard=!0);return Object.fromEntries(Object.entries(m).sort())};
12
+ const dataSourceRoutesConfig={translationJsonFileName:"~.json",tokens:{parentReference:"^",idDelimiter:".",pathnameWildcard:"*"}};let i$3=e=>e.replace(/\.index$/,""),o$4=e=>formatRoutePathname(e.replace(/\*/g,"").replace(/[[{]{1,2}(.*?)[\]}]{1,2}/g,(e,t)=>`[${t.trim()}]`)),l$3=r=>capitalize(changeCaseCamel(r)),m$6=(e,t,r,a)=>{let n=t[a].pathnames[r];if(n.startsWith(`/${e.source.routes.tokens.parentReference}`)){let s=RegExp(`^\\/\\${e.source.routes.tokens.parentReference}`);n=n.replace(s,"");let i=a.split(".").slice(0,-1).join(".");if(i)n=m$6(e,t,r,i)+n;else throw Error("Used a parent route token reference without a matching parent route")}return n},p$4=(e,t)=>{forin(t,(a,n)=>{forin(n.pathnames,r=>{t[a].pathnames[r]=m$6(e,t,r,a);});});},c=e=>{let t=e.match(/\[.*?\]/g);if(t)return t.map(e=>e.slice(1,-1).trim()).reduce((e,t)=>(e[t]="stringOrNumber",e),{})},h$2=(e,t)=>{let{defaultLocale:r,locales:a}=e;for(let e in t){let s=t[e].pathnames,i=s[r],o={};for(let e in s){let t=s[e];t!==i&&(o[e]=t);}Object.keys(o).length===a.length-1||(Object.keys(o).length>=1?(o[r]=i,t[e].optimizedPathnames=objectSortByKeysMatching(o,r)):t[e].optimizedPathnames=i);}};let getDataSourceRoutes=(e,{translationFiles:t})=>{let{defaultLocale:r}=e,s=[],m={};for(let p=0;p<t.length;p++){let{path:h,locale:u,data:d}=t[p];if(h===e.source.routes.translationJsonFileName){let t=objectFlat(d,e.source.routes.tokens.idDelimiter);for(let a in t){let p=t[a],h=i$3(a);if(!m[h]){m[h]=m[h]||{};let t=l$3(h),r=c(h),a=p.includes(e.source.routes.tokens.pathnameWildcard);m[h].id=h,m[h].typeName=t,r&&(m[h].params=r),a&&(m[h].wildcard=!0,s.push(h));}m[h].pathnames=m[h].pathnames||{},m[h].pathnames[u]=o$4(p),m[h].pathnames=objectSortByKeysMatching(m[h].pathnames,r);}}}if(p$4(e,m),h$2(e,m),s.length)for(let e in m)s.some(t=>e.startsWith(t)&&t!==e)&&(m[e].inWildcard=!0);return Object.fromEntries(Object.entries(m).sort())};
13
13
 
14
14
  let s$2=["zero","one","two","few","many","other"],a$4="other";let isPluralSuffix=e=>s$2.includes(e)||isNumericLiteral(e);let removePluralSuffix=e=>{let[l]=splitReverse(e,"_");return l?e.replace(`_${l}`,""):e};let isPluralKey=e=>{let[l]=splitReverse(e,"_");return isPluralSuffix(l)};let i$2=e=>{let l={};return e.forEach(e=>{let[r]=split(e,"_");l[r]=l[r]||[],l[r].push(e);}),l};let transformKeysForPlurals=l=>{let r=l.filter(isPluralKey);if(r.length){let t=[...l];return forin(i$2(r),(e,r)=>{l.includes(e)||t.push(e),r.forEach(e=>{l.includes(e)&&(t=t.filter(l=>l!==e));});}),t}return l};let hasPlurals=e=>Object.keys(e).includes(a$4);let hasOnlyPluralKeys=e=>!!hasPlurals(e)&&0===pickNonPluralKeys(e).length;let pickNonPluralKeys=e=>Object.keys(e).filter(e=>!isPluralSuffix(e));let pickNonPluralValue=e=>hasPlurals(e)?objectPick(e,pickNonPluralKeys(e)):e;
15
15
 
16
- const dataSourceTranslationsConfig={dynamicDelimiters:{start:"{{",end:"}}"},fnsAsDataSources:!0,fnsPrefix:"",createArrayIndexBasedFns:!1};let u=RegExp(sep,"g"),p$2=e=>{let t=e.replace(/~/g,"$").replace(/-/g,"_").replace(u,"_").replace(/_+/g,"_").replace(/[^a-zA-Z0-9_$]/gi,"");return /^[0-9]/.test(t)?"$"+t:t},f$2=(e,t)=>{if(isString(t)){let{start:r,end:a}=e.source.translations.dynamicDelimiters,l=RegExp(`${r}(.*?)${a}`,"gm"),s=t.match(l);if(s)return s.map(e=>e.replace(r,"").replace(a,"").trim()).reduce((e,t)=>(e[t]="stringOrNumber",e),{})}},m$5=(e,t)=>(Object.keys(t).filter(isPluralKey).forEach(e=>{let r=removePluralSuffix(e);t[r]&&(t[r].plural=!0),delete t[e];}),t),d$3=(e,t,r,a,l)=>{if(isPrimitive(a)){let s=f$2(e,a);l[t]=l[t]||{},l[t].values=l[t].values||{},l[t].values[r]=a,l[t].typeValue="Primitive",s&&(l[t].params=s);}else {if(e.source.translations.fnsAsDataSources){let e=isArray(a)?"Array":"Object";l[t]=l[t]||{},l[t].values=l[t].values||{},l[t].values[r]=a,l[t].typeValue=e;}if(isArray(a)){if(e.source.translations.createArrayIndexBasedFns)for(let s=0;s<a.length;s++)d$3(e,t+"_"+s,r,a[s],l);}else for(let s in a)d$3(e,t+"_"+p$2(s),r,a[s],l);}return l},g=(l,s,n)=>{let{locale:i,path:o}=s,c=join(dirname(o),basename(o,extname(o)));for(let e in s.data){let t=s.data[e];d$3(l,p$2(c+(e?"_"+e:"")),i,t,n);}return n};let getDataSourceTranslations=(e,{translationFiles:t})=>{let r={};for(let a=0;a<t.length;a++)t[a].path!==e.source.routes.translationJsonFileName&&g(e,t[a],r);return Object.fromEntries(Object.entries(r=m$5(e,r)).sort())};
16
+ const dataSourceTranslationsConfig={dynamicDelimiters:{start:"{{",end:"}}"},fnsAsDataSources:!0,fnsPrefix:"",createArrayIndexBasedFns:!1};let u=RegExp(sep,"g"),p$3=e=>{let t=e.replace(/~/g,"$").replace(/-/g,"_").replace(u,"_").replace(/_+/g,"_").replace(/[^a-zA-Z0-9_$]/gi,"");return /^[0-9]/.test(t)?"$"+t:t},f$1=(e,t)=>{if(isString(t)){let{start:r,end:a}=e.source.translations.dynamicDelimiters,l=RegExp(`${r}(.*?)${a}`,"gm"),s=t.match(l);if(s)return s.map(e=>e.replace(r,"").replace(a,"").trim()).reduce((e,t)=>(e[t]="stringOrNumber",e),{})}},m$5=(e,t)=>(Object.keys(t).filter(isPluralKey).forEach(e=>{let r=removePluralSuffix(e);t[r]&&(t[r].plural=!0),delete t[e];}),t),d$3=(e,t,r,a,l)=>{if(isPrimitive(a)){let s=f$1(e,a);l[t]=l[t]||{},l[t].values=l[t].values||{},l[t].values[r]=a,l[t].typeValue="Primitive",s&&(l[t].params=s);}else {if(e.source.translations.fnsAsDataSources){let e=isArray(a)?"Array":"Object";l[t]=l[t]||{},l[t].values=l[t].values||{},l[t].values[r]=a,l[t].typeValue=e;}if(isArray(a)){if(e.source.translations.createArrayIndexBasedFns)for(let s=0;s<a.length;s++)d$3(e,t+"_"+s,r,a[s],l);}else for(let s in a)d$3(e,t+"_"+p$3(s),r,a[s],l);}return l},g$1=(l,s,n)=>{let{locale:i,path:o}=s,c=join(dirname(o),basename(o,extname(o)));for(let e in s.data){let t=s.data[e];d$3(l,p$3(c+(e?"_"+e:"")),i,t,n);}return n};let getDataSourceTranslations=(e,{translationFiles:t})=>{let r={};for(let a=0;a<t.length;a++)t[a].path!==e.source.routes.translationJsonFileName&&g$1(e,t[a],r);return Object.fromEntries(Object.entries(r=m$5(e,r)).sort())};
17
17
 
18
18
  const dataSourceConfig={routes:dataSourceRoutesConfig,translations:dataSourceTranslationsConfig};let getDataSource=(t,r)=>({routes:getDataSourceRoutes(t,r),translations:getDataSourceTranslations(t,r)});
19
19
 
@@ -170,7 +170,7 @@ export default routesSlim;
170
170
 
171
171
  let dataParamsToTsInterfaceBody=e=>Object.keys(e).reduce((r,t)=>{let a=e[t],s="";switch(a){case"number":s="number";break;case"string":s="string";break;default:s="string | number";}return r.push(`${t}: ${s};`),r},[]).join(" ");
172
172
 
173
- let i=e=>isString(e)||isNumber(e)?`"${e}"`:isBoolean(e)?`${e}`:isArray(e)?JSON.stringify(e):`(${JSON.stringify(e)})`,p$1=(t,r)=>areEqual(t,r),m$3=(e,r)=>{let{defaultLocale:o}=e,l="";return forin(r,(e,t)=>{e===o||p$1(t,r[o])||(l+=`locale === "${e}" ? ${i(t)} : `);}),l+=i(r[o])};var s$1 = (({config:e,data:r})=>{let o=`
173
+ let i=e=>isString(e)||isNumber(e)?`"${e}"`:isBoolean(e)?`${e}`:isArray(e)?JSON.stringify(e):`(${JSON.stringify(e)})`,p$2=(t,r)=>areEqual(t,r),m$3=(e,r)=>{let{defaultLocale:o}=e,l="";return forin(r,(e,t)=>{e===o||p$2(t,r[o])||(l+=`locale === "${e}" ? ${i(t)} : `);}),l+=i(r[o])};var s$1 = (({config:e,data:r})=>{let o=`
174
174
  /* eslint-disable @typescript-eslint/no-unused-vars */
175
175
  /* eslint-disable prefer-const */
176
176
  import type { I18n } from "./types";
@@ -179,7 +179,7 @@ import { tInterpolateParams } from "./tInterpolateParams";
179
179
  `;return forin(r.source.translations,(t,{values:r,params:l,plural:s})=>{let p=`${e.source.translations.fnsPrefix}${t}`;l&&s&&(l.count="number");let $=[l?`params: { ${dataParamsToTsInterfaceBody(l)} }`:"","locale?: I18n.Locale"].filter(Boolean).join(", ");o+=`export let ${p} = (${$}) => `;let f="";isPrimitive(r)?f+=i(r):f+=m$3(e,r),f=l?`tInterpolateParams(${f}, params);`:`${f};`,o+=f+`
180
180
  `;}),o});
181
181
 
182
- let e$3=e=>e.split("").map(e=>`\\${e}`).join("");var f$1 = (({config:a})=>{let{start:t,end:r}=a.source.translations.dynamicDelimiters;return `
182
+ let e$3=e=>e.split("").map(e=>`\\${e}`).join("");var f = (({config:a})=>{let{start:t,end:r}=a.source.translations.dynamicDelimiters;return `
183
183
  export let tInterpolateParams = (
184
184
  value: string,
185
185
  params?: object,
@@ -231,7 +231,7 @@ import type { I18n } from "./types";
231
231
  `;return forin(a.source.routes,(t,{typeName:a,pathnames:n,params:p})=>{let s=`to_${changeCaseCamel(t)}`,i=`I18n.RouteParams.${a}`,u=[p?`params: ${i}`:"",m?"":"locale?: I18n.Locale"].filter(Boolean).join(", "),c=m?'""':"locale",f=p?", params":"";$+=`export let ${s} = (${u}) => `,isString(n)?$+=`toFormat(${c}, "${n}"${f});`:$+=`toFormat(${c}, ${r$2(l,n)}${f});`,$+=`
232
232
  `;}),$});
233
233
 
234
- var p = (({config:a})=>`
234
+ var p$1 = (({config:a})=>`
235
235
  export function toFormat(
236
236
  locale: string | undefined,
237
237
  pathname: string,
@@ -288,33 +288,377 @@ export function toFormat(
288
288
  export default toFormat;
289
289
  `);
290
290
 
291
- let d$1=(e,t)=>{if(!isArray(t)&&isObject(t)){if(hasOnlyPluralKeys(t))return `'${e}': string;`;if(hasPlurals(t))return `'${e}': string | ${f(pickNonPluralValue(t))}`}return `'${e}': ${f(t)}`},f=e=>{let t="",r="";if(isBoolean(e)?r="boolean":isString(e)&&(r="string"),r)t+=r+";";else if(e){if(isArray(e)){let r=e[0];t+=`${f(r)}[];`;}else if(isObject(e)){t+="{";let r=transformKeysForPlurals(Object.keys(e));for(let o=0;o<r.length;o++){let s=r[o],i=e[s]||"";t+=d$1(s,i);}t+="};";}}else t+="";return (t=t.replace(/;\[\];/g,"[];")).replace(/;+/g,";")},y=(e,t)=>{let{translationFiles:r}=t,{defaultLocale:o}=e,s=r.filter(e=>e.locale===o),i=`
291
+ let p=(t,n)=>{if(!isArray(n)&&isObject(n)){if(hasOnlyPluralKeys(n))return `'${t}': string;`;if(hasPlurals(n))return `'${t}': string | ${d$1(pickNonPluralValue(n))}`}return `'${t}': ${d$1(n)}`},d$1=t=>{let i="",r="";if(isBoolean(t)?r="boolean":isString(t)&&(r="string"),r)i+=r+";";else if(t){if(isArray(t)){let e=t[0];i+=`${d$1(e)}[];`;}else if(isObject(t)){i+="{";let e=transformKeysForPlurals(Object.keys(t));for(let n=0;n<e.length;n++){let s=e[n],a=t[s]||"";i+=p(s,a);}i+="};";}}else i+="";return (i=i.replace(/;\[\];/g,"[];")).replace(/;+/g,";")},h$1=(t,e)=>{let{translationFiles:n}=e,{defaultLocale:s}=t,a=n.filter(t=>t.locale===s),i=`
292
292
  export interface Translations {
293
- `;for(let e=0;e<s.length;e++){let{path:t,data:r}=s[e],o=t.replace(".json","");i+=`"${o}": ${f(r)}
293
+ `;for(let t=0;t<a.length;t++){let{path:e,data:n}=a[t],s=e.replace(".json","");i+=`"${s}": ${d$1(n)}
294
294
  `;}return i+`
295
295
  }
296
- `},m$2=e=>{let t="\n";return forin(e,(e,{typeName:r,params:o})=>{o&&(t+=` export interface ${r} { ${dataParamsToTsInterfaceBody(o)} }
297
- `);}),t},$=(e,t)=>Object.keys(e).filter(r=>t(r,e[r])).sort().map(e=>`"${e}"`).join(" | ");var l$1 = (({config:r,data:o})=>{let s=m$2(o.source.routes),i=readFileSync(join(__dirname,"../../types.ts"),"utf-8"),a=readFileSync(join(__dirname,"../../types-utils.ts"),"utf-8"),n=$(o.source.routes,(e,t)=>!t.params),l=$(o.source.routes,(e,t)=>!!t.params);return `
296
+ `},m$2=e=>{let n="\n";return forin(e,(t,{typeName:e,params:s})=>{s&&(n+=` export interface ${e} { ${dataParamsToTsInterfaceBody(s)} }
297
+ `);}),n},g=(t,e)=>Object.keys(t).filter(n=>e(n,t[n])).sort().map(t=>`"${t}"`).join(" | ");var l$1 = (({config:t,data:e})=>{let n=m$2(e.source.routes),s=g(e.source.routes,(t,e)=>!e.params),a=g(e.source.routes,(t,e)=>!!e.params);return `
298
298
  /* eslint-disable @typescript-eslint/no-namespace */
299
299
  /* eslint-disable @typescript-eslint/no-explicit-any */
300
300
  /* eslint-disable @typescript-eslint/ban-types */
301
301
 
302
302
  export namespace I18n {
303
- export type Locale = ${r.locales.map(e=>`"${e}"`).join(" | ")};
303
+ export type Locale = ${t.locales.map(t=>`"${t}"`).join(" | ")};
304
304
 
305
305
  export type LocalesMap<T = any> = Record<Locale, T>;
306
306
 
307
- ${y(r,o.fs)}
308
- ${i}
307
+ ${h$1(t,e.fs)}
308
+
309
+ // import type {
310
+ // // Get,
311
+ // Split,
312
+ // } from "@koine/utils";
313
+
314
+ type Split<
315
+ S extends string,
316
+ Delimiter extends string,
317
+ > = S extends \`\${infer Head}\${Delimiter}\${infer Tail}\`
318
+ ? [Head, ...Split<Tail, Delimiter>]
319
+ : S extends Delimiter
320
+ ? []
321
+ : [S];
322
+
323
+ /**
324
+ * @file
325
+ *
326
+ * Test this on TSplayground:
327
+ * https://www.typescriptlang.org/play?#code/PTAEFpK6dv4YpSBQJSgJYEYAcA7YAFwE8AHTfAcwGcA6AEzqJrTGQ86+7hRQYCmAYwA2AQwBOA0PjEBbATTJih0gJJ58oAN4oMlIgIkAzFdIAqEsfhriimAPY2dejKHQAiAH4eAXKGsSAG5XAF8+fXxDEzNQS2tbMXsnGgBhJ2NMKh1w8LYIHkKi4sh8rE1ickVmVnQS+obUFFIyaQApB0oAHgBlbAAaUB6AJgA+UABeIexQAQAPQ3wGGlAaIglKKlcAfiHh2YWBJZW1jepXDF2AAwASbT7QujuR0KuL0H8+1398AQA3IwhNAAKmBemBoAAIpghMlZBISKAHMYAiIRKAiAALaTrBJ2Rw2cHAFDzMgOCREDFVOJWGz4lLQ2EEySIqYaAh0eJ0pIEmhA4Cg8GgABy8kUylUJ0xDgAriIGKA5EkhJiMdjQJkREcxSsAO5GHG0xJwlaSaSCTK-BhEklzMkUqmtGl4pICUUKJSxKYAUQWVlhXQA1gISMjndyTYy4SzBqdNqN+YLQBCAErCGUSGiYAGKsRkVoKlo4hwHXGw1HomU0MQAIy1q3Wm1AyixK2MEgccgCGKN9PwQotlEwcKRNYAVsJKQAKKsysRoxHtzvdto9ADywo1mC1AEo6EK1JSq4o1dIrpRjEYrqAPI2hIGPBiS2tyTj1Q5x5OsFoFHJyYjrAVMgpABKIhQAbQvIdDG-NZrHsHknFmCQO0zABdKdMSIIgyBoXwQDg+8HABEwRAcXU6CETtgAAR2AAB2ABWRiAGZsBYgA2DjgGwABOFjcF4+iAAYdxtUlyUpItw2NXkAAUkkxGgulccwUHGKZdAwcCAGlv1AX0yyIIMQzDcxY0bahRjQ3x3nQeZRBlQRZjkMhSFHCdYRoQYBFc9zJCsEhvJctzETjahWDcOJdLQ0sjmWUA0yoiQGC6cKqEGX4SPGAAfGR-iMcDYryjxH12LKjDssApCEDMswBERESVWQqGkD9PJYd5-HMGK4uORLhHJVL0sGGV8EDfByPwcZdneDA8tubQdNedwwCxTAVg2p9QBraRa3rIgS2PU8b3ax8aGlClaqIObQDyjpuh0wYuVklIFNbLoep0tDRlGKqK3I5sNjkYdsxPckAhQsRETDYDMBB+wAUitxut6+ZFgS9K7pkGU5F2iRsZrBwHC1axsYAQShkg0ss7I8vwXH8cJ4nSem0BrjuZa3ii+y5kc5zAnW6hZhEGgBC6-KSJCUJwMM-1jODUMUXMhszioay+oSi8jFANd2d1j5JcBPgBTBZMBtqzMwdzfMBELakw3nStqzraQsZbJSNQ7LssWkXVpQO3tEMJc2GBhaMEToUBD1AY8Vl90Bz3wS8JGvW8NnvR9DobV8Tvar9KEVXz-wCJYgYKsDzcg5PoOkSg4KiTBg+Q1CaAwrCcLwgiiBUQNiKMYwyIoqi5FohjmLYzjuL4gShNE8S7Ukx0LCDk1ybRd7Pc01xwM3Qu5ZUBXTOV1feSjZkEQstXrP8LS3F0-SD4DRWzNPhlw4vkhd7Q-p0pv27eb82kL5UKHlJzBRAf5KmEC-JhVpsjKKL0+w0HPk4Fk380aHH6klIaNM1aZQKhIXKRsJBFWxqVfWFUJAAOqumK2DUmrWDEK1MBXlbrdTfjYVB8Iv7CjQpgjGKwcEpTwZsUa41Jq6jZtsW6UUFp3GFKEXwnMVroHWpteOJZdoBFdttY6CcPBnVWJdIg11ZFuHup0fAXQ9Lo3iicWm+tFqKOUUtFa-hnFKKuM9ThNBN7KSQcHFBH80EIgwd9X6NCAa6iBvDUGSMkQEwCtDJEKI4YIzBgglGMlkHcPQXwgR9jVZNnpozHWeUiYkwEGTPKlNAqiOFqUvG5Sdos2qdIxOCilEqO5lFVaBwgGlxIELbIAhRbiz6YbKhYRZZ+kPiZJWOSgl5LCXwq+8Y0LS1mUZBZr8XSRhCTw9ZVlYp2P6trAmetdh6x+IQxMZsIQAFV8AiEwMGfSVxfgLHALiCMAhrz6gCAwICYgaBZmFjQTs0hgJ2wEJaO2kMqC4yOCwAIkUIQXUktdNsEMR5-i0MdIQoLFC2XNuAROhjxweGvKC7smKroykpMYCGVxtCgCkEQDM+A1yfi8v4dYMppCvCFOSq4pUaWmmMVixlGoWVstMGiGsvd-AUOFcmBe9opLUkCXCHoJjrqTFOlS8hHh7lCgAIqCoRKwqSJYyRkDlK6E6HKuUIt+caBFWMDBGDJPiYWDAkhiCFBDMQSzdX6sZfudV5thRrnMN6fw-sjix3BdkUN9LTEyuUGCpsOqCRrjcrybaVwiBTh3NeLak1KQ0BlPmSSdshTmDXJCNc-LqTVkvGqLau1MRiD+I4DMqSTrrRSoi5FURQBTgcIWlIe4NVL2knmpwlqjCslcHlMag4rTrpxmiHdS78B6ulTdeaLhJngUVv4dKNlimNJxs0gmFS2nWBCFFUIZrzYFqLWGBO7q7DSGMGNJkSEQ1hoJEehlRAo0PJFHGhNoAk0EtTXSiNlJs3IYPQWk0xbS3lqwCsKtqxa2aobebJtLa21Og7W+btAhe39tlATH96oR0KkkEihQE6p0zpsHO5MxIJIOkXZwrDRaJg7s3XCygpHsYHog5mk92M75RRdRIblvKWDbH8JU1mr6+kKpEEq+8Wnb101M0VPTUULRiDlEQEz6VLOgHfSbJMEIdUAaAyOf2MJVTTujAuAIQhVBuW7LID0Eo9ptkwJmSk7Hx0nuBAJxeQntWrwEJ9d04pYhnISu5zLnpVDY0k-ChgGlVL5Yi5rFYeWdQRfeLsGr4WzCpXMBVswf0oocP2QISEcKbMiCIB+tzaWNSeYJMmYEuphzSkZaCGQtWzCDGHNEzRqwxlfkCEOhOTshR9rENufabt4H6SdvNprkpoNJc1cvMNPW+u2cNZ9NMnK1OGr-lOVweEwMpHXiIfx-RXA0RMweldl9XAOGByJnjrAdyTHGOYZ7XKhu3dG-gYDWhQRTaxHNsLWXVBLdi2iciq2xZanLE4RqJ1Tt7YO67IU6VNpaATlQMGWhccFYEJd2013hPdba6oFr-PgFYNy2loXGlQAqQwAjgQL2tBTHSupD7GAvsHr8YpAJviVlf1awt1QP0AcYCB11iMBJQckEN0iSH+z83Q5QLDiY8PEdqeR+51H6PQBkQcGLZegxs7HQp4iQu8MyBak4z3bDWIkgpsUEKBOJbryAbRyOSgYcapEEp1jmblJfh22FhNciWoGAsOY0dtWWAc8CDtui7asocKRvnSlp07mAAyxMfdTGV6sEzgRLfG++-gc3luIcm9etyu3DvxgOZczBgAQoiKi+BQIjg7Iyk8W7QYpEhnXJn6orheDoGOSF+BE-bh3ydMiRKtQ0GANoS-84BChGJObDsDgmUk0EBIKO5hMRbWklIGFMWCdQhREMkHNOsREXHLbdULdBFVfQwAAchr3v1eTFhBSxCgKnArgQ2m1AAYBIDCxhGbEkl5D40S25wXVS26wYBTDr2kCmDV38U+i10OXQW8A8B+mR0eXsFeXcgbgDTIl+Bu2znRnlgrEnSVDmAxF1BLC1AajwIEDckxFhxVG3AYCkC0HgNj3Nij2rR7gpD1FwOZ1Z1M2g0bV-w0Rj0A3RGzlaiknVBrRrC0JWEdi0DzFDxhGbjNDEB8joCoCjlO0cOcODRRFDQDQuiJkkAYFGmQ2Wyz1JRgyuCSLj2pEhFBUxEiJSloLXxWAYLSxoLoLSF-3lA0LXGMC6A8HCIyIcCiI8ATCFCSO5nIMEy1Wb3yOyMMCKLUNKPKNUh6H0NRRywcXwVUiq1M0NXcwKLX3UgmLGMWnMH6MkBYEeDuAuVAAADFos1gVjtA1iehBolg1V9ZNiYtsZFoTjtinh7gDiGAjj-B7JbADBwAw4XYtRwAvkiBwBXkhCAABIsGgIQDYNycARQb4ogYASacAMaY8BgcAP4SQZGcwOYu4BYgYmgHYtYi4ogI43YLE74EhfkdgRoYkkk8AMoHAAgYAOw8wGofIUk+k+oPgFom7AAcVlyRKmClziCFzGMazx2kA3SWCkytENWK2k2tFGC7xQIEHs1pkt3Zwi2ByF3tzh1ADkm9g2nS3cwyz1wEF+mR1BFjXjVBG6hLCVHeX+MBMwBCxkIkEDBkBhDGURGxCkFzHeRrVdJENkDpyriuCkEhREABDaGPwAFkHAGA5R-l25sJcJ8JgBdQEzmAqgASgSM9rB-DyQqBiAASMgsgABif0kmIM0M8MyMsSc2QuUMQdEtHMmufwo-JwU-LULnZkxfNYUAakw1KcMtVU7QUIWHWlNkogcwQkgoBk8c7gckioY8GkzqOoCchcjgJk5LSkQQUQM0d3EcGcstUff9XrUwWzEIZktc8QV0pPD3GcnU87YXQRW7cXKcBUswbqIXHcXc10IXQXXU+oukxc38hAKcykw6Wk+cv80C6AE2MAEPSgp0NlPkjnaI27KYmCUIL2ZcDwOgSoVoGgcACk-AU1PgaSNcF5EgRY+wIQT6MYv+WYoYndRadKR4cCO4eitCK4+i3peRbQeiugRizi2mUINCdixOHi5i1iviwSq4YSvigSnYEhfEkcgi6kIixqSEAg+QGECioY0zCXJEmi09Oivi7ipiqS0StWI4jiriyS0y6SvSyyzYfikyuy8S2y6gfi3pBrfEqhZHH4omFCYnUAcCMQfUSFBQH5EBf9L44cIwecH5ZM6MzuOMlnLEGUGsSiaiKgGbNETAIwYAQKxQKFUK1ycK3gqKkQGKrCgsugiQcAZQKwOQbCmq1Ncs8g6SJ+IgDogQBSWqgJSi2mCXLGTStSS4C2XBEaLS-Eh4sE54jaQ7d49GCK34y01MkEx4qICEhwKEhmNAuEhE0YzSxaNYgAfUWIpBllWOTh1k6vkHsrOpTgGm2Lcp0H8vtMLkuq7DyhfhRFavateuUjTDWBvnGKaSZnCAwHuLAFBKeJeJmo+PmukD+OTKtOBIhtWshOhK2vhMzF2pFxWH2vOoJiOoGNOt2LxrVMkCuusv1jZQvX0lepvSxiBp1hBsNj7KBGZMXQcDdzyL511NSnYO-LZu1QcFIphHasNSUpIoj3UsmPav5pXJu3MAcBUsIKEFFqmHFqVrUvIulroNlp50FsploEmB3XAgVuFpVroLQmNoVo1pBnNrX0GC+roJ+s+kVtUttpls2RnwaIVuvFxwVC0Njh4OHBICjhjhUGC0pAEGmx1nTU2HrDi3DywBRF0PwzpUlqECFADqgNAzFkXzYwkA4xRTVGj29QkF9VdHjhgLdqIM1W-WMDj3VCzrFG-xY2TOgMUGhRIK3w3Js0OiVDIqdiDzxqkCYHBCFG8vJA7F1BWACqCvysMEKtdAiuiGiv+LitjJAESsxGStStHnStlEyuytyuCoEAKtDyXuKqsFKv+KaquyXnPJHEOinDoBfvYy+xNo5rS0GBfs5AcANrblhzvi1EpCkHxBzCmFKj0zbMpHAiIFiimDfqBH0BRCnDfroC1GoCxEmAmCmBYkAfeEwBQbfvAmGDQnwcmVAZ5HAYxFQYLpoHAmwB-kRXodIb3AAPEFUCnA8GBA8EGFKh3EcyZpQrGR90IcnTQYwaoCwZwamGGHIbcEocRnoJoeIcYYEbCAiHZVly5S0bAYEGlggrHLArAoAsIBnIcGAqJOMZMYoIdAfvG3MZ7LvmgYxFFLFnMC4Z8HUYwFUy0GftfrodNP-snyfCnCIG-oCdoHUbyBAusb-PJMKt8hRR1k6LnKsbid-L4BcecMNTvmlAUBVWAF4dcFrDr0KdKcZShJoA8BQGcxQBcYbPwE2K1AUlagAAkoVcnXB7AM8BAVUOmFBmxmEBAam6mGnj9mmOrhnyYiYZVt4MAemtQVUZm68hnWpimMAFAe5b53hcdln8o1gEUtnQ1ccamMBQhLdUCiB-BwJ3hDFfgNm3BbwZCznQAf5XBIESBb4LmPnYEW8Nprn-LLa6mTyNzHzCt2QtA75S7TBCs1cz0nmfBKMBAwxnDHN8m+nl4wxGnJnWmBABn9H3gKnAWixsWJmz88WVnGU9M8hIhohYWV4bcUhRMt8+zanlzrtQUCChBNzxsczWgnHNG1FFBAWZzXBYmmgMAXHKQphtzvH+k3AAA9GRKVlIKSAltxgQDxjwDFjweV9AJVlV0AaVqlmVmPbV4lvVvTA1jAZVoVtaEV-wcx0uQsBwcV9JyV41tVjEdq3I81zxq191qKO11VmwI8NAhWzVhWsta1sAQ1+1jER1js9koNrgVwaV31tMP8GCBB3UfbSkakrho4Xhm8Lx2N4No16VglrNt-ZRwK-N5N4cotvCvh3V-VuN21mRdN712BmtwwYYP3TN4uftv3at4dgQAdjEU1vtid+BgIPN5bdUzsTUugJ2KcW5qKQtjwYtncS3DALd4tvhrxvdxt7Vw9m8Ntk9g9ltm8S13d1wMh8t+N7tsN0zP15TAIflLhjF3wRZkZ+9qKGsflAlrhv9vVk9oQL9jwYl3wY5ugU5gDtwBgflU1rhuDhDk9zFmE6N6D2ZogcD94YwflBwMJ311tqFAjzd-lId7NgQb9ijxDjATEYDqFGd0D4cLUSjtwTAaj8d4YLhmD9DsULj-QYYFDvDmd-jjwK5kTz3ajwoyTgTvD2T9AP47C0kScEE3ygmT5yGQKKUA+hUQjbRY8Q7d4OQcTuvItv5gFlTsANTkEu0TTowVCEKdyfOLyKVOUIzt-HaaQUzn0twCzqdvD6z0KWThwFjhQRT28Dj-9sINFL1sNp9ztvgUIIAA
328
+ *
329
+ * @see
330
+ * - https://stackoverflow.com/q/75531366/1938970: fix for Type instantiation is excessively deep and possibly infinite in Promise.all
331
+ *
332
+ * @notes
333
+ * I might take a better look at how things were done in [i18next](https://github.com/i18next/i18next/blob/master/index.d.ts)
334
+ */
335
+
336
+ type MergeDictionaries<T, K> = Omit<T, keyof K> & K;
337
+
338
+ type Join<A, Sep extends string = "", R extends string = ""> = A extends [
339
+ infer First,
340
+ ...infer Rest,
341
+ ]
342
+ ? Join<
343
+ Rest,
344
+ Sep,
345
+ R extends "" ? \`\${First & string}\` : \`\${R}\${Sep}\${First & string}\`
346
+ >
347
+ : R;
348
+
349
+ type BuildRecursiveJoin<TList, TSeparator extends string> = Exclude<
350
+ TList extends [...infer ButLast, unknown]
351
+ ? Join<ButLast, TSeparator> | BuildRecursiveJoin<ButLast, TSeparator>
352
+ : never,
353
+ ""
354
+ >;
355
+
356
+ type JoinObjectPath<S1, S2> = S1 extends string
357
+ ? S2 extends string
358
+ ? \`\${S1}.\${S2}\`
359
+ : S1
360
+ : never;
361
+
362
+ /**
363
+ * A very simplified version of \`type-fest\`'s \`Get\` type
364
+ */
365
+ type GetWithPath<BaseType, Keys extends readonly string[]> = Keys extends []
366
+ ? BaseType
367
+ : Keys extends readonly [infer Head, ...infer Tail]
368
+ ? GetWithPath<
369
+ Head extends keyof BaseType ? BaseType[Head] : unknown,
370
+ Extract<Tail, string[]>
371
+ >
372
+ : never;
373
+
374
+ /**
375
+ * A very simplified version of \`type-fest\`'s \`Get\` type, its implementation
376
+ * of square brackets doesn't match our convention as we always keep dots
377
+ * around the brackets and the brackets do not mean dynamic object access but
378
+ * just a dynamic portion of a route.
379
+ */
380
+ type Get<BaseType, Path extends string | readonly string[]> = GetWithPath<
381
+ BaseType,
382
+ Path extends string ? Split<Path, "."> : Path
383
+ >;
384
+
385
+ /**
386
+ *
387
+ */
388
+ type TranslationsDictionaryDefault = {
389
+ // "~": any;
390
+ "~": {
391
+ test: string;
392
+ static: string;
393
+ dynamic: {
394
+ static: string;
395
+ "[key]": {
396
+ index: string;
397
+ "[id]": string;
398
+ };
399
+ };
400
+ };
401
+ };
402
+
403
+ /**
404
+ * Dictionary of all the translations
405
+ *
406
+ * It must uses \`~\` as namespace for routes defintions to make the \`to\` and \`useTo\`
407
+ * working
408
+ */
409
+ export type TranslationsDictionary = MergeDictionaries<
410
+ TranslationsDictionaryDefault,
411
+ I18n.Translations
412
+ >;
413
+
414
+ /**
415
+ * Namespaces should match the filenames were translations are defined
416
+ */
417
+ export type TranslateNamespace = Extract<keyof TranslationsDictionary, string>;
418
+
419
+ /**
420
+ * Translation **value** found at a specific _path_ in the given _namespace_
421
+ *
422
+ * \`TPath\` can be any of all possible paths:
423
+ * - \`plusKey\` sub dictionaries within a namespace
424
+ * - \`plusKey.nested\` whatever nested level of nesting within a namespace
425
+ */
426
+ export type TranslationAtPathFromNamespace<
427
+ TNamespace extends TranslateNamespace,
428
+ TPath extends TranslationsPaths<TranslationsDictionary[TNamespace]>,
429
+ > = TNamespace extends TranslateNamespace
430
+ ? TPath extends string // TranslationsPaths<TranslationsDictionary[TNamespace]>
431
+ ? Get<TranslationsDictionary[TNamespace], TPath>
432
+ : TranslationsDictionary[TNamespace]
433
+ : never;
434
+
435
+ /**
436
+ * The generic type passed and to use with {@link TranslationAtPath} when you
437
+ * want to build a type extending that
438
+ */
439
+ export type TranslationAtPathGeneric =
440
+ | TranslateNamespace
441
+ | TranslationsAllPaths;
442
+
443
+ /**
444
+ * Translation **value** found at a _path_
445
+ *
446
+ * \`TPath\` can be any of all possible paths begininng with a namespace:
447
+ * - \`namespace\` only a namespace
448
+ * - \`namespace:plusKey\` sub dictionaries within a namespace
449
+ * - \`namespace:plusKey.nested\` whatever nested level of nesting
450
+ */
451
+ export type TranslationAtPath<TPath extends TranslationAtPathGeneric> =
452
+ TPath extends TranslateNamespace
453
+ ? TranslationsDictionary[TPath]
454
+ : TPath extends \`\${infer Namespace}:\${infer Path}\`
455
+ ? Namespace extends TranslateNamespace
456
+ ? Get<TranslationsDictionary[Namespace], Path>
457
+ : never
458
+ : never;
459
+
460
+ /**
461
+ * All translations paths from the given _path_
462
+ *
463
+ * \`TPath\` can be any of all possible paths begininng with a namespace:
464
+ * - \`namespace\` only a namespace
465
+ * - \`namespace:plusKey\` sub dictionaries within a namespace
466
+ * - \`namespace:plusKey.nested\` whatever nested level of nesting
467
+ */
468
+ export type TranslationPathsFrom<TPath extends TranslationAtPathGeneric> =
469
+ TPath extends TranslateNamespace
470
+ ? TranslationsPaths<TranslationsDictionary[TPath]>
471
+ : TPath extends \`\${infer Namespace}:\${infer Path}\`
472
+ ? Namespace extends TranslateNamespace
473
+ ? Get<TranslationsDictionary[Namespace], Path> extends object
474
+ ? TranslationsPaths<Get<TranslationsDictionary[Namespace], Path>>
475
+ : TranslationsPaths<TranslationsDictionary[Namespace]>
476
+ : never
477
+ : never;
478
+
479
+ /**
480
+ * All translations _paths_ of the given one, e.g. if \`TPath\` would be
481
+ * \`"dashboard.users.[id].edit"\` the generated type would be the union
482
+ * \`"dashboard.users.[id]" | "dashboard.users" | "dashboard"\`.
483
+ */
484
+ export type TranslationsPathsAncestors<
485
+ TPath extends string,
486
+ TSeparator extends string = ".",
487
+ > = BuildRecursiveJoin<Split<TPath, TSeparator>, TSeparator>;
488
+
489
+ /**
490
+ * Recursive mapped type to extract all usable string paths from a translation
491
+ * definition object (usually from a JSON file).
492
+ * It uses the \`infer\` "trick" to store the object in memory and prevent
493
+ * [infinite instantiation errors](https://stackoverflow.com/q/75531366/1938970)
494
+ */
495
+ export type TranslationsPaths<T, TAsObj extends boolean = true> = {
496
+ [K in Extract<keyof T, string>]: T[K] extends // exclude empty objects, empty arrays, empty strings
497
+ | Record<string, never>
498
+ | never[]
499
+ | ""
500
+ ? never
501
+ : // recursively manage objects
502
+ T[K] extends Record<string, unknown>
503
+ ?
504
+ | (TAsObj extends true ? \`\${K}\` : never) // this is to be able to use the "obj" shortcut
505
+ | JoinObjectPath<K, TranslationsPaths<T[K], TAsObj>>
506
+ : // allow primitives or array of primitives
507
+ // TODO: support array of objects recursively? For now we just stop at the array name
508
+ T[K] extends
509
+ | string
510
+ | number
511
+ | boolean
512
+ | Array<string | number | boolean | object>
513
+ ? \`\${K}\`
514
+ : // exclude anything else
515
+ never;
516
+ }[Extract<keyof T, string>] extends infer O
517
+ ? O
518
+ : never;
519
+
520
+ /**
521
+ * Recursive mapped type of all usable string paths from the whole translations
522
+ * dictionary. It uses the \`infer\` "trick" to store the object in memory and prevent
523
+ * [infinite instantiation errors](https://stackoverflow.com/q/75531366/1938970)
524
+ */
525
+ export type TranslationsAllPaths = {
526
+ [N in Extract<keyof TranslationsDictionary, string>]: {
527
+ [K in Extract<
528
+ keyof TranslationsDictionary[N],
529
+ string
530
+ >]: TranslationsDictionary[N][K] extends // exclude empty objects, empty arrays, empty strings
531
+ | Record<string, never>
532
+ | never[]
533
+ | ""
534
+ ? never
535
+ : // recursively manage objects
536
+ TranslationsDictionary[N][K] extends Record<string, unknown>
537
+ ?
538
+ | \`\${N}:\${K}\` // this is to be able to use the "obj" shortcut
539
+ | JoinObjectPath<
540
+ K extends string ? \`\${N}:\${K}\` : \`\${N}:\`,
541
+ TranslationsPaths<TranslationsDictionary[N][K]>
542
+ >
543
+ : // allow primitives or array of primitives
544
+ // TODO: support array of objects recursively? For now we just stop at the array name
545
+ TranslationsDictionary[N][K] extends
546
+ | string
547
+ | number
548
+ | boolean
549
+ | Array<string | number | boolean | object>
550
+ ? \`\${N}:\${K}\`
551
+ : // exclude anything else
552
+ never;
553
+ }[Extract<keyof TranslationsDictionary[N], string>];
554
+ }[Extract<keyof TranslationsDictionary, string>] extends infer O
555
+ ? O
556
+ : never;
557
+
558
+ /**
559
+ * Unlike in \`next-translate\` we allow passing some predefined arguments as
560
+ * shortcuts for common use cases:
561
+ * - \`"obj"\` as a shortcut for \`{ returnObjects: true }\`
562
+ * - \`""\` as a shortcut for \`{ fallback: "" }\`
563
+ *
564
+ */
565
+ export type TranslationShortcut = "obj" | "";
566
+
567
+ /**
568
+ * Query object to populate the returned translated string interpolating data
569
+ * or a TranslationShortcut.
570
+ *
571
+ * NOTE: when using a shortcut passing TranslationOptions to \`t()\` is not supported
572
+ * TODO: type safe this behaviour of the third argument (options).
573
+ */
574
+ export type TranslationQuery =
575
+ | undefined
576
+ | null
577
+ | TranslationShortcut
578
+ | {
579
+ [key: string]: string | number | boolean;
580
+ };
581
+
582
+ /**
583
+ * Opions of the translate function or a TranslationShortcut.
584
+ *
585
+ * NOTE: when using a shortcut passing TranslationOptions to \`t()\` is not supported
586
+ * TODO: type safe this behaviour of the third argument (options).
587
+ */
588
+ export type TranslationOptions =
589
+ | undefined
590
+ | TranslationShortcut
591
+ | {
592
+ returnObjects?: boolean;
593
+ fallback?: string | string[];
594
+ default?: string;
595
+ };
596
+
597
+ /**
598
+ * Translate function which optionally accept a namespace as first argument
599
+ */
600
+ export type Translate<
601
+ TNamespace extends TranslateNamespace | undefined = TranslateNamespace,
602
+ > = TNamespace extends TranslateNamespace
603
+ ? TranslateNamespaced<TNamespace>
604
+ : TranslateDefault;
605
+
606
+ /**
607
+ * Translate function **without** namespace, it allows to select any of the all
608
+ * available strings in _all_ namespaces.
609
+ */
610
+ export type TranslateDefault = <
611
+ TPath extends TranslationsAllPaths,
612
+ TReturn = TranslationAtPath<TPath>,
613
+ >(
614
+ s: TPath,
615
+ q?: TranslationQuery,
616
+ o?: TranslationOptions,
617
+ ) => TReturn;
618
+
619
+ /**
620
+ * Translate function **with** namespace, it allows to select all available
621
+ * strings _only_ in the given namespace.
622
+ */
623
+ export type TranslateNamespaced<TNamespace extends TranslateNamespace> = <
624
+ TPath extends TranslationsPaths<TranslationsDictionary[TNamespace]>,
625
+ TReturn = TranslationAtPathFromNamespace<TNamespace, TPath>,
626
+ >(
627
+ s: TPath,
628
+ q?: TranslationQuery,
629
+ o?: TranslationOptions,
630
+ ) => TReturn;
631
+
632
+ /**
633
+ * Translate function _loose_ type, to use only in implementations that uses
634
+ * the \`t\` function indirectly without needng knowledge of the string it needs
635
+ * to output.
636
+ */
637
+ export type TranslateLoose<TReturn = string> = (
638
+ s?: any,
639
+ q?: TranslationQuery,
640
+ o?: TranslationOptions,
641
+ ) => TReturn;
642
+
643
+ /**
644
+ * Translate function _loosest_ type it allows to return string or object or array
645
+ * or whatever basically.
646
+ */
647
+ export type TranslateLoosest<TReturn = any> = (
648
+ s?: any,
649
+ q?: TranslationQuery,
650
+ o?: TranslationOptions,
651
+ ) => TReturn;
652
+
309
653
 
310
654
  // export type RouteId = keyof typeof routesSlim;
311
655
  export type RouteId = RouteIdStatic | RouteIdDynamic;
312
656
 
313
657
  // export type RouteIdStatic = I18n.Utils.RouteStrictIdStatic<RouteId>;
314
- export type RouteIdStatic = ${n};
658
+ export type RouteIdStatic = ${s};
315
659
 
316
660
  // export type RouteIdDynamic = I18n.Utils.RouteStrictIdDynamic<RouteId>;
317
- export type RouteIdDynamic = ${l};
661
+ export type RouteIdDynamic = ${a};
318
662
 
319
663
  /**
320
664
  * Utility standalone type to extract all children routes that starts with the
@@ -333,14 +677,109 @@ ${i}
333
677
  > = T extends \`\${TStarts}.\${infer First}\` ? \`\${TStarts}.\${First}\` : never;
334
678
  }
335
679
 
336
- export namespace I18n.RouteParams {${s}}
680
+ export namespace I18n.RouteParams {${n}}
337
681
 
338
682
  export namespace I18n.Utils {
339
- ${a}
683
+ // import type { Trim } from "@koine/utils";
684
+ type Whitespace = \`\u{20}\`;
685
+ type TrimLeft<V extends string> = V extends \`\${Whitespace}\${infer R}\`
686
+ ? TrimLeft<R>
687
+ : V;
688
+ type TrimRight<V extends string> = V extends \`\${infer R}\${Whitespace}\`
689
+ ? TrimRight<R>
690
+ : V;
691
+ /** A simplified version of type-fest's Trim which creates too complex types */
692
+ type Trim<V extends string> = TrimLeft<TrimRight<V>>;
693
+
694
+ type WithDynamicPortion<F extends string, D extends GenericDelimiters> =
695
+ | \`\${string}\${F}\${D["start"]}\${string}\${D["end"]}\${F}\${string}\`
696
+ | \`\${string}\${F}\${D["start"]}\${string}\${D["end"]}\`
697
+ | \`\${D["start"]}\${string}\${D["end"]}\${F}\${string}\`
698
+ | \`\${D["start"]}\${string}\${D["end"]}\`;
699
+
700
+ type FilterOnlyStaticRouteId<
701
+ T extends string,
702
+ F extends string,
703
+ D extends GenericDelimiters,
704
+ > = T extends WithDynamicPortion<F, D> ? never : T;
705
+
706
+ type FilterOnlyDynamicRouteId<
707
+ T extends string,
708
+ F extends string,
709
+ D extends GenericDelimiters,
710
+ > = T extends WithDynamicPortion<F, D> ? T : never;
711
+
712
+ type InferredSeparator<T extends string> = T extends \`\${string}.\${string}\`
713
+ ? "."
714
+ : T extends \`\${string}/\${string}\`
715
+ ? "/"
716
+ : never;
717
+
718
+ type GenericDelimiters = {
719
+ start: string;
720
+ end: string;
721
+ };
722
+
723
+ type WithDelimitedString<Start extends string, End extends string> =
724
+ | \`\${string}\${Start}\${string}\${End}\${string}\`
725
+ | \`\${string}\${Start}\${string}\${End}\`
726
+ | \`\${Start}\${string}\${End}\${string}\`
727
+ | \`\${Start}\${string}\${End}\`;
728
+
729
+ type InferredDelimiters<T extends string> =
730
+ T extends WithDelimitedString<"[", "]">
731
+ ? { start: "["; end: "]" }
732
+ : T extends WithDelimitedString<"{{", "}}">
733
+ ? { start: "{{"; end: "}}" }
734
+ : T extends WithDelimitedString<"{", "}">
735
+ ? { start: "{"; end: "}" }
736
+ : never;
737
+
738
+ /**
739
+ * @borrows [awesome-template-literal-types](https://github.com/ghoullier/awesome-template-literal-types)
740
+ */
741
+ // prettier-ignore
742
+ export type DynamicParams<
743
+ T extends string,
744
+ Separator extends string = InferredSeparator<T>,
745
+ Delimiters extends GenericDelimiters = InferredDelimiters<T>,
746
+ > = string extends T
747
+ ? Record<string, string>
748
+ : T extends \`\${string}\${Delimiters["start"]}\${infer Param}\${Delimiters["end"]}\${Separator}\${infer Rest}\`
749
+ ? {
750
+ [k in Trim<Param> | keyof DynamicParams<Rest>]: string | number;
751
+ }
752
+ : T extends \`\${string}\${Delimiters["start"]}\${infer Param}\${Delimiters["end"]}\`
753
+ ? {
754
+ [k in Trim<Param>]: string | number;
755
+ }
756
+ // : T extends \`\${Delimiters["start"]}\${infer Param}\${Delimiters["end"]}\`
757
+ // ? {
758
+ // [k in Trim<Param>]: string | number;
759
+ // }
760
+ // eslint-disable-next-line @typescript-eslint/ban-types
761
+ : {};
762
+
763
+ export type RouteStrictIdStatic<T extends string> = FilterOnlyStaticRouteId<
764
+ T,
765
+ InferredSeparator<T>,
766
+ InferredDelimiters<T>
767
+ >;
768
+
769
+ export type RouteStrictIdDynamic<T extends string> = FilterOnlyDynamicRouteId<
770
+ T,
771
+ InferredSeparator<T>,
772
+ InferredDelimiters<T>
773
+ >;
774
+
775
+ export type RouteStrictId<T extends string> =
776
+ | RouteStrictIdStatic<T>
777
+ | RouteStrictIdDynamic<T>;
778
+
340
779
  }
341
780
  `});
342
781
 
343
- var e$2 = (()=>({files:[{name:"config",fn:e$4,ext:"ts",index:!0},{name:"defaultLocale",fn:t$3,ext:"ts",index:!0},{name:"deriveLocalisedPathnames",fn:o$2,ext:"ts",index:!0},{name:"isLocale",fn:n$4,ext:"ts",index:!0},{name:"locales",fn:m$4,ext:"ts",index:!0},{name:"pathnameToRouteId",fn:a$3,ext:"ts",index:!0},{name:"routes",fn:r$3,ext:"ts",index:!0},{name:"routesSlim",fn:i$1,ext:"ts",index:!0},{name:"tFns",fn:s$1,ext:"ts"},{name:"tInterpolateParams",fn:f$1,ext:"ts",index:!1},{name:"to",fn:x$1,ext:"ts",index:!0},{name:"toFns",fn:d$2,ext:"ts",index:!0},{name:"toFormat",fn:p,ext:"ts",index:!0},{name:"types",fn:l$1,ext:"ts",index:!0}]}));
782
+ var e$2 = (()=>({files:[{name:"config",fn:e$4,ext:"ts",index:!0},{name:"defaultLocale",fn:t$3,ext:"ts",index:!0},{name:"deriveLocalisedPathnames",fn:o$2,ext:"ts",index:!0},{name:"isLocale",fn:n$4,ext:"ts",index:!0},{name:"locales",fn:m$4,ext:"ts",index:!0},{name:"pathnameToRouteId",fn:a$3,ext:"ts",index:!0},{name:"routes",fn:r$3,ext:"ts",index:!0},{name:"routesSlim",fn:i$1,ext:"ts",index:!0},{name:"tFns",fn:s$1,ext:"ts"},{name:"tInterpolateParams",fn:f,ext:"ts",index:!1},{name:"to",fn:x$1,ext:"ts",index:!0},{name:"toFns",fn:d$2,ext:"ts",index:!0},{name:"toFormat",fn:p$1,ext:"ts",index:!0},{name:"types",fn:l$1,ext:"ts",index:!0}]}));
344
783
 
345
784
  var e$1 = (()=>`
346
785
  "use client";
package/package.json CHANGED
@@ -2,8 +2,8 @@
2
2
  "name": "@koine/i18n",
3
3
  "sideEffects": false,
4
4
  "dependencies": {
5
- "@koine/node": "2.0.0-beta.43",
6
- "@koine/utils": "2.0.0-beta.43"
5
+ "@koine/node": "2.0.0-beta.44",
6
+ "@koine/utils": "2.0.0-beta.44"
7
7
  },
8
8
  "peerDependencies": {
9
9
  "glob": "^10.3.10",
@@ -47,5 +47,5 @@
47
47
  },
48
48
  "module": "./index.esm.js",
49
49
  "main": "./index.cjs.js",
50
- "version": "2.0.0-beta.43"
50
+ "version": "2.0.0-beta.44"
51
51
  }