NishuGoel / svelte-i18next

Internationalization for svelte framework. Based on i18next ecosystem
https://npmjs.com/package/svelte-i18next
MIT License
49 stars 11 forks source link

feature idea: Trans component #4

Open adrai opened 2 years ago

adrai commented 2 years ago

It would be awesome to have also something like a Trans component, like in react-i18next.

That would help to interpolate components/html elements...

<Trans i18nKey="test.key">
    Hi from <a href="https://www.i18next.com" target="_blank" rel="noopener">i18next</a> 👋
</Trans>
ralyodio commented 2 years ago

why would you want this? just curious.

adrai commented 2 years ago

why would you want this? just curious.

How would you interpolate a text like the one in my example?

i.e. english like this:

<div>
  Hi from <a href="https://www.i18next.com" target="_blank" rel="noopener">i18next</a> 👋
</div>

i.e. german like this:

<div>
  Ein freundliches 👋 "Hallo" von <a href="https://www.i18next.com" target="_blank" rel="noopener">i18next</a>
</div>

i.e. italian like this:

<div>
  <a href="https://www.i18next.com" target="_blank" rel="noopener">i18next</a> ti saluta 👋
</div>
CanRau commented 1 year ago

Yes please I'm in the need for the same thing in Svelte trying to add links to translations 🙏

coeursoft commented 3 months ago

I concur. Running into this issue with HTML and a on:click in the middle of the string. Not sure how to address this right now

coeursoft commented 2 months ago

I've come up with a quick solution to be improved

Translation component: (loc is the translation store)

<script lang="ts">
import { derived, readable } from 'svelte/store';
import { loc } from './store';
import { isString } from 'lodash-es';
import type { ComponentType, ComponentProps } from 'svelte';

export let key: string;

export let parts: Array<[ComponentType, ComponentProps, string]> = [];

const content = key
  ? derived(loc, (loc) => {
    const result = loc.t(key);
    const final = result && result !== key ? result : null;
    if (!final) {
      console.warn('No translation found for ' + key + ' - ');
    }
    return final ?? '';
  })
  : readable('');

const contentParts = derived(content, content => {
  if (!isString(content)) {
    return [];
  }
  if (!content?.trim()) {
    return [];
  }

  const regex = /(?<=@@\d+)|(?=@@\d+)/;
  const split = content.split(regex);
  const prefix = '@@';
  const replaced = split.map(part => {
    if (part.startsWith(prefix)) {
      const index = Number(part.replace(prefix, ''));
      if (isNaN(index)) {
        return part;
      }
      return parts[index];
    }
    return part;
  });

  return replaced.length > 0 ? replaced : [ content ];
});

</script>

{#if $contentParts.length}
  {#each $contentParts as part}
    {#if isString(part)}
      {part}
    {:else}
      <svelte:component this={part[0]} { ...part[1] }>{part[2]}</svelte:component>
    {/if}
  {/each}
{:else}
  <slot />
{/if}

Translation file

{
  "home.contact": "Contact us on @@0 or @@1.",
}

Then in one template

      <I18n key="home.contact"
            html={true}
            parts={[
              [ Link, { href: discordUrl, target: '_blank' }, 'Discord' ],
              [ Link, { href: facebookUrl, target: '_blank' }, 'Discord' ],
            ]}
      >
        Contact us on [[<Link href={discordUrl} target="_blank">Discord</Link>]] or [[<Link href={facebookUrl} target="_blank">Facebook</Link>]].
      </I18n>