Translations (@lefapps)
This package should be accompanied by @lefapps/translations-server
to get the fulle experience.
Setup
Install from npm:
npm i @lefapps/translations
Import in your app:
import { Translator } from '@lefapps/translations'
Define a translator configuration:
const languages = ['nl', 'fr', 'en'] // you are free to decide how they look
const defaultLanguage = languages[0]
const translatorConfig = {
languages,
language:
typeof navigator !== 'undefined'
? (navigator.language || navigator.userLanguage).split('-')[0] || defaultLanguage
: defaultLanguage,
canEdit: () => true // must be a function
}
Wrap your app code:
const App = () => {
return (
<Translator {...translatorConfig}>
// App code
</Translator>
)
}
Note: changing the language can be done by changing the language prop in the config.
Use the Translate Component:
import { Translate } from '@lefapps/translations'
const Content = page => {
return <Translate _id={page} />
}
Access the raw translation string:
import { useRawTranslation } from '@lefapps/translations'
const Logo = ({ src }) => {
const { translation } = useRawTranslation('menu/logo/alt')
return <h1><img src={src} alt={translation} /></h1>
}
Include the languageSwitcher:
import { PickLanguage } from '@lefapps/translations'
const Header = () => {
return (
<header>
<h1>My Brand</h1>
<nav>
<ul />
<PickLanguage />
</nav>
</header>
)
}
Translate
Use the Translate
component to fetch translations using a specific identifier.
Props
Prop | Type | Required? | Default | Description |
---|---|---|---|---|
_id | String | ✓ | "" | Identifier of the translation (see guidelines) |
md | Bool | false | Formatted using MarkDown (html rendered using the markdown-it plugin) | |
tag | String Component |
span | HTML tag to wrap the translation | |
className | String | "" | Optional classnames for | |
params | Object | {} | Replace text: {{key}} gets replaced by its value
|
|
autoHide | Bool | false | Hide the component when the translation is empty | |
onInit | Func | null | Callback when the component is initialised (before loading) | |
onLoad | Func | null | Callback when the translation data is finished loading | |
children | String Nodes |
null | Initial value while loading | |
language | String | [current] | Force a different language to be loaded |
Guidelines
We recommend to define your identifiers with the following schema:
header
header/menu
header/menu/about
header/menu/about/team
header/menu/about/contact
etc...
This way you can easily group and organise them in your storage/database.
Attributes
The following HTML attributes are automatically applied on the element:
.translation: always present
.translation__loading: present while fetching
.translation__md: present when markdown formatting is applied
.translation__error: present when an error occurred while loading
[data-translation]: the identifier
*The component is automatically subtly styled while loading.
useRawTranslation
Use the useRawTranslation
hook to fetch translations as a string instead of React Component.
Notes
- To keep your code organised, only use
useRawTranslation
when necessary
(e.g. foralt
ortitle
attributes — these do not accept React Components). - The returned translation is the raw string. If the value contains MarkDown, you should take care of encoding it in your app.
Arguments
Accepts one argument, the identifier: useRawTranslation(_id)
.
Returns
Key | Type | Description |
---|---|---|
loading | Bool | True while the translation is being fetched |
error | Object | Populated with Apollo Error if something goes wrong |
translation | String | Translation for the identifier in the currently active language |
_id | String | Identifier of the translation |
md | Bool | Whether the returned string contains MarkDown |
params | Array | Available keys |
PickLanguage
Use this component to change language. it is automatically hidden when only one language is provided.
Props
Prop | Type | Required? | Default | Description |
---|---|---|---|---|
children | String Nodes |
null | Content to show as label of the dropdown | |
showTitle | Bool | false | Show the current language |
Attributes
The following HTML attributes are automatically applied on the element:
"#language-picker": always present
No default styling is applied on this component.
The languages shown in the picker are Translate components with the following identifier: translator/{{language}}
. Transalte them to your linking!
Internals
If you want to access certain dynamic fields from the translator, e.g. the current language, use the useTranslator hook.
import { useTranslator } from '@lefapps/translations'
const Page = () => {
const { languages, setLanguage } = useTranslator()
return (
<ul>
{languages.map(lang => {
return <li key={lang} onClick={() => setLanguage(lang)}>
{lang}
</li>
})}
</ul>
)
}
The same can be achieved with the HOC withTranslator
:
import { withTranslator } from '@lefapps/translations'
const SetEnglish = withTranslator(({ language }) => {
return (
<p>
This is the current language: {language}
<br/>
<button onClick={() => setLanguage('en')}>
Set current language to EN
</button>
</p>
)
})
Props
These values can be retrieved from the hook/hoc:
Prop | Type | Description |
---|---|---|
language | String | Currently selected language |
languages | [String] | All available languages |
setLanguage | Function(ln) | Change current language to ln
|
Data Fetching
The transltions are fetched over the wire using GraphQl queries and mutations. When your app is using graphql already, you can easily integrate the queries
. Use the companion package @lefapps/translations-server
to implement the backend correctly.
Editing
When the canEdit config returns a non-falsy value, the translated text shows an editor to edit the translation in place after double clicking it. Note: this is merely a client-side optimisation, you should always authenticate and validate incoming requests server-side.
Otherwise you can build your own backend list to edit each translation. An example:
import React, { useState } from 'react'
import { useQuery } from '@apollo/react-hooks'
import { queries, Editor } from '@lefapps/translations'
const Translation = ({ _id }) => {
const [isOpen, setOpen] = useState(false)
const onClick = () => setOpen(!isOpen)
const actions = { onClick }
const name = _id.split('/')
return (
<li key={_id}>
<h3 {...actions}>{name}</h3>
<Editor _id={_id} toggle={toggle} isOpen={isOpen} alert={alert} />
</li>
)
}
const Translations = () => {
const { data } = useQuery(queries.TRANSLATION_LIST)
return (
<section>
<h1>Translations</h1>
<ul>
{data.translations.map(Translation)}
</ul>
</section>
)
}