Skip to content

Commit

Permalink
Update README
Browse files Browse the repository at this point in the history
  • Loading branch information
kisvegabor committed Mar 18, 2019
1 parent 9b4a3a9 commit 60460a4
Showing 1 changed file with 106 additions and 129 deletions.
235 changes: 106 additions & 129 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,34 @@ lv_i18n - Internationalization for LittlevGL
[![Build Status](https://img.shields.io/travis/littlevgl/lv_i18n/master.svg?style=flat)](https://travis-ci.org/littlevgl/lv_i18n)
[![NPM version](https://img.shields.io/npm/v/lv_i18n.svg?style=flat)](https://www.npmjs.org/package/lv_i18n)

> Translate your firmware with ease!
Lightweight [gettext](https://www.gnu.org/software/gettext/) replacement
tools for C. Add multi-language support to your embedded projects with ease.

Usage example
-------------

Translations compiler will give you 2 files (`lv_i18n.h` & `lv_i18n.c`) for use
in your program. Those include everything you need and not require additional
libraries.
## Quick overview

Initialize i18n module, somewhere in your code:
1. Mark up the text in your C files as `_("some text")` (singular) and `_p("%d item", item_cnt)` (plural)
2. Create template `yml` files for the translations you are interested in
3. Run `extract` to fill the `yml` files with the texts in `_()` and `_p()`
4. Add the translations into the `yml` files
5. Run `compile` to convert the `yml` files to a C and H file. They will contain the translations and all the background functions you need.

```c
#include "lv_i18n.h"

// Load translations & default locale (usually, done once)
lv_i18n_init(lv_i18n_lang_pack);
// Set active locale (can be switched anytime)
lv_i18n_set_locale("ru-RU");
```
## Install the script

Then, in all your sources:
[node.js](https://nodejs.org/en/download/) required.

```c
#include "lv_i18n.h"
Global install of the last version, execute as "lv_i18n"

const char* mytext = _("my text to translate");
const char* my_plural_text = _p("my text to translate", number);
```sh
npm i lv_i18n -g
```

CLI tools install
-----------------
**Alternatives:**

[node.js](https://nodejs.org/en/download/) required.
Install from github's repo, master branch

```sh
# "global" install of last version, execute as "lv_i18n"
npm i lv_i18n -g
# install from github's repo, master branch
npm i littlevgl/lv_i18n -g
```

Expand All @@ -57,158 +47,145 @@ Then commit `package.json` & put `/node_modules` into `.gitignore`. Next time
use just `npm i` to install.


Translation workflow
--------------------

Here are simplified steps:
## Mark up the text in your code

- Wrap texts in your code with `_()` and `_p()` wrappers. Without
translations your program will continue work "as is", returning wrapped texts.
- Anytime, run "extractor" to analyze existing code, translations, and fill
translation files with texts to translate.
- Translate (edit `.yml` files, replace `~` with translated texts).
- Run "compiler" to generate `lv_i18n.h` & `lv_i18n.c` files,then rebuild your
program

If you change base phrases in source code, their existing translations become
"lost". CLI tools allow search such phrases, and "rename" or remove keys in all
translation at once, to minimize handwork.
```c
#include "lv_i18n/lv_i18n.h" /*Assuming you have the translations here. (See below)*/

/* Load translations & default locale (usually, done once) */
lv_i18n_init(lv_i18n_lang_pack);

C API
-----
/* Set active locale (can be switched anytime) */
lv_i18n_set_locale("ru-RU");

### int lv_i18n_init(const lv_i18n_lang_pack_t * langs)
/* The translation of "title1" will be returned according to the selected locale.
* ("title1" is only a uniqu ID of the text.) Example:
* en-GB: "Main menu"
* ru_RU: "Главное меню"
*/
gui_set_text(label, _("title1"));

/* According to `user_cnt` different text can be returned
* en-GB `user_cnt == 1` : "One user is logged in"
* `user_cnt == 6` : "%d users are logged in"
*/
char buf[64];
sprintf(buf, _p("user_logged_in", user_cnt)), user_cnt); /*E.g. buf == "7 users are logged in"*/
gui_set_text(label, buf);
```
Attach generated translations to be used by `lv_i18n_get_text()`. Return 0
on success, -1 on fail.
`_` and `_p` are normal functions. They just have this short name to enable fast typing of texts.
Rules of getting the translation:
- If the translation is not available on the selected locale then the default language will be used instead
- If the translation is not available on the default locale the text ID ("title1" in the example) will be returned
### int lv_i18n_set_locale(const char * l_name)
Set locale to be used by `lv_i18n_get_text()`.
## Create template yml files for the translations
- _l_name_ - locale name (`en-GB`, `ru-RU`).
- Locale names are not restricted to standard list, but only ASCII chars
allowed (for simple normalization).
For each translation, you need to create a `yml` file with "language code" name. For example:
Returns 0 on success, -1 if locale not found.
- en-GB.yml
- ru-RU.yml
Here is a [list](https://www.andiamo.co.uk/resources/iso-language-codes/) of the language and locale codes.
### const char * lv_i18n_get_text(const char * msg_id)
Add the `\<locale-name\>: ~` line to the `yml` files. Replace "language-code" with the actual language code.
E.g.: `en-GB: ~` or simply `en: ~`
Mapped to `_(...)` or `_t(...)` via `#define`
Technically you can have one `yml` file where you list all language codes you need but its more modular to separate them.
Get translated text. If not translated, return fallback (try default locale
first, then input param if default not exists)
## Run extract to fill the yml files
### char* lv_i18n_get_text_plural(char* text, int32_t plural)
Run `extract` like this (assuming your source files are in the `src` folder an the `yml` files in the translations folder):
Mapped to `_p(...)` or `_tp(...)` via `#define`
```sh
lv_i18n extract -s 'src/**/*.+(c|cpp|h|hpp)' -t 'translations/*.yml'
```

Get plural form of translated text. Use current local to select proper plural
algorythm. If not translated, fallback to default locale first, then to input
param.
It will fill the `yml` files the texts marked with `_` and `_p`.
For example:

```yml
en-GB:
title1: ~
user_logged_in:
one: ~
other: ~
```
### CLI tools
CLI tools are used to simplify translation process and generate `.c` file with
optimized data. Utilities are written in JS and require
[node.js](https://nodejs.org/en/download/) to work. After node.js been
installed, type this:
## Add the translations into the yml files
```sh
npm install -g lv_i18n
```
The naming conventions in the `yml` files follow the rules of [CLDR](http://cldr.unicode.org/translation/language-names) so most of the translation offices will know them.

This will install utilities and create shourtcut `lv_i18n` to call from terminal.
You can also use local install and type explicit path to `lv_i18n.js`:
Example:

```sh
npm install lv_i18n
```yml
'en-GB':
title1: Main menu
user_logged_in:
one: One user is logged in
other: %d users are logged in
```
## Run compile to convert the yml files to a C and H file

All "actions" use similar pattern:
Once you have the translations in the `yml` files you only need to run the `compile` to generate a C and H files from the `yml` files. No other library will be required to get the translation with `_()` and `_p`.

- Load source C/CPP files and extract phrases.
- Load yaml translations to understand existing progress.
- Do something useful (command):
- Fill yaml files with new phrases to translate
- Check errors and inconsistencies
- Generate ("compile") `.c` file with optimized translation data.

See help:
Running `compile`:

```sh
lv_i18n -h
lv_i18n extract -h
lv_i18n compile -h
lv_i18n compile -t 'translations/*.yml' -o 'src/lv_i18n'
```

Examples:
The deafult locale is `en-GB` but you change it with `-l 'language-code'`.

## Follow modifications in the source code
To change a text id in the `yml` files use:
```sh
lv_i18n extract -s 'src/**/*.+(c|cpp|h|hpp)' -s inc/**/* -t src/i18n/*.yml
lv_i18n compile -t src/i18n/*.yml -o src/translations.c
lv_i18n rename -t src/i18n/*.yml --from 'Hillo wold' --to 'Hello world!'
```

[Glob expressions](https://github.com/isaacs/node-glob#glob-primer) supported.
## C API

Note for Windows: use *only forward-slashes* in glob expressions.
#### int lv_i18n_init(const lv_i18n_lang_pack_t * langs)
Attach generated translations to be used by `lv_i18n_get_text()`.

- _return_ - 0 on success, -1 on fail.

Translation files format
------------------------
___

Translations are stored in `.yaml` (or `.json`) files. Structure is similar to
one in [Ruby on Rails](https://guides.rubyonrails.org/i18n.html), with minor
simplifications:
#### int lv_i18n_set_locale(const char * l_name)
Set locale to be used by `lv_i18n_get_text()`.

```yaml
# Default locale
'en-GB':
# May be useful if shortcuts used instead of real texts.
# For example: `msg_file_missed`. When real texts used, default locale
# doesn't need translation for singular phrases (leave "null" here)
Settings: ~
# Plurals - should be filled
Nut:
# Valid plural form names are `zero`, `one`, `two`, `few`, `many`, `other`
# Each language has specific set of required forms.
one: Nut
many: Nuts
'LVGL is awesome!': ~

# Example of additional locale. Usually, placed to separate file.
'ru-RU':
Settings: Настройки
Nut:
one: Гайка
few: Гайки
many: Гаек
'LVGL is awesome!': ~ # null (still untranslated), added by extractor
```
- _l_name_ - locale name (`en-GB`, `ru-RU`).
- _returns_ - 0 on success, -1 if locale not found.

- No nesting subtrees, `foo.bar.baz` will be just a plain string
___

It's possible to store everything in one file, but we strongly recommend keep
each locale in separate one. When you run CLI tools and pass multiple
files, all data will be joined automatically.
#### const char * lv_i18n_get_text(const char * msg_id)
Mapped to `_(...)` or `_t(...)` via `#define`

If you wish add new locale - just create a new file with this content:
Get translated text. If not translated, return fallback (try default locale
first, then input param if default not exists)
- _msg_id_ - The ID of a text to translate (e.g. `"title1"`)
- _return_ - pointer to the traslation

```yaml
your_new_locale_name: ~
```
___

#### char* lv_i18n_get_text_plural(char* msg_id, int32_t plural)
Mapped to `_p(...)` or `_tp(...)` via `#define`

Then run extractor to fill missed keys.
Get the plural form of translated text. Use current locale to select plural
algorithm. If not translated, fallback to default locale first, then to input
param.

- _msg_id_ - The ID of a text to translate (e.g. `"title1"`)
- _plural_ - number of items to decide which plural for to use
- _return_ - pointer to the traslation

References:
-----------
## References:

To understand i18n principles better, you may find useful links below:

Expand Down

0 comments on commit 60460a4

Please sign in to comment.