Skip to content

Commit 1ef5e62

Browse files
docs: document i18n and bundling (#606)
1 parent a7ecb3c commit 1ef5e62

File tree

5 files changed

+434
-196
lines changed

5 files changed

+434
-196
lines changed

.storybook/preview.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,6 @@ import React, { useEffect } from 'react';
1515
import 'react-app-polyfill/ie11';
1616

1717
addParameters({
18-
options: {
19-
storySort: (a, b) => {
20-
return a[1].kind === b[1].kind
21-
? 0
22-
: a[1].id.localeCompare(b[1].id, undefined, { numeric: true, caseFirst: 'upper' });
23-
}
24-
},
2518
passArgsFirst: true,
2619
viewMode: 'docs',
2720
docs: { forceExtractedArgTypes: true },
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { Meta } from '@storybook/addon-docs/blocks';
2+
3+
<Meta title="Internationalization" />
4+
5+
# Setup Internationalization (i18n)
6+
7+
UI5 Web Components (for React) aim to be feature rich and with a minimal code footprint at the same time.
8+
In order to achieve this, most UI5 Web Components packages ship their assets as `.json` files while also providing a public module import for them.
9+
10+
**Prerequisite: your `@ui5/webcomponents-react` dependency has to be at least at `0.10.0-rc.2` or newer.**
11+
12+
In order to make your app translatable into various languages, you need to import the following assets:
13+
14+
```js
15+
import '@ui5/webcomponents/dist/Assets.js';
16+
import '@ui5/webcomponents-fiori/dist/Assets.js';
17+
import '@ui5/webcomponents-react/dist/Assets';
18+
```
19+
20+
That's it! You can now test whether the translations work correctly by adding e.g. `?sap-ui-language=de` to your URL for German translations.
21+
22+
<br />
23+
<br />
24+
25+
## How to add custom translations
26+
27+
_Please also read the [UI5 Web Components i18n documentation](https://github.com/SAP/ui5-webcomponents/blob/master/docs/i18n.md)._
28+
_This chapter requires `@ui5/webcomponents@>1.0.0-rc.8`._
29+
30+
**1. Start by creating some `i18n` resources in a directory that can be served, for example:**
31+
32+
| File | Content |
33+
| ------------------------------------ | -------------------------- |
34+
| `assets/messagebundle_de.properties` | `PLEASE_WAIT=Bitte warten` |
35+
| `assets/messagebundle_fr.properties` | `PLEASE_WAIT=Patientez.` |
36+
| `assets/messagebundle_es.properties` | `PLEASE_WAIT=Espere` |
37+
| `assets/messagebundle_en.properties` | `PLEASE_WAIT=Please wait` |
38+
39+
**2. Import the following `i18n`-related modules to your app:**
40+
41+
```js
42+
import '@ui5/webcomponents-base/dist/features/PropertiesFormatSupport.js';
43+
import { registerI18nBundle, fetchI18nBundle, getI18nBundle } from '@ui5/webcomponents-base/dist/i18nBundle.js';
44+
```
45+
46+
The first one provides support for `.properties` files, as used in the example and the second one contains the functions
47+
that will allow you to take advantage of the `i18n` functionality.
48+
49+
**3. Register your message bundles:**
50+
51+
```js
52+
registerI18nBundle('myApp', {
53+
de: './assets/messagebundle_de.properties',
54+
es: './assets/messagebundle_es.properties',
55+
fr: './assets/messagebundle_fr.properties',
56+
en: './assets/messagebundle_en.properties'
57+
});
58+
```
59+
60+
The first argument is an ID that will be used to reference this message bundle and the second, an object,
61+
providing the URLs where the `i18n` assets can be found.
62+
63+
_Note:_ This is just asset registration, no data will be fetched at that point.
64+
65+
**4. Use your translated texts in your components**
66+
67+
Add the following import statement to the component where you want to use translated texts:
68+
69+
```js
70+
import { useI18nText } from '@ui5/webcomponents-react-base/lib/hooks';
71+
```
72+
73+
Now, you can use the `useI18nText` hook in your functional components in order to get your translated texts.
74+
75+
The hook has the following signature:
76+
77+
```js
78+
const translatedTextsArray = useI18nText((messageBundleId: string), (...textsToTranslate: (string | string[])[]));
79+
```
80+
81+
In case you have texts without placeholder values you can use the simple `string` arguments, but if you have parameters
82+
in your message bundle you will have to use the `array` notation (_please see step 6_).
83+
You can mix `string` and `array` arguments in the same call.
84+
85+
Each parameter will be translated one by one and returned in an array in the same order.
86+
87+
**Example:**
88+
89+
```jsx
90+
const MyTranslatedTextComponent = () => {
91+
const [pleaseWaitText, anotherText] = useI18nText('myApp', 'PLEASE_WAIT', 'ANOTHER_TEXT_TO_TRANSLATE');
92+
93+
return (
94+
<div>
95+
<span>{pleaseWaitText}</span>
96+
<p>{anotherText}</p>
97+
</div>
98+
);
99+
};
100+
```
101+
102+
**5. Use texts with placeholder values**
103+
104+
In case you have texts with placeholders in your message bundle, you can pass an array as text parameter to receive
105+
translated text with parameters. In this case, the first entry in the array is the translation key, followed by an
106+
arbitrary number of parameters which should be inserted into the translation.
107+
108+
**Example:**
109+
110+
You have this text in your message bundle:
111+
112+
```properties
113+
CAROUSEL_DOT_TEXT=Item {0} of {1} displayed
114+
```
115+
116+
Your hook call would now look like this:
117+
118+
```js
119+
const [carouselText, pleaseWaitText] = useI18nText('myApp', ['CAROUSEL_DOT_TEXT', 5, 20], 'PLEASE_WAIT');
120+
```
121+
122+
This would resolve this text:<br />
123+
`Item 5 of 20 displayed`

docs/6-Efficient-Bundling.stories.mdx

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { Meta } from '@storybook/addon-docs/blocks';
2+
3+
<Meta title="Efficient Bundling" />
4+
5+
# Efficient Bundling
6+
7+
This section describes some options on how to optimize your bundle size.
8+
9+
**Following these steps require you to modify the default `create-react-app` setup. This might result in broken applications!**<br />
10+
**We can't provide any support for custom build setups.**
11+
12+
UI5 Web Components (for React) aim to be feature rich and with a minimal code footprint at the same time, so we are
13+
providing several assets as `.json` files which can be optionally imported (like `themes`, `i18n translations`).
14+
As `create-react-app` is inlining all `.json` files by default, this could result in a very large bundle size.
15+
16+
Here are two options how to improve your bundle size:
17+
18+
## Option 1: Using `react-app-rewired`
19+
20+
[react-app-rewired](https://github.com/timarney/react-app-rewired) is tweaking the `create-react-app` config without
21+
the need to use `eject` or to fork `react-scripts`. <br />
22+
Please read through the projects' [`README`](https://github.com/timarney/react-app-rewired/blob/master/README.md) before continuing.
23+
24+
**Step by Step Guide**
25+
26+
1. Follow the setup instructions of [react-app-rewired](https://github.com/timarney/react-app-rewired#how-to-rewire-your-create-react-app-project)
27+
2. Add the following content to the `config-overrides.js` file:
28+
29+
```js
30+
module.exports = function override(config, env) {
31+
config.module.rules.push({
32+
test: /assets\/.*\.json$/,
33+
use: 'file-loader',
34+
type: 'javascript/auto'
35+
});
36+
return config;
37+
};
38+
```
39+
40+
## Option 2: Ejecting your `create-react-app`
41+
42+
Another option to take full control of configs in your app is to eject your `create-react-app` by running `npm run eject`
43+
in your project. Please read the `create-react-app` notes about the [eject command](https://github.com/facebook/create-react-app/blob/master/packages/cra-template/template/README.md#npm-run-eject) carefully:
44+
45+
> ### `npm run eject`
46+
>
47+
> **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
48+
>
49+
> If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
50+
>
51+
> Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
52+
>
53+
> You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
54+
>
55+
> _Quote from https://github.com/facebook/create-react-app/blob/master/packages/cra-template/template/README.md#npm-run-eject_
56+
57+
**Step by Step Guide**
58+
59+
1. Run `npm run eject` in your project
60+
2. Open `config/webpack.config.js` and look for the `oneOf` array inside of `module.rules`. Add the following `file-loader`
61+
anywhere in this array but **before the existing `file-loader`**:
62+
63+
```js
64+
{
65+
test: /assets\/.*\.json$/,
66+
use: 'file-loader',
67+
type: 'javascript/auto'
68+
}
69+
```
70+
71+
Your `config/webpack.config.js` should now look similar to:
72+
73+
```js
74+
...
75+
return {
76+
...
77+
module: {
78+
strictExportPresence: true,
79+
rules: [
80+
...,
81+
{
82+
// "oneOf" will traverse all following loaders until one will
83+
// match the requirements. When no loader matches it will fall
84+
// back to the "file" loader at the end of the loader list.
85+
oneOf: [
86+
...,
87+
{
88+
test: /assets\/.*\.json$/,
89+
use: 'file-loader',
90+
type: 'javascript/auto'
91+
},
92+
// "file" loader makes sure those assets get served by WebpackDevServer.
93+
// When you `import` an asset, you get its (virtual) filename.
94+
// In production, they would get copied to the `build` folder.
95+
// This loader doesn't use a "test" so it will catch all modules
96+
// that fall through the other loaders.
97+
{
98+
loader: require.resolve('file-loader'),
99+
// Exclude `js` files to keep "css" loader working as it injects
100+
// its runtime that would otherwise be processed through "file" loader.
101+
// Also exclude `html` and `json` extensions so they get processed
102+
// by webpacks internal loaders.
103+
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
104+
options: {
105+
name: 'static/media/[name].[hash:8].[ext]',
106+
},
107+
},
108+
// ** STOP ** Are you adding a new loader?
109+
// Make sure to add the new loader(s) before the "file" loader.
110+
],
111+
},
112+
],
113+
},
114+
...
115+
};
116+
};
117+
118+
```
119+
120+
<br />
121+
122+
_In case you also have other alternatives for improving the bundle size, please feel free to submit a pull request!_

package.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@
2020
"lerna:version-dryrun": "lerna version --conventional-graduate --no-git-tag-version --no-push"
2121
},
2222
"dependencies": {
23-
"@storybook/addon-actions": "6.0.0-rc.5",
24-
"@storybook/addon-controls": "6.0.0-rc.5",
25-
"@storybook/addon-docs": "6.0.0-rc.5",
26-
"@storybook/addon-knobs": "6.0.0-rc.5",
27-
"@storybook/addon-toolbars": "6.0.0-rc.5",
28-
"@storybook/addons": "6.0.0-rc.5",
29-
"@storybook/cli": "6.0.0-rc.5",
30-
"@storybook/react": "6.0.0-rc.5",
31-
"@storybook/theming": "6.0.0-rc.5",
23+
"@storybook/addon-actions": "6.0.0-rc.8",
24+
"@storybook/addon-controls": "6.0.0-rc.8",
25+
"@storybook/addon-docs": "6.0.0-rc.8",
26+
"@storybook/addon-knobs": "6.0.0-rc.8",
27+
"@storybook/addon-toolbars": "6.0.0-rc.8",
28+
"@storybook/addons": "6.0.0-rc.8",
29+
"@storybook/cli": "6.0.0-rc.8",
30+
"@storybook/react": "6.0.0-rc.8",
31+
"@storybook/theming": "6.0.0-rc.8",
3232
"@ui5/webcomponents": "1.0.0-rc.7",
3333
"@ui5/webcomponents-fiori": "1.0.0-rc.7",
3434
"@ui5/webcomponents-icons": "1.0.0-rc.7",

0 commit comments

Comments
 (0)