docs: ✏️ move docs out of wiki

This commit is contained in:
Christian Kaisermann 2020-01-15 23:06:42 -03:00
parent 1c059060e2
commit 69ec477ffd
10 changed files with 757 additions and 3 deletions

3
.gitignore vendored
View File

@ -1,5 +1,4 @@
node_modules node_modules
*.log *.log
dist/ dist/
coverage/ coverage/
docs/

View File

@ -45,4 +45,4 @@
} }
``` ```
### [Go see the documentation](https://github.com/kaisermann/svelte-i18n/wiki) ### [Go see the documentation](https://github.com/kaisermann/svelte-i18n/blob/master/docs)

13
docs/CLI.md Normal file
View File

@ -0,0 +1,13 @@
`svelte-i18n` provides a command-line interface to extract all your messages to the `stdout` or to a specific JSON file.
```bash
$ svelte-i18n extract [options] <glob-pattern> [output-file]
```
### Options
- `-s, --shallow` - extract all messages to a shallow object, without creating nested objects. Default: `false`.
- `--overwrite` - overwrite the content of the `output` file instead of just appending missing properties. Default: `false`.
- `-c, --configDir` - define the directory of a [`svelte.config.js`](https://github.com/UnwrittenFun/svelte-vscode#generic-setup) in case your svelte components need to be preprocessed.

31
docs/Dictionary.md Normal file
View File

@ -0,0 +1,31 @@
`import { dictionary } from 'svelte-i18n'`
### `$dictionary`
The `$dictionary` store is responsible for holding all loaded message definitions for each locale. A dictionary of messages can be a shallow or deep object:
###### `en-shallow.json`
```json
{
"title": "Sign up",
"field.name": "Name",
"field.birth": "Date of birth",
"field.genre": "Genre"
}
```
###### `en-deep.json`
```json
{
"title": "Sign up",
"field": {
"name": "Name",
"birth": "Date of birth",
"genre": "Genre"
}
}
```
It's recommended to use the [`addMessages()`](/docs/Methods.md#addmessages) and [`register()`](/docs/Methods.md#register) methods to add new message dictionaries to your app.

34
docs/FAQ.md Normal file
View File

@ -0,0 +1,34 @@
##### `'this' keyword is equivalent to 'undefined'`
When using `Rollup` as a bundler, you're possibly seeing this warning. It's related to the output of the typescript compiler used to transpile the `intl-messageformat` package. In this case, it's harmless and you can learn to live with it on your terminal or teach your rollup to ignore this kind of warning:
```js
// modified version of onwarn provided by sapper projects
const onwarn = (warning, onwarn) => {
if (
(warning.code === 'CIRCULAR_DEPENDENCY' &&
/[/\\]@sapper[/\\]/.test(warning.message))
) {
return
}
// ignores the annoying this is undefined warning
if(warning.code === 'THIS_IS_UNDEFINED') {
return
}
onwarn(warning)
}
export default {
client: {
...,
onwarn,
},
server: {
...,
onwarn,
},
}
```

179
docs/Formatting.md Normal file
View File

@ -0,0 +1,179 @@
### Message syntax
Under the hood, `formatjs` is used for localizing your messages. It allows `svelte-i18n` to support the ICU message syntax. It is strongly recommended to read their documentation about it.
- [Basic Internationalization Principles](https://formatjs.io/guides/basic-i18n/)
- [Runtime Environments](https://formatjs.io/guides/runtime-environments/)
- [ICU Message Syntax](https://formatjs.io/guides/message-syntax/)
### `$format` or `$_` or `$t`
`import { _, t, format } from 'svelte-i18n'`
The `$format` store is the actual formatter method. It's also aliased as `$_` and `$t` for convenience. To format a message is as simple as executing the `$format` method:
```svelte
<script>
import { _ } from 'svelte-i18n'
</script>
<h1>{$_('page_title')}</h1>
```
The formatter can be called with two different signatures:
- `format(messageId: string, options?: MessageObject): string`
- `format(options: MessageObject): string`
```ts
interface MessageObject {
id?: string
locale?: string
format?: string
default?: string
values?: Record<string, string | number | Date>
}
```
- `id`: represents the path to a specific message;
- `locale`: forces a specific locale;
- `default`: the default value in case of message not found in the current locale;
- `format`: the format to be used. See [#formats](#formats);
- `values`: properties that should be interpolated in the message;
You can pass a `string` as the first parameter for a less verbose way of formatting a message.
If the message id literal value is not in the root of the dicitonary, `.` (dots) are interpreted as a path:
```jsonc
// en.json
{
"shallow.prop": "Shallow property",
"deep": {
"property": "Deep property"
}
}
```
```svelte
<div>{$_('shallow.prop')}</div> <!-- Shallow property -->
<div>{$_('deep.prop')}</div> <!-- Deep property -->
```
### Formatting utilities
The formatter method also provides some casing methods:
- `_.upper` - transforms a localized message into uppercase;
- `_.lower` - transforms a localized message into lowercase;
- `_.capital` - capitalize a localized message;
- `_.title` - transforms the message into title case;
```html
<div>{$_.upper('greeting.ask')}</div>
<!-- PLEASE TYPE YOUR NAME -->
<div>{$_.lower('greeting.ask')}</div>
<!-- please type your name -->
<div>{$_.capital('greeting.ask')}</div>
<!-- Please type your name -->
<div>{$_.title('greeting.ask')}</div>
<!-- Please Type Your Name -->
```
#### `_.time(time: Date, options: MessageObject): string`
Formats a date object into a time string with the specified format. Please refer to the [#formats](#formats) section to see available formats.
```html
<div>{$_.time(new Date(2019, 3, 24, 23, 45))}</div>
<!-- 11:45 PM -->
<div>{$_.time(new Date(2019, 3, 24, 23, 45), { format: 'medium' } )}</div>
<!-- 11:45:00 PM -->
```
#### `_.date(date: Date, options: MessageObject): string`
Formats a date object into a string with the specified format. Please refer to the [#formats](#formats) section to see available formats.
```html
<div>{$_.date(new Date(2019, 3, 24, 23, 45))}</div>
<!-- 4/24/19 -->
<div>{$_.date(new Date(2019, 3, 24, 23, 45), { format: 'medium' } )}</div>
<!-- Apr 24, 2019 -->
```
#### `_.number(number: number, options: MessageObject): string`
Formats a number with the specified locale and format. Please refer to the [#formats](#formats) section to see available formats.
```html
<div>{$_.number(100000000)}</div>
<!-- 100,000,000 -->
<div>{$_.number(100000000, { locale: 'pt' })}</div>
<!-- 100.000.000 -->
```
### Formats
`svelte-i18n` comes with a default set of `number`, `time` and `date` formats:
**Number:**
- `currency`: `{ style: 'currency' }`
- `percent`: `{ style: 'percent' }`
- `scientific`: `{ notation: 'scientific' }`
- `engineering`: `{ notation: 'engineering' }`
- `compactLong`: `{ notation: 'compact', compactDisplay: 'long' }`
- `compactShort`: `{ notation: 'compact', compactDisplay: 'short' }`
**Date:**
- `short`: `{ month: 'numeric', day: 'numeric', year: '2-digit' }`
- `medium`: `{ month: 'short', day: 'numeric', year: 'numeric' }`
- `long`: `{ month: 'long', day: 'numeric', year: 'numeric' }`
- `full`: `{ weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' }`
**Time:**
- `short`: `{ hour: 'numeric', minute: 'numeric' }`
- `medium`: `{ hour: 'numeric', minute: 'numeric', second: 'numeric' }`
- `long`: `{ hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'short' }`
- `full`: `{ hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'short' }`
### Accessing formatters directly
`svelte-i18n` also provides a low-level API to access its formatter methods:
```js
import {
getDateFormatter,
getNumberFormatter,
getTimeFormatter,
getMessageFormatter,
} from 'svelte-i18n'
```
By using these methods, it's possible to manipulate values in a more specific way that fits your needs. For example, it's possible to create a method which receives a `date` and returns its relevant date related parts:
```js
import { getDateFormatter } from 'svelte-i18n'
const getDateParts = date =>
getDateFormatter()
.formatToParts(date)
.filter(({ type }) => type !== 'literal')
.reduce((acc, { type, value }) => {
acc[type] = value
return acc
}, {})
getDateParts(new Date(2020, 0, 1)) // { month: '1', day: '1', year: '2020' }
```
Check the [methods documentation](/docs/Methods.md#low-level-api) for more information.

124
docs/Getting Started.md Normal file
View File

@ -0,0 +1,124 @@
### Getting started
#### 1. Locale dictionaries
A locale dictionary is a regular JSON object which contains message definitions for a certain language.
```jsonc
// en.json
{
"page_title": "Page titlte",
"sign_in": "Sign in",
"sign_up": "Sign up",
}
// pt.json
{
"page_title": "Título da página",
"sign_in": "Entrar",
"sign_up": "Registrar",
}
```
#### 2. Adding locale dictionaries
There are two different ways of adding a new dicitonary of messages to a certain locale:
##### 2.1 Synchronous
Just `import`/`require` your locale `.json` files and pass them to the [`addMessages(locale, dict)`](/docs/Methods.md#addmessages) method.
```js
// src/i18n.js
import { addMessages } from 'svelte-i18n'
import en from './en.json'
import enUS from './en-US.json'
import pt from './pt.json'
addMessages('en', en)
addMessages('en-US', enUS)
addMessages('pt', pt)
// en, en-US and pt are available
```
##### 2.2 Asynchronous
A more performant way to load your dictionaries is to register `loader` methods. This way, only the files registered to the current locale will be loaded. A `loader` is a method which must return a `Promise` that resolves to a `JSON` object. A [`$locale`](/kaisermann/svelte-i18n/Locale) value change will automatically load the registered loaders for the new locale.
```js
// src/i18n.js
import { register } from 'svelte-i18n'
register('en', () => import('./en.json'))
register('en-US', () => import('./en-US.json'))
register('pt', () => import('./pt.json'))
// en, en-US and pt are not available yet
```
#### 3. Initializing
After populating your [`$dictionary`](/docs/Dictionary) with [`addMessages()`](/docs/Methods.md#addmessages) or registering loaders via [`register()`](/docs/Methods.md#register), you are ready to bootstrap the library. You can use [`init()`](/docs/Methods.md#init) to define the fallback locale, initial locale and other options of your app.
```js
// src/i18n.js
import { register, init } from 'svelte-i18n'
register('en', () => import('./en.json'))
register('en-US', () => import('./en-US.json'))
register('pt', () => import('./pt.json'))
// en, en-US and pt are not available yet
init({
fallbackLocale: 'en',
initialLocale: {
navigator: true, // i.e 'en-US'
},
})
// starts loading 'en-US' and 'en'
```
_**Note**: If you're using Sapper, remember to also call `init()` on your server side code (`server.js`)._
Since we're using `register`, and not `addMessages`, we need to wait for it's loaders to finish before rendering your app.
In **Svelte**, the [`$isLoading`](/docs/Locale.md#loading) store can help to only show your app after the initial load as shown in [Locale](/docs/Locale.md#loading).
In **Sapper**, you can use the `preload` static method together with `waitLocale`:
```svelte
<!-- src/_layout.svelte -->
<script context="module">
import { waitLocale } from 'svelte-i18n'
export async function preload() {
// awaits for the loading of the 'en-US' and 'en' dictionaries
return waitLocale()
}
</script>
```
Please note that the `fallbackLocale` is always loaded, independent of the current locale, since only some messages can be missing.
#### 4. Localizing your app
After having the initial locale set, you're ready to start localizing your app. Import the [`$format`](/docs/Formatting) method, or any of its aliases, to any component that needs to be translated. Then, just call [`$format`](/docs/Formatting) passing the message `id` on your layout and voila! 🎉
```svelte
<script>
import { _ } from 'svelte-i18n'
</script>
<svelte:head>
<title>{$_.upper('page_title')}</title>
</svelte:head>
<nav>
<a>{$_('sign_in')}</a>
<a>{$_('sign_up')}</a>
</nav>
```
See [Formatting](/docs/Formatting) to read about the supported message syntax.

60
docs/Locale.md Normal file
View File

@ -0,0 +1,60 @@
`import { locale } from 'svelte-i18n'`
<!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->
### `$locale`
The `locale` store defines what is the current locale. When its value is changed, before updating the actual stored value, `svelte-i18n` sees if there's any message loaders registered for the new locale:
- If yes, changing the `locale` is an async operation.
- If no, the locale's dictionary is fully loaded and changing the locale is a sync operation.
The `<html lang>` attribute is automatically updated to the current locale.
#### Usage on component
To change the locale inside a component is as simple as assinging it a new value.
```svelte
<script>
import { locale, locales } from 'svelte-i18n'
</script>
<select bind:value={$locale}>
{#each $locales as locale}
<option value={locale}>{locale}</option>
{/each}
</select>
```
#### Usage on regular script
```js
import { locale } from 'svelte-i18n'
// Set the current locale to en-US
locale.set('en-US')
// This is a store, so we can subscribe to its changes
locale.subscribe(() => console.log('locale change'))
```
### `$loading`
While changing the `$locale`, the `$loading` store can be used to detect if the app is currently fetching any enqueued message definitions.
```svelte
<script>
import { loading } from 'svelte-i18n'
</script>
{#if loading}
Please wait...
{:else}
<Nav />
<Main />
{/if}
```
> `$loading` will only be `true` if fetching takes more than 200ms.

202
docs/Methods.md Normal file
View File

@ -0,0 +1,202 @@
#### init
> `import { init } from 'svelte-i18n'`
`init(options: InitOptions): void`
Method responsible for configuring some of the library behaviours such as the global fallback and initial locales. Must be called before setting a locale and displaying your view.
```ts
interface InitOptions {
// the global fallback locale
fallbackLocale: string
// set of heuristic configs to define the client's locale
initialLocale?: InitialLocaleOptions
// custom time/date/number formats
formats?: Formats
// loading delay interval
loadingDelay?: number
}
interface InitialLocaleOptions {
// the fallback locale to use if no message is found in the current one
fallback?: string
// when 'true', check the 'window.navigator.language' to set the current locale
navigator?: boolean
// key to look for a locale on 'window.location.search'
// 'example.com?locale=en-US'
search?: string
// key to look for a locale on 'window.location.hash'
// 'example.com#locale=en-US'
hash?: string
// pattern to look in the window.location.pathname.
// It returns the first capturing group.
pathname?: RegExp
// pattern to look in the window.location.hostname.
// It returns the first capturing group.
hostname?: RegExp
}
```
**Example**:
```js
import { init } from 'svelte-i18n'
init({
// fallback to en if current locale is not in the dictionary
fallbackLocale: 'en',
initialLocale: {
// based on the user's browser
navigator: true,
},
})
```
##### Custom formats
It's possible to define custom format styles via the `formats` property if you want to quickly pass a set of options to the underlying `Intl` formatter.
```ts
interface Formats {
number: Record<string, Intl.NumberFormatOptions>
date: Record<string, Intl.DateTimeFormatOptions>
time: Record<string, Intl.DateTimeFormatOptions>
}
```
Please refer to the [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat) and [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat) documentations to see available formatting options.
**Example**:
```js
import { init } from 'svelte-i18n'
init({
// fallback to en if current locale is not in the dictionary
fallbackLocale: 'en',
formats: {
number: {
EUR: { style: 'currency', currency: 'EUR' },
},
},
})
```
```html
<div>
{$_.number(123456.789, { format: 'EUR' })}
</div>
<!-- 123.456,79 € -->
```
#### addMessages
`import { addMessages } from 'svelte-i18n`
`addMessages(locale: string, ...dicts: Dictionary[]): void`
Merge one ore more dictionary of messages with the `locale` dictionary.
**Example**:
```js
addMessages('en', { field_1: 'Name' })
addMessages('en', { field_2: 'Last Name' })
addMessages('pt', { field_1: 'Nome' })
addMessages('pt', { field_2: 'Sobrenome' })
// Results in dictionary
{
en: {
field_1: 'Name',
field_2: 'Last Name'
},
pt: {
field_1: 'Nome',
field_2: 'Sobrenome'
}
}
```
#### register
> `import { register } from 'svelte-i18n'`
`register(locale: string, loader: () => Promise<object>): void`
Registers an async message `loader` for the specified `locale`. The loader queue is executed when changing to `locale` or when calling `waitLocale(locale)`.
**Example**:
```js
import { register } from 'svelte-i18n'
register('en', () => import('./_locales/en.json'))
register('pt', () => import('./_locales/pt.json'))
```
See [how to asynchronously load dictionaries](/svelte-i18n/wiki#22-asynchronous).
#### waitLocale
> `import { waitLocale } from 'svelte-i18n'`
`waitLocale(locale: string = $locale): Promise<void>`
Executes the queue of `locale`. If the queue isn't resolved yet, the same promise is returned. Great to use in the `preload` method of Sapper for awaiting [`loaders`](/svelte-i18n/wiki#22-asynchronous).
**Example**:
```svelte
<script context="module">
import { register, waitLocale, init } from 'svelte-i18n'
register('en', () => import('./_locales/en.json'))
register('pt-BR', () => import('./_locales/pt-BR.json'))
register('es-ES', () => import('./_locales/es-ES.json'))
init({ fallbackLocale: 'en' })
export async function preload() {
// awaits for 'en' loaders
return waitLocale()
}
</script>
```
### Low level API
#### getDateFormatter / getTimeFormatter / getNumberFormatter
> `import { getDateFormatter, getNumberFormatter, getTimeFormatter } from 'svelte-i18n'`
```ts
type FormatterOptions<T> = T & {
format?: string
locale?: string // defaults to current locale
}
getDateFormatter(
options: FormatterOptions<Intl.DateTimeFormatOptions>
): Intl.DateTimeFormat
getTimeFormatter(
options: FormatterOptions<Intl.DateTimeFormatOptions>
): Intl.DateTimeFormat
getNumberFormatter(
options: FormatterOptions<Intl.NumberFormatOptions>
): Intl.NumberFormat
```
Each of these methods return their respective [`Intl.xxxxFormatter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects.md#Internationalization) variant. Click [here](/docs/formatting.md#accessing-formatters-directly) for an example of usage.
#### getMessageFormatter
> `import { getMessageFormatter } from 'svelte-i18n'`
`getMessageFormatter(messageId: string, locale: string): IntlMessageFormat`
Returns an instance of a [`IntlMessageFormat`](https://github.com/formatjs/formatjs/blob/master/packages/intl-messageformat/README.md).

112
docs/Migration.md Normal file
View File

@ -0,0 +1,112 @@
#### From `v1` to `v2`
##### Adding dictionaries
In `v1`, dictionaries were added through the store API of `$dictionary`.
```js
import { dictionary } from 'svelte-i18n'
dictionary.set({
en: { ... },
pt: { ... },
})
dictionary.update(d => {
d.fr = { ... }
return d
})
```
In `v2`, you can use [`addMessages(locale, messages)`](/docs/Methods.md#addmessages) to add new messages to the main dictionary.
```js
import { addMessages } from 'svelte-i18n'
addMessages('en', { ... })
addMessages('pt', { ... })
addMessages('fr', { ... })
// message dictionaries are merged together
addMessages('en', { ... })
```
_It's also possible to asynchronously load your locale dictionary, see [register()](/docs/Methods.md#register)._
##### Setting the initial and fallback locales
In `v1`, to set the initial and fallback locales you could use `getClientLocale()` together with `$locale = ...` or `locale.set(...)`.
```js
import { getClientLocale, locale } from 'svelte-i18n'
locale.set(
getClientLocale({
fallback: 'en',
navigator: true,
})
)
```
In `v2`, both locales can be defined very similarly with [`init()`](/docs/Methods.md#init).
```js
import { init } from 'svelte-i18n'
init({
fallbackLocale: 'en',
initialLocale: {
navigator: true,
},
})
```
##### Interpolating values
In `v1`, interpolated values were the whole object passed as the second argument of the `$format` method.
```svelte
<h1>
{$_('navigation.pagination', { current: 2, max: 10 })}
</h1>
<!-- Page: 2/10 -->
```
In `v2`, the interpolated values are passed in the `values` property.
```svelte
<h1>
{$_('navigation.pagination', { values: { current: 2, max: 10 }})}
</h1>
<!-- Page: 2/10 -->
```
##### Adding custom formats
In `v1`, custom formats could be added with `addCustomFormats()`.
```js
import { addCustomFormats } from 'svelte-i18n'
addCustomFormats({
number: {
EUR: { style: 'currency', currency: 'EUR' },
},
})
```
In `v2`, custom formats are added through [`init()`](/docs/Methods.md#init).
```js
import { init } from 'svelte-i18n'
init({
fallbackLocale: ...,
initialLocale, ...,
formats:{
number: {
EUR: { style: 'currency', currency: 'EUR' },
},
}
})
```