sveltejs / svelte

Cybernetically enhanced web apps
https://svelte.dev
MIT License
78.19k stars 4.09k forks source link

Internationalization support #1034

Closed emilos closed 6 years ago

emilos commented 6 years ago

I'm opening this issue to discuss what would be the suggested pattern to handle translations with svelte. The ideal output of this discussion might be examples for docs, maybe a new feature that makes it simpler, not sure yet. Let's assume that we build a big app that handles few languages. Some ideas:

a)

Import where needed:

// Hello.html
<p>{{translate('hello')}}</p>
<script>
import { translate } from './utilities/translations'
export default {
  helpers: { translate }
}
</script>

Pros: simple, works out of the box, available only where needed Cons: repetition of the import

b)

Pass it to all child components inside of the top component

// Main.html
<Hello/>
<script>
import Hello from './Hello.html'
import { translate } from './utilities/translations'
export default {
  // somehow pass the translation method to all child components that it's available in the template (?)
  components: { Hello }
}
</script>
// Hello.html
<p>{{translate('hello')}}</p>

Pros: no repetition, saves keystrokes Cons: global dependency, available everywhere

c)

Pass it to all child components outside of the top component

// main.js
import Main from './Main.html'
import { translate } from './utilities/translations'
new Main({
   // somehow pass the translation method to all child components so that it's available in the templates
  // maybe:
  helpers: { translate }
})
// Hello.html
<p>{{translate('hello')}}</p>

Pros: no repetition, saves keystrokes Cons: global dependency, available everywhere

I don't have a strong opinion which one is better, any other ideas? Maybe there's an option to do so already and I missed it :) Please let me know what you think.

ekhaled commented 6 years ago

There is this https://github.com/sveltejs/svelte/pull/1023

But I suppose that's a variation of (a) just moving the repetition to oncreate. Unless you want to sprinkle your templates with {{root.options.translate('hello')}}

This seems like another case for dependency injection.

Rich-Harris commented 6 years ago

What about Store?

// main.js
const store = new Store({
  translate: str => {
    // some logic happens
  }
});

const app = new App({ target, store });
<p>{{$translate('hello')}}</p>

translate could even be set lazily (after you've lazy-loaded the translations, for example) or a computed property that depends on a lang property that the user can select — whatever is most suitable for the app in question.

emilos commented 6 years ago

Thanks guys :)

Having the translate method defined as a part of the store works fine. I thought the purpose of the store is to hold the data and not the utility methods. The only concern I have is that it might be unexpected for people to have it there, but maybe it just shouldn't be passed this way. A computed texts property might be a better choice in some scenarios.

Stormsher commented 5 years ago

Seems like it would not work with Svelte3 ... Any other ideas? Maybe somebody have tool like i18nliner?

cristovao-trevisan commented 5 years ago

I've written a library using the store approach. Forked it from the svelte-intl package and just send a PR.

There's also the svelte-i18n, but I didn't use because the bundle is too bloated (about 73kB gz)