Skip to content

Commit

Permalink
Merge pull request #8 from slune-org/createMessages
Browse files Browse the repository at this point in the history
Check type when creating messages
  • Loading branch information
sveyret authored Jan 28, 2020
2 parents 633e32a + 27a352b commit fcb3b2e
Show file tree
Hide file tree
Showing 17 changed files with 1,649 additions and 992 deletions.
20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ $ yarn add intl-ts

# Language/langue

Because French is my native language, finding all documents and messages in French is not an option. Other translations are welcome.
Because French is my native language, you will find all documents and messages in French. Other translations are welcome.

Anyway, because English is the language of programming, the code, including variable names and comments, are in English.

Expand All @@ -43,7 +43,7 @@ Anyway, because English is the language of programming, the code, including vari

```typescript
// English version — default
const en = {
const en = createMessages({
$: 'English',
welcome: 'Welcome here!',
hello: (name: string) => `Hello ${name}`,
Expand All @@ -60,13 +60,13 @@ const en = {
}
}
},
}
})

// Type describing messages
type langType = typeof en

// French version — full
const fr: langType = {
const fr = createMessages<langType>({
$: 'Français',
welcome: 'Bienvenue ici !',
hello: (name: string) => `Bonjour ${name}`,
Expand All @@ -83,22 +83,24 @@ const fr: langType = {
}
}
},
}
})

// Canada french version — partial
const fr_ca: PartialMessages<langType> = {
const fr_ca = createMessages<PartialMessages<langType>>({
$: 'Français (Canada)',
welcome: 'Bienvenue icitte !',
}
})

// Esperanto version — partial
const eo: PartialMessages<langType> = {
const eo = createMessages<PartialMessages<langType>>({
$: 'Esperanto',
welcome: 'Bonvenon ĉi-tie!',
hello: (name: string) => `Saluton ${name}`,
}
})
```

The function `createMessages` is actually an identity function and is therefore not mandatory. Its sole purpose is to allow TypeScript to check the type of the given messages.

Note that the message names _must not contain one of the keyword_ of the [Intl API](doc/api.md#intlt-extends-messages).

- Create the corresponding language map:
Expand Down
6 changes: 5 additions & 1 deletion doc/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ This interface describes how objects containing messages for a given language sh

It is advised not to use keys starting with `$` in messages, to prevent conflict with the `Intl` reserved keywords; but it is mandatory that, for each language, a `$` entry is provided with the name of language. Usually, the name of the language is specified in the language itself.

Do not type your default language with `Message`, because if you do so, `typeof` will return `Message` and you will loose the specific type checking of your messages. If your default language is not written as expected, a compile time error will be raised when the language map is created.
Do not type your default language with `Message`, because if you do so, `typeof` will return `Message` and you will loose the specific type checking of your messages. Use the function `createMessages` to check the messages type.

# PartialMessages\<T extends Messages>

This type may be used to indicate a partial translation. It is similar to `Partial<T>` except that the `$` entry is mandatory.

# createMessages\<T extends Messages>(messages: T): T

This identity function does nothing by itself. It is advised to use it anyway for creating messages of a given language as it will allow TypeScript to check for the message types.

# LanguageMapDefinition\<T extends Messages>

This interface describes the data of a full language map. It may be used to retrieve a serialized language map, for exemple when a language map is transmitted from server to browser (see [examples](./examples.md)).
Expand Down
15 changes: 7 additions & 8 deletions doc/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Those two projects very basics do exactly the same thing as seen by a final user
In order to test one of those project, change to its directory and execute the command:

```bash
$ npm install && npm run go
$ npm install && npm start
```

You can then observe the result with a browser going at the address: http://localhost:8080.
Expand Down Expand Up @@ -52,7 +52,7 @@ The main file will be for the default (reference) language. By the way, you can

```typescript
// locale/en.ts
export const messages = {
export const messages = createMessages({
$: 'English',
welcome: 'Welcome here!',
hello: (name: string) => `Hello ${name}!`,
Expand Down Expand Up @@ -88,7 +88,7 @@ export const messages = {
}
return time
},
}
})
```

## Full translation
Expand All @@ -101,7 +101,7 @@ A full language file will match the default language type to ensure no entry is
// locale/fr.ts
import { messages as defLang } from './en'

export const messages: typeof defLang = {
export const messages = createMessages<typeof defLang>({
$: 'Français',
welcome: 'Bienvenue ici !',
hello: (name: string) => `Bonjour ${name} !`,
Expand Down Expand Up @@ -129,7 +129,7 @@ export const messages: typeof defLang = {
}
return time
},
}
})
```

## Partial translation
Expand All @@ -138,13 +138,12 @@ A partial language file will match a `PartialMessages` of the default language t

```typescript
// locale/fr_ca.ts
import { PartialMessages } from 'intl-ts'
import { messages as defLang } from './en'

export const messages: PartialMessages<typeof defLang> = {
export const messages = createMessages<PartialMessages<typeof defLang>>({
$: 'Français (Canada)',
welcome: 'Bienvenue icitte !',
}
})
```

Note that, by default, when the `Français (Canada)` will be selected, the `fr_ca` preference will be given to the `Intl` object which will automatically add the `fr` preference as second choice, which will correctly fill the translations (this is the result of the `createGenerics` option in the constructor and in the `$changePreferences()` method). As a last choice, if translation is still not found, the default language is used.
Expand Down
20 changes: 11 additions & 9 deletions doc/fr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ $ yarn add intl-ts

# Langue

Le français étant ma langue maternelle, fournir les documents et messages en français n'est pas une option. Les autres traductions sont bienvenues.
Le français étant ma langue maternelle, vous trouverez tous les documents et messages en français. Les autres traductions sont bienvenues.

Cependant, l'anglais étant la langue de la programmation, le code, y compris les noms de variable et commentaires, sont en anglais.

Expand All @@ -35,7 +35,7 @@ Cependant, l'anglais étant la langue de la programmation, le code, y compris le

```typescript
// Version anglaise — défaut
const en = {
const en = createMessages({
$: 'English',
welcome: 'Welcome here!',
hello: (name: string) => `Hello ${name}`,
Expand All @@ -52,13 +52,13 @@ const en = {
}
}
},
}
})

// Le type décrivant les messages
type langType = typeof en

// Version française — complète
const fr: langType = {
const fr = createMessages<langType>({
$: 'Français',
welcome: 'Bienvenue ici !',
hello: (name: string) => `Bonjour ${name}`,
Expand All @@ -75,22 +75,24 @@ const fr: langType = {
}
}
},
}
})

// Version en français canadien — partielle
const fr_ca: PartialMessages<langType> = {
const fr_ca = createMessages<PartialMessages<langType>>({
$: 'Français (Canada)',
welcome: 'Bienvenue icitte !',
}
})

// Version en Espéranto — partielle
const eo: PartialMessages<langType> = {
const eo = createMessages<PartialMessages<langType>>({
$: 'Esperanto',
welcome: 'Bonvenon ĉi-tie!',
hello: (name: string) => `Saluton ${name}`,
}
})
```

La function `createMessages` est en réalité une fonction identité et n'est donc pas nécessaire. Son seul objectif est de permettre à TypeScript de contrôler le type des messages fournis.

Notez que les noms des messages _ne doivent pas contenir l'un des mots-clés_ de l'[API Intl](api.md#intlt-extends-messages).

- Créez la table de langues correspondante :
Expand Down
6 changes: 5 additions & 1 deletion doc/fr/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ Cette interface décrit comment écrire les objets contenant les messages pour u

Il est conseillé de ne pas utiliser de clé commençant par `$` dans les messages, afin d'éviter les conflits avec les mots-clés réservés de `Intl` ; mais il est nécessaire que, pour chaque langue, une entrée `$` soit fournie, avec le nom de la langue. En général, le nom de la langue est spécifiée dans la langue elle-même.

Ne typez pas votre langue par défaut avec `Message`, car dans ce cas, `typeof` retournera `Message` et vous perdrez la vérification spécifique de vos messages. Si la langue par défaut n'est pas écrite comme attendu, une erreur de compilation sera levée lorsque la table de langues sera créée.
Ne typez pas votre langue par défaut avec `Message`, car dans ce cas, `typeof` retournera `Message` et vous perdrez la vérification spécifique de vos messages. Utilisez la function `createMessages` pour vérifier le type des messages.

# PartialMessages\<T extends Messages>

Ce type peut être utilisé pour indiquer une traduction partielle. Il est similaire à `Partial<T>` sauf que l'entrée `$` est obligatoire.

# createMessages\<T extends Messages>(messages: T): T

Cette fonction identité ne fait rien en soi. Il est tout de même conseillé de l'utiliser lors de la création des messages pour une langue donnée, car elle permet à TypeScript de vérifier le type des messages.

# LanguageMapDefinition\<T extends Messages>

Cette interface décrit les données d'une table de langues complète. Elle peut être utilisée pour récupérer une table de langues sérialisée, par exemple, lorsqu'une table de langues est transmise par un serveur à un navigateur (cf. [exemples](./examples.md)).
Expand Down
15 changes: 7 additions & 8 deletions doc/fr/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Ces deux projets très basiques font exactement la même chose du point de vue d
Pour tester l'un de ces projet, déplacez-vous dans son répertoire et exécutez la commande :

```bash
$ npm install && npm run go
$ npm install && npm start
```

Vous pourrez ensuite observer le résultat à l'aide d'un navigateur en vous rendant à l'adresse http://localhost:8080.
Expand Down Expand Up @@ -52,7 +52,7 @@ Le fichier principal sera pour la langue par défaut (référence). Vous pouvez

```typescript
// locale/en.ts
export const messages = {
export const messages = createMessages({
$: 'English',
welcome: 'Welcome here!',
hello: (name: string) => `Hello ${name}!`,
Expand Down Expand Up @@ -88,7 +88,7 @@ export const messages = {
}
return time
},
}
})
```

## Traduction complète
Expand All @@ -101,7 +101,7 @@ Un fichier de langue complet sera typé sur la langue par défaut afin de vérif
// locale/fr.ts
import { messages as defLang } from './en'

export const messages: typeof defLang = {
export const messages = createMessages<typeof defLang>({
$: 'Français',
welcome: 'Bienvenue ici !',
hello: (name: string) => `Bonjour ${name} !`,
Expand Down Expand Up @@ -129,7 +129,7 @@ export const messages: typeof defLang = {
}
return time
},
}
})
```

## Traduction partielle
Expand All @@ -138,13 +138,12 @@ Un fichier de langue partiel sera typé sur un objet `PartialMessages` du type d

```typescript
// locale/fr_ca.ts
import { PartialMessages } from 'intl-ts'
import { messages as defLang } from './en'

export const messages: PartialMessages<typeof defLang> = {
export const messages = createMessages<PartialMessages<typeof defLang>>({
$: 'Français (Canada)',
welcome: 'Bienvenue icitte !',
}
})
```

Notez que par défaut, lorsque le `Français (Canada)` sera sélectionné, la préférence `fr_ca` sera donnée à l'objet `Intl` qui ajoutera automatiquement la préférence `fr` en second choix, ce qui complétera les traductions correctement (c'est le résultat de l'option `createGenerics` du constructeur ou de la méthode `$changePreferences()`). En dernier choix, si la traduction n'est toujours pas trouvée, c'est la langue par défaut qui est utilisée.
Expand Down
4 changes: 2 additions & 2 deletions examples/content/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "dist/index.js",
"scripts": {
"test": "eslint src/*.ts src/**/*.ts src/**/*.tsx",
"go": "rimraf dist && tsc && webpack-cli && node ."
"start": "rimraf dist && tsc && webpack-cli && node ."
},
"author": "",
"license": "ISC",
Expand All @@ -19,7 +19,7 @@
"devDependencies": {
"@types/express": "^4.17.2",
"@types/glob": "^7.1.1",
"@types/node": "^12.12.8",
"@types/node": "^13.5.0",
"@types/react": "^16.9.11",
"@types/react-dom": "^16.9.4",
"@typescript-eslint/eslint-plugin": "^2.7.0",
Expand Down
6 changes: 4 additions & 2 deletions examples/content/src/locale/en.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export const messages = {
import { createMessages } from 'intl-ts'

export const messages = createMessages({
$: 'English',
welcome: 'Welcome here!',
displayTime: (date: string, time: string) => `We are ${date}, and it is ${time}.`,
Expand Down Expand Up @@ -59,4 +61,4 @@ export const messages = {
}
return time
},
}
})
6 changes: 3 additions & 3 deletions examples/content/src/locale/eo.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PartialMessages } from 'intl-ts'
import { PartialMessages, createMessages } from 'intl-ts'
import { messages as defLang } from './en'

export const messages: PartialMessages<typeof defLang> = {
export const messages = createMessages<PartialMessages<typeof defLang>>({
$: 'Esperanto',
welcome: 'Bonvenon ĉi-tie!',
displayTime: (date: string, time: string) => `Nun estas ${date} je ${time}.`,
Expand Down Expand Up @@ -71,4 +71,4 @@ export const messages: PartialMessages<typeof defLang> = {
}
return time
},
}
})
5 changes: 3 additions & 2 deletions examples/content/src/locale/fr.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/* eslint-disable no-irregular-whitespace */
import { createMessages } from 'intl-ts'
import { messages as defLang } from './en'

export const messages: typeof defLang = {
export const messages = createMessages<typeof defLang>({
$: 'Français',
welcome: 'Bienvenue ici !',
displayTime: (date: string, time: string) => `Nous sommes ${date} et il est ${time}.`,
Expand Down Expand Up @@ -54,4 +55,4 @@ export const messages: typeof defLang = {
}
return time
},
}
})
6 changes: 3 additions & 3 deletions examples/content/src/locale/fr_ca.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PartialMessages } from 'intl-ts'
import { PartialMessages, createMessages } from 'intl-ts'
import { messages as defLang } from './en'

export const messages: PartialMessages<typeof defLang> = {
export const messages = createMessages<PartialMessages<typeof defLang>>({
$: 'Français (Canada)',
welcome: 'Bienvenue icitte !',
}
})
Loading

0 comments on commit fcb3b2e

Please sign in to comment.