javascriptsveltesvelte-store

How to wrap an existing readable store to add extra parameters?


I am new to Svelte. I'm trying to wrap a small wrapper around svelte-i18n to allow relative translation key.

This is how the library can be used:

<script>
import { _ } from 'svelte-i18n';
</script>

<p>{$_('absolute_key')}</p>

What I would like to achieve

<script>
import { tr } from "my_wrapper.js"

const _ = tr("base.path.relative");
</script>

<p>{$_('.relative_key')}</p>

Closest I was able to achieve is

// my_wrapper.js
import { _ } from 'svelte-i18n';

export const tr = (basePath) => (path, options = {}) => {
  let returnVal;
  if (path.startsWith(".")) {
    _.subscribe((t) => returnVal = t(`${basePath}${path}`, options));
  } else {
    _.subscribe((t) => returnVal = t(path, options));
  }
  return returnVal;
}

It kinda work but I have to omit the $ sign in {$_('.relative_key')}. Is there a way to keep its usage with a dollar sign as if I was using the default API?


Solution

  • I have implemented exactly that before, you just have to return a function from the store. Looks something like this:

    // better name for _
    import { format } from 'svelte-i18n';
    
    export function section(path) {
        const { subscribe } = derived(
            format,
            $format => (idOrMessage, options) => {
                if (typeof idOrMessage == 'string')
                    return $format(`${path}.${idOrMessage}`, options);
    
                return $format(
                    {
                        ...idOrMessage,
                        id: `${path}.${idOrMessage.id}`
                    },
                    options,
                );
            },
        );
    
        return {
            path,
            subscribe,
            // allows chaining
            section: (subPath) => section(`${path}.${subPath}`),
        }
    }
    

    Would recommend not including the joining . anywhere except the internals, it's pretty ugly otherwise.

    <script>
      // ...
      const s = section('section');
      const sub = s.section('subSection');
      // or
      const sub2 = section('section.subSection');
    </script>
    
    Should be equivalent:
    {$_('section.subSection.value')}
    {$s('subSection.value')}
    {$sub('value')}
    {$sub2('value')}
    

    REPL