Skip to content

Commit

Permalink
Merge pull request #97 from robisim74/dev-inline-v3
Browse files Browse the repository at this point in the history
Dev inline
  • Loading branch information
robisim74 authored Nov 25, 2023
2 parents 053a441 + 818bec3 commit 2b1ed24
Show file tree
Hide file tree
Showing 87 changed files with 5,001 additions and 4,367 deletions.
76 changes: 40 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ npm install qwik-speak --save-dev

## Getting Started
- [Quick Start](./docs/quick-start.md)
- [Tutorial: localized routing with prefix only](./docs/tutorial-routing.md)
- [Tutorial: localized routing with url rewriting](./docs/tutorial-routing-rewrite.md)
- [Tutorial: localized routing with the language](./docs/tutorial-routing.md)
- [Tutorial: translated routing with url rewriting](./docs/tutorial-routing-rewrite.md)
- [Translate](./docs/translate.md)
- [Translation functions](./docs/translation-functions.md)
- [Translation functions](./docs/translation-functions.md)
- [Lazy loading translation](./docs/lazy-loading.md)
- [Qwik Speak and Adapters](./docs/adapters.md)
- [Testing](./docs/testing.md)

Expand All @@ -21,15 +22,15 @@ Live example on [Cloudflare pages](https://qwik-speak.pages.dev/) and playground
## Overview
### Getting the translation
```tsx
import { useTranslate } from 'qwik-speak';
import { inlineTranslate } from 'qwik-speak';

export default component$(() => {
const t = useTranslate();
const t = inlineTranslate();

return (
<>
<h1>{t('app.title@@Qwik Speak')}</h1> {/* Qwik Speak */}
<p>{t('home.greeting@@Hi! I am {{name}}', { name: 'Qwik Speak' })}</p> {/* Hi! I am Qwik Speak */}
<h1>{t('title@@Qwik Speak')}</h1> {/* Qwik Speak */}
<p>{t('greeting@@Hi! I am {{name}}', { name: 'Qwik Speak' })}</p> {/* Hi! I am Qwik Speak */}
</>
);
});
Expand All @@ -53,6 +54,11 @@ export default component$(() => {
});
```

## Static translations
Translation are loaded and inlined in chunks sent to the browser during the build.

See [Qwik Speak Inline Vite plugin](./docs/inline.md) for more information on how it works and how to use it.

## Extraction of translations
To extract translations directly from the components, a command is available that automatically generates the files with the keys and default values.

Expand All @@ -63,11 +69,6 @@ To automatically translate files, an external command is available that uses Ope

See [GPT Translate JSON](./docs/gpt-translate-json.md) for more information on how to use it.

## Production
In production, translations are loaded and inlined during the build.

See [Qwik Speak Inline Vite plugin](./docs/inline.md) for more information on how it works and how to use it.

## Speak context
```mermaid
stateDiagram-v2
Expand All @@ -93,24 +94,19 @@ stateDiagram-v2
- loadTranslation$
end note
note right of State5
key-value pairs
of translation data
runtime assets
end note
```
> `SpeakState` is immutable: it cannot be updated after it is created and is not reactive
- `useSpeakContext()` Returns the Speak state
- `useSpeakConfig()` Returns the configuration in Speak context
- `useSpeakLocale()` Returns the locale in Speak context

### Speak config
- `defaultLocale` The default locale to use as fallback
- `supportedLocales` List of locales supported by the app
- `assets` Translation file names. Each asset is passed to the `loadTranslation$` function to obtain data according to the language
- `runtimeAssets` Assets available at runtime
- `keySeparator` Separator of nested keys. Default is `.`
- `keyValueSeparator` Key-value separator. Default is `@@`
- `rewriteRoutes` Rewrite routes as specified in Vite config for qwikCity
- `rewriteRoutes` Rewrite routes as specified in Vite config for `qwikCity` plugin

### SpeakLocale
The `SpeakLocale` object contains the `lang`, in the format `language[-script][-region]`, where:
Expand All @@ -129,36 +125,34 @@ and optionally contains:
`TranslationFn` interface can be implemented to change the behavior of the library:
- `loadTranslation$` QRL function to load translation data

### Translation
`Translation` contains only the key value pairs of the translation data provided with the `runtimeAssets`

## APIs
### Components
#### QwikSpeakProvider component
`QwikSpeakProvider` component provides the Speak context to the app. `Props`:
### Providers
`useQwikSpeak(props: QwikSpeakProps)` provides the Speak context to the app. `QwikSpeakProps`:
- `config` Speak config
- `translationFn` Optional functions to use
- `locale` Optional locale to use
- `langs` Optional additional languages to preload data for (multilingual)

#### Speak component (scoped translations)
`Speak` component can be used for scoped translations. `Props`:
`useSpeak(props: SpeakProps) ` can be used for lazy loading translation. `SpeakProps`:
- `assets` Assets to load
- `runtimeAssets` Assets to load available at runtime
- `langs` Optional additional languages to preload data for (multilingual)

### Functions
#### Translate
- `useTranslate: () => (keys: string | string[], params?: Record<string, any>, lang?: string)`
Translates a key or an array of keys. The syntax of the string is `key@@[default value]`
### Context
- `useSpeakContext()` Returns the Speak state
- `useSpeakConfig()` Returns the configuration in Speak context
- `useSpeakLocale()` Returns the locale in Speak context

- `inlineTranslate(keys: string | string[], ctx: SpeakState, params?: Record<string, any>, lang?: string)`
Translates a key or an array of keys outside the `component$`. The syntax of the string is `key@@[default value]`
### Translate
- `inlineTranslate: () => (keys: string | string[], params?: Record<string, any>, lang?: string)`
Translates a key or an array of keys. The syntax of the string is `key@@[default value]`

- `usePlural: () => (value: number | string, key?: string, params?: Record<string, any>, options?: Intl.PluralRulesOptions, lang?: string)`
- `inlinePlural: () => (value: number | string, key?: string, params?: Record<string, any>, options?: Intl.PluralRulesOptions, lang?: string)`
Gets the plural by a number using [Intl.PluralRules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules) API

- `useTranslatePath: () => (paths: string | string[], lang?: string)`
Translates a path or an array of paths. The translating string can be in any language. If not specified the target lang is the current one

#### Localize
### Localize
- `useFormatDate: () => (value: Date | number | string, options?: Intl.DateTimeFormatOptions, lang?: string, timeZone?: string)`
Formats a date using [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat) API

Expand All @@ -171,6 +165,16 @@ Formats a number using [Intl.NumberFormat](https://developer.mozilla.org/en-US/d
- `useDisplayName: () => (code: string, options: Intl.DisplayNamesOptions, lang?: string)`
Returns the translation of language, region, script or currency display names using [Intl.DisplayNames](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DisplayNames) API

### Routing
- `localizePath: () => (route: (string | URL) | string[], lang?: string)`
Localize a path, an URL or an array of paths with the language

- `translatePath: () => (route: (string | URL) | string[], lang?: string)`
Translates a path, an URL or an array of paths. The translating string can be in any language. If not specified the target lang is the current one

### Testing
- `QwikSpeakMockProvider` component provides the Speak context to test enviroments

## Development Builds
### Library & tools
#### Build
Expand Down
5 changes: 3 additions & 2 deletions SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
## Library

* [Quick Start](docs/quick-start.md)
* [Tutorial: localized routing with prefix only](docs/tutorial-routing.md)
* [Tutorial: localized routing with url rewriting](docs/tutorial-routing-rewrite.md)
* [Tutorial: localized routing with the language](docs/tutorial-routing.md)
* [Tutorial: translated routing with url rewriting](docs/tutorial-routing-rewrite.md)
* [Translate](docs/translate.md)
* [Translation functions](docs/translation-functions.md)
* [Lazy loading translation](docs/lazy-loading.md)
* [Qwik Speak and Adapters](docs/adapters.md)
* [Testing](docs/testing.md)

Expand Down
2 changes: 1 addition & 1 deletion docs/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ If your production environment doesn't support _dynamic import_, you can import
/**
* Translation files are imported directly as string
*/
const translationData = import.meta.glob('/i18n/**/*.json', { as: 'raw', eager: true });
const translationData = import.meta.glob<Translation>('/i18n/**/*.json', { as: 'raw', eager: true });

const loadTranslation$: LoadTranslationFn = server$((lang: string, asset: string) =>
JSON.parse(translationData[`/i18n/${lang}/${asset}.json`])
Expand Down
6 changes: 3 additions & 3 deletions docs/extract.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
#### Get the code ready
Optionally, you can use a default value for the keys. The syntax is `key@@[default value]`:
```html
<p>{t('app.title@@Qwik Speak'}</p>
<p>{t('home.greeting@@Hi! I am {{name}}', { name: 'Qwik Speak' })}</p>
<p>{t('title@@Qwik Speak'}</p>
<p>{t('greeting@@Hi! I am {{name}}', { name: 'Qwik Speak' })}</p>

```
When you use a default value, it will be used as initial value for the key in every translation.

> Note. A key will not be extracted when it is an identifier or contains an indentifier (dynamic)
> Note that it is not necessary to provide the default value of a key every time: it is sufficient and not mandatory to provide it once in the app
#### Naming conventions
If you use scoped translations, the first property will be used as filename:
Expand Down
45 changes: 12 additions & 33 deletions docs/inline.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
# Qwik Speak Inline Vite plugin

> Inline Qwik Speak `useTranslate`, `inlineTranslate` and `usePlural` functions at compile time
> Inline Qwik Speak `inlineTranslate` and `inlinePlural` functions at compile time
## How it works
In development mode, translation happens _at runtime_: `assets` are loaded during SSR or on client, and the lookup also happens at runtime.
O the server, translation happens _at runtime_: `assets` are loaded during SSR and the lookup also happens at runtime.

In production mode, `assets` are loaded only during SSR, and to get the translations on the client as well you have to use _Qwik Speak Inline_ Vite plugin.
On the client, translation happens _at compile-time_: `assets` are loaded and inlined in chunks sent to the browser during the build, reducing resource usage at runtime.

Using the _Qwik Speak Inline_, translation happens _at compile-time_: `assets` are loaded and inlined in chunks sent to the browser during the build, reducing resource usage at runtime:
`runtimeAssets` are always loaded at runtime, both on the server or on the client, allowing dynamic translations.

```mermaid
sequenceDiagram
participant Server
participant assets
participant runtimeAssets
participant Client
Server->>assets: loadTranslation$
activate assets
assets-->>Server: data
deactivate assets
Server->>Client: SSR: no serialize data
Note over Client: inlined data
Server->>runtimeAssets: loadTranslation$
activate runtimeAssets
runtimeAssets-->>Server: data
deactivate runtimeAssets
Server->>Client: SSR: serialize data
Note over Client: runtime data
```

## Usage
Expand Down Expand Up @@ -104,32 +111,4 @@ _dist/build/it-IT/q-*.js_

At the end of the build, in root folder a `qwik-speak-inline.log` file is generated which contains:
- Missing values
- Translations with dynamic keys
- Translations with dynamic params

## Qwik Speak Inline Vite plugin & runtime
When there are translations with dynamic keys or params, you have to use separate files, and add them to `runtimeAssets`:

```typescript
export const config: SpeakConfig = {
/* ... */
assets: [
'app' // Translations shared by the pages
],
runtimeAssets: [
'runtime' // Translations with dynamic keys or parameters
]
};
```
Likewise, you can also create scoped runtime files for different pages and pass them to `Speak` components:
```tsx
export default component$(() => {
return (
<Speak assets={['home']} runtimeAssets={['runtimeHome']}>
<Page />
</Speak>
);
});
```
> `runtimeAssets` are serialized and sent to the client, and loaded when required
- Translations with dynamic keys or params
43 changes: 43 additions & 0 deletions docs/lazy-loading.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Lazy loading translation

If you are developing a large app, you can consider using lazy loading translation: translations that are lazy loaded only when requested (when the user navigates to a specific section or page of the app):

```mermaid
C4Container
Container_Boundary(a, "App") {
Component(a0, "root", "useQwikSpeak", "Translations available in the whole app")
Container_Boundary(b1, "Site") {
Component(b10, "Page", "useSpeak", "Translations available in Page component")
}
Container_Boundary(b2, "Admin") {
Component(b20, "layout", "useSpeak", "Translations available in child components")
}
}
```

For lazy loading of files in a specific section, you need to add `useSpeak` to the layout:
```tsx
import { useSpeak } from 'qwik-speak';

export default component$(() => {
useSpeak({assets:['admin'], runtimeAssets: ['runtimeAdmin']});

return (
<>
<main>
<Slot />
</main>
</>
);
});
```
or in a specific page:
```tsx
export default component$(() => {
useSpeak({runtimeAssets: ['runtimePage']});

return <Page />;
});
```
> Note that you must create a component for the page, because Qwik renders components in isolation, and translations are only available in child components
Loading

0 comments on commit 2b1ed24

Please sign in to comment.