Skip to content

Commit

Permalink
[doc] rework i18n, make return statement optionnal, add production ad…
Browse files Browse the repository at this point in the history
…vices
  • Loading branch information
enzoaicardi committed Mar 3, 2024
1 parent 4fe1d81 commit 0b46891
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 25 deletions.
68 changes: 68 additions & 0 deletions wiki/concepts/production.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Build or not to build ?

Vif allows you to publish your site or application without any build step, this is very useful during your developments and will also work perfectly in production.

However, in order to preserve bandwidth and thus help reduce the ecological impact of the web while making your application more accessible and quick to load, we advise you to go through a process of building your application.

There are many tools to do this like [SWC](https://swc.rs/), [Rollup](https://rollupjs.org/), or [Vite](https://vitejs.dev /).

The different steps to carry out are as follows:

- minify your javascript files
- mangle some property names
- minify template literals of type `html...`
- minify your css style sheets
- minify your html documents
- compress your images and svg

## Rollup config

If you use Rollup you can use this configuration as a basis:

```js
// Import rollup plugins
import resolve from "@rollup/plugin-node-resolve";
import minifyHTML from "rollup-plugin-minify-html-literals";
import { terser } from "@rollup/plugin-terser";
import summary from "rollup-plugin-summary";

export default {
// Setup input files
input: {
"first-component": "src/first-component.js",
"second-component": "src/second-component.js",
}
// Setup output directory
output: {
dir: "dist",
format: "esm",
},
plugins: [
// Resolve bare module specifiers to relative paths
resolve(),
// Minify HTML template literals
minifyHTML(),
// Minify JS and mangle properties starting by "_"
terser({
mangle: {
properties: {
regex: /^_/,
},
},
}),
// Print bundle summary
summary(),
],
// Mark vif and web-imported modules as external
// so they are not included into the bundle
external: ["vifjs", /^https:/],
preserveEntrySignatures: "strict",
};
```

---

# Next

Done for the moment...
[back to home](../README.md)
4 changes: 2 additions & 2 deletions wiki/methods/define.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ The render function has one argument with three properties:
The render function should return one of theses types:

- `string` representing an HTML template
- `this || undefined` representing the current component
- `this | undefined` representing the current component (you can simply omit the return statement)
- `this.children` reprensenting the current component's children
- `this.childNodes` reprensenting the current component's childNodes

Expand Down Expand Up @@ -64,7 +64,7 @@ function Counter({ props }) {
const count = (props.count = useSignal(0));
props.increment = () => count(count.value + 1);

return this;
return this; // optionnal
}

useDefine("counter", Counter);
Expand Down
102 changes: 79 additions & 23 deletions wiki/methods/i18n.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,17 @@ export default {

In a second time you will need to reference your files with the `useI18n` method, all the files will be lazy load only when necessary.

By doing this, the `useI18n` method will return a [signal](./signal.md) which contains the default `export` of the corresponding translation file.

```js
const localeImport = (url) => import(url);
const localeImport = (src, locale) => {
// update html lang attribute (optionnal)
document.documentElement.setAttribute("lang", locale);
return import(src);
};

useI18n({
// translations can be considered as a global signal
const translations = useI18n({
fr: {
FR: () => localeImport("./fr.js"),
default: "FR",
Expand All @@ -37,7 +44,7 @@ useI18n({
});
```

The object argument must have 2 levels and the default property is always mandatory, for example this is not allowed :
The object argument (called `definition`) must have two levels and the **default property** is **mandatory**, for example this is not allowed :

```js
useI18n({
Expand All @@ -46,47 +53,97 @@ useI18n({
});
```

## Wait locale before hydrate
## Hydration with locales

There are two ways to hydrate a component with translations:

Before using translations you must be sure that the file is loaded otherwise you will get errors in your javascript expressions.
- Define the component once the queries to the translations are completed.
- Immediately define component but add `&&` clause in translated directives.

To achieve this use the `onload` method of `useI18n` :
If you immediately define a component and use translations in your directives you may get errors in your javascript expressions, for example this will throw an error :

```js
useI18n.onload(() => {
// all your component definitions should be here
useDefine("app", function ({ props }) {
props.i18n = useI18n;
return this;
});
// ...
});
const myTranslationsSignal = useI18n(/* your definition here */);

// App render function
function App({ props }) {
props.translations = myTranslationsSignal;
return this;
}

// Component definition
useDefine("app", App);
```

## Use translations in components
```html
<x-app>
<p x-text="translations().app.mycustomtext">...</p>
</x-app>
```

At this point, javascript execution will throw an error because `translations()` returns `undefined` and the `.app` property does not exist on type `undefined`. This error occurs because your `App` component is defined before translations are loaded.

We will see below how to solve this problem.

### Wait before hydration

To use translations in your components simply use the i18n method without parameters.
The first solution is to use the native `onload` method of each translation signal. This will wait for your translations to be loaded before defining your component.

In that case, i18n refers to the current translation object.
```js
const myTranslationsSignal = useI18n(/* your definition here */);

// App render function
function App({ props }) {
props.translations = myTranslationsSignal;
return this;
}

// Component definition using `onload`
myTranslationsSignal.onload(() => useDefine("app", App));
```

Pros :

- Easy to setup, maintain and debug.
- Apply translated directives just one time.

Cons :

- Interactivity of the component is only available once the translations are loaded

## Use translations in components

The second solution applies directly to directives and simply consists of performing a `&&` type check on the translation signal.

```html
<!-- x-app definition in previous section -->
<x-app>
<h1 x-text="i18n().app.title">Default SEO title</h1>
<p x-text="translations() && translations().app.mycustomtext">...</p>
</x-app>
```

Pros :

- Immediate interactivity of the component.

Cons :

- Apply translated directives two times instead on one.
- One time with `undefined` as value.
- One time with the translation as value.
- More verbose than `onload`.
- Harder to setup, maintain and debug.

## Change the locale manually

Vif set the locale by default from `localStorage.getItem("locale")` or `navigator.language`. After that you can redefine the locale with the `locale` method of `i18n`.
Vif set the locale by default from `localStorage.getItem("locale")` or `navigator.language`. After that you can redefine the locale with the `locale` method of `useI18n`.

```js
function LanguageSwitcher({ props, html }) {
props.toEnglish = () => useI18n.locale("en-US");
return html`<button x-on:click="toEnglish">Switch to english</button>`;
}

useDefine("lang", LanguageSwitcher);
useDefine("language-switcher", LanguageSwitcher);
```

If you want to store the new locale by default for the user, you can simply use `localStorage.setItem("locale", "en-US")`.
Expand All @@ -95,5 +152,4 @@ If you want to store the new locale by default for the user, you can simply use

# Next

Done for the moment...
[back to home](../README.md)
[Make your app ready for production !](../concepts/production.md)

0 comments on commit 0b46891

Please sign in to comment.