@interdan/react-localized-texts
v1.0.11
Published
Make static texts editable, supports multi languages
Downloads
235
Readme
React localized texts
Library allows to achieve two main goals for your React application:
- add localization: your app can have two or more languages
- ability to edit your text content right inside your application and observe the results instantly (for one, two, three or more languages)
The library supports the nexts features:
- pure text or html format
- variables (substitutions) inside your text values
- intelligent code completion of the text keys (tested in VS code)
- mobile view mode
- __Type__Script
Installation: texts are just pure strings
npm install @interdan/react-localized-texts
Installation: texts can be pure strings or html content as well
npm install @interdan/react-localized-texts
npm install @tinymce/tinymce-react
npm install react-html-parser
In general "react-html-parser" not required, you can use any function that build react element from the html string.
"texts.js" file (or "texts.ts" for TypeScript)
To store all text keys it's necessary to create a "texts" object. The keys of this object are keys of all possible texts that should be accessible in your source code. But you can request additional keys from your API along with their values. The values of this "texts" object are collections of all possible parameters for a text template. If the text isn't expected to have parameters - that object must be empty. There is only one exception - special value for that object "pureString: true". It used along with HTML content. "pureString: true" - means that this particular text value shouldn't contain HTML content since it used as text input placeholder, select's option value, etc. somewhere where ONLY strings are allowed. Here is an example of "texts" file:
import { transformTextsIfProxyNotSupported, tryCreateTextsES6Proxy } from '@interdan/react-localized-texts';
const x = null;
const texts = {
hi_there: { pureString: true },
empty_text_example: { },
current_time_is: { time: x },
multiline_text_example: {},
enter_your_name_click_to_edit: { pureString: true },
};
const resultTexts = Object.assign(tryCreateTextsES6Proxy(), texts);
transformTextsIfProxyNotSupported(resultTexts);
export default resultTexts;
The value of the "x" variable could be anything, you can use 0, 1 instead of variable "x" at all. "x" used just for better readability. Use this "tryCreateTextsES6Proxy" and "transformTextsIfProxyNotSupported" as in this example - that methods just convert the original texts object values to react elements or functions that expect parameter values and also return react elements.
Using texts in your code
To reference texts in the app just import this "texts" object:
import texts from '<path to file>/texts';
and use it inside your .jsx (or .tsx)
return (
<div>
<p>{texts.hi_there}</p>
<p>{texts.empty_text_example}</p>
<p>
{texts.current_time_is({ time: <b>{time}</b> })}
</p>
<p>
Value can be a string or number: {texts.current_time_is({ time: "12:00" })}
</p>
{texts.multiline_text_example}
{texts["Dynamic key. If no value provided doesn't work in IE (shows empty string instead)"]}
<div className="input-example">
<TextSourceProvider text={texts.enter_your_name_click_to_edit} propertyToSet="placeholder">
<input type="text" />
</TextSourceProvider>
</div>
</div>
);
As you can see from example, if your text has parameter (or parameters) you must use it as a function with a single object parameter. That object should contain values for the same keys you have declared in the "texts" file. Look one more time on this "current_time_is" example:
in "texts" file we have
current_time_is: { time: x },
in JSX we have
<p>
{texts.current_time_is({ time: <b>{time}</b> })}
</p>
Text parameter value type can be number, string any valid react element.
If the key stored in some variable (they are delivered from your API) and not specified in the "texts" file you have to use square brackets (just a standard way to access JavaScript object value):
<p>
{texts[myCompanyNameTextKey]}
</p>
If you for some reason (testing, scaffolding, etc.) don't want to declare text key in the "texts" file your code will be still workable (thanks to ES6 proxy, but not supported by IE):
<h3>This is allowed:</h3>
<p>
{texts['any key I have in my mind']}
{texts.justTestKey}
</p>
Texts values initialization
To set texts values (that can be stored locally or be retrieved from your API) you should use the next JSON format:
const myTextsValues = {
texts: [
{
key: "keyOne",
pureString: false,
values: [
{
language: "en",
value: "Hi there",
},
{
language: "fr",
value: "Salut",
},
],
},
{
key: "current_time_is",
values: [
{
language: "en",
value: "Current time is: {{time}}",
},
{
language: "fr",
value: "[ Not translated yet ]",
},
],
},
...
]
};
For texts with parameters use the curly braces to refer the parameter by name:
value: "Current time is: {{time}}"
To set texts values use method "loadTexts":
import { loadTexts } from '@interdan/react-localized-texts';
loadTexts(myTextsValues);
Text editor initialization
To work with library you need to have languages array (it's easy to delare as an object) and "save text values" method. Here is an examples:
const languages = {
en: 'en',
fr: 'fr',
de: 'de',
dk: 'dk',
};
const TEXTS_VAR_NAME = "texts JSON";
const saveTextValues = (textJson, successCallBack) => {
localStorage.set(TEXTS_VAR_NAME, JSON.stringify(textJson));
console.log('textJson: ', textJson);
// mock of API request delay
window.setTimeout(() => successCallBack({ success: true }), 2000);
};
On the top level of your app, you have to init library:
import { initLocalizedTextsLibrary, loadTexts } from '@interdan/react-localized-texts';
initLocalizedTextsLibrary({
saveTextValues,
languagesList: Object.values(languages),
});
loadTexts(myTextsValues);
If you want HTML support for you text variables additional property required:
import { initLocalizedTextsLibrary, loadTexts } from '@interdan/react-localized-texts';
import ReactHtmlParser from 'react-html-parser';
import { Editor } from '@tinymce/tinymce-react';
const TINY_API_KEY = 'your secred TINY API KEY';
initLocalizedTextsLibrary({
saveTextValues,
languagesList: Object.values(languages),
htmlToReactBuilder: ReactHtmlParser,
textValueEditor: buildTinyEditor(Editor, TINY_API_KEY),
});
loadTexts(myTextsValues);
The last step for setting up: wrapping your app tree with TextsEditor:
import { TextsEditor } from '@interdan/react-localized-texts';
const lang = languages.en;
<TextsEditor language={lang}>
<MyApp />
</TextsEditor>
With Redux it makes sence to connect TextEditor:
import { TextsEditor } from '@interdan/react-localized-texts';
import { connect } from 'react-redux';
import { getCurrentLanguage } from '<my selectors path>';
const mapStateToProps = state => ({ language: getCurrentLanguage(state) });
const ConnectedTextsEditor = connect(mapStateToProps)(TextsEditor);
<ConnectedTextsEditor>
<MyApp />
</ConnectedTextsEditor>
Using texts with inputs
To use texts with inputs you need "TextSourceProvider", it is a React component that exported by default from this library. Here is a simple example of how you can use it to set placeholder for your text input:
import TextSourceProvider from '@interdan/react-localized-texts';
<TextSourceProvider text={texts.enter_your_name} propertyToSet="placeholder">
<input type="text" />
</TextSourceProvider>
or for select's option:
import TextSourceProvider from '@interdan/react-localized-texts';
const StandardOption = ({ value, label }) => <option value={value}>{label}</option>;
const LocalizedOption = ({ value, label }) => (
<TextSourceProvider text={label} propertyToSet="label">
<StandardOption value={value} />
</TextSourceProvider>
);
Required
propertyToSet
property - just a child's property name you want to fullfill with string value.Required
text
property accepts the text you want to be set to child element as a string value (placeholder and option's content in examples).
text
could be a text from "texts" object collection, or just a string key. This two lines of code produce the equivalent results:
<TextSourceProvider text={texts.years} propertyToSet="label">
and
<TextSourceProvider text="years" propertyToSet="label">
The only benefit for the first approach is intelligent code completion
text
also could be a function. Sometime you need your string value to be formatted with some custom formatter. The formatter function should have the next form:
import TextSourceProvider from '@interdan/react-localized-texts';
import texts from './multilang/texts';
export const formatWithYears = (toText, value) => {
const isOne = parseInt(value, 10) === 1;
return toText`${value} ${isOne ? texts.year : texts.years}`;
};
the first parameter if always ES6 Tag Function - toText
that you have to apply to returned ES6 Template string as in example above. Other parameters are the same that you passed to TextSourceProvider with the optional textParameters
property as an array.
The full example of using text
as a function:
// reusable formatter
const formatWithYears = (toText, value) => {
const isOne = parseInt(value, 10) === 1;
return toText`${value} ${isOne ? texts.year : texts.years}`;
};
// reusable component
const StandardOption = ({ value, label }) => <option value={value}>{label}</option>;
// reusable component
const SelectOption = ({ value, getText, textParameters }) => (
<TextSourceProvider text={getText} textParameters={textParameters} propertyToSet="label">
<StandardOption value={value} />
</TextSourceProvider>
);
<select value={1} {...otherProps}>
<SelectOption value={1} getText={formatWithYears} textParameters={[1]} />
...
</select>
showTextEditor function
To show texts editor and make it possible to click on any text and edit them use function showTextEditor
:
import { showTextEditor } from '@interdan/react-localized-texts';
// some code
...
showTextEditor();
Library helpers functions
keyOf (textReactElement)
- extracts the key from the text: keyOf(texts.years)
will returns "years"
stringOf (textReactElement, language)
- gets the string value for provided texts and language.
toReactElement
- ES6 Tag Function, allows to build a React element with a string template:
import { toReactElement } from '@interdan/react-localized-texts';
const SomeComponent = ({ durationDays }) => {
return toReactElement`${texts.it_will_take} ${<b>{durationDays}</b>} ${texts.days}`;
};
The result of toReactElement
will be the React element or just a string (if all the parameters are primitives)