www.hubleto.eu Reddit User Guide Stack Overflow LinkedIn Community Report an issue

Translating React components using const T = Translator and T.translate

The pattern

Every functional component in the community apps creates one Translator object at the top of the file and names it T:

apps/Deals/Components/FC/FormDeal.tsx
import Translator from '@hubleto/react-ui/core/Translator';

const componentName = 'FormDeal'; // must be the same as the exported const
const parentApp = 'Hubleto/App/Community/Deals';
const T = new Translator(parentApp + '/Loader', 'Components/' + componentName);

Then every text in the component goes through T.translate():

apps/Deals/Components/FC/FormDeal.tsx (parts)
<Input title={T.translate("Lead")}>
  ...
</Input>

<span className='text'>{T.translate('Select parent lead')}</span>

<span className='text'>{T.translate('Price is calculated from items.')}</span>

tabs={{
  default: {title: <b>{T.translate('Deal')}</b>, content: () => <TabDefault {...props} />},
  items: {title: T.translate('Items'), content: () => <TabItems {...props} />},
  documents: {title: T.translate('Documents'), content: () => <TabDocuments {...props} />},
}}

The Translator class

@hubleto/react-ui/core/Translator.tsx
export default class Translator {
  context: string;
  contextInner: string;

  constructor(context: string, contextInner: string) {
    this.context = context;
    this.contextInner = contextInner;
  }

  translate(orig: string, context?: string, contextInner?: string, vars?: any): string {
    context = (context ?? this.context).replaceAll('/', '\\');
    contextInner = (contextInner ?? this.contextInner).replaceAll('/', '\\');

    try {
      return globalThis.hubleto.translate(orig, context, contextInner, vars);
    } catch (e) {
      return orig;
    }
  };
}
Constructor argument Value Resolves to
context parentApp + '/Loader' hubleto-app-community-deals-loader (the dictionary file)
contextInner 'Components/' + componentName Components\FormDeal (the key in the dictionary)

Translator arguments.

globalThis.hubleto.translate() converts the context to lower case with dashes, looks up the text in the dictionary and replaces the variables. The dictionary for the user's language is embedded in the desktop page by the Desktop controller, so no extra request is needed.

Signature of translate()

T.translate(orig: string, context?: string, contextInner?: string, vars?: any): string
Argument Description
orig English text.
context Optional. Overrides the context of the translator.
contextInner Optional. Overrides the inner context.
vars Optional. Values for {{ name }} placeholders.

Arguments of translate().

Examples

Simple text

<button className='btn btn-small btn-transparent' onClick={() => setShowMiddleNameInput(true)}>
  <span className='text'>{T.translate('Add middle name')}</span>
</button>

Text with a variable

const message = T.translate('{{ count }} deals without future plan', undefined, undefined, { count: dealsCount });

Text in another app's context

The Deals app adds a tab to the lead form. The tab title reuses the translation of the Deals app name from its manifest:

apps/Deals/Loader.tsx
title: globalThis.hubleto.translate('Deals', 'Hubleto\\App\\Community\\Deals\\Loader', 'manifest'),

Titles and labels passed as props

Translate before you pass the text. Components don't translate their props:

<Divider>{T.translate('Contacts')}</Divider>

<Form
  title={{fields: ['first_name', 'middle_name', 'last_name'], sub: T.translate('Contact')}}
  ...
/>

What you don't have to translate in React

Labels of inputs and column headers come from the model's describeColumns(), already translated by PHP ($this->translate('First name')). So <Input field='first_name' /> shows a translated label without any code in React.

Rules

Legacy class components

Class components (components/cc/...) set translationContext and translationContextInner properties and call this.translate():

Class component generated by php hubleto create mvc
export default class FormBook<P, S> extends FormExtended<FormBookProps, FormBookState> {
  translationContext: string = 'Hubleto\\App\\Custom\\MyFirstApp';
  translationContextInner: string = 'Components\\FormBook';

  renderTitle(): React.JSX.Element {
    return <small>{this.translate('Book')}</small>;
  }
}