Shift3 / boilerplate-client-react

The Bitwise standard starting point for new React web projects.
7 stars 10 forks source link

Implement complete i18n translation coverage for static front-end content, English and Spanish #762

Closed coreyshuman closed 1 year ago

coreyshuman commented 1 year ago

Changes

  1. Apply i18n translation to all static front-end content: labels, buttons, headers, toasts, etc
  2. Refactored some hard-coded content to be data-driven and translatable (app name, environment)
  3. Refactored Constants file and yup schemas to support translation

Purpose

This PR adds comprehensive i18n coverage for English and Spanish (Latin America). All static content on the front-end application is included.

User input and server-driven content is NOT translated. Date and time localization is NOT included.

Approach

This PR builds on the initial translation efforts which had already began on this project. We use the react-i18next package to power our translation functionality. I split translations into two files: common.json and translation.json in an attempt to separate reusable common entries from page-specific translations.

Learning

The useTranslation hook and t() functions are the primary approach for implementing translation, as recommended by the react-18next documentation. This functional approach is nice because it can also be used for programmatic translations, such as passing a localized string to a notification toast. It also has the advantage of updating in real-time. The limitation is that this approach cannot do interpolation or complex components.

https://react.i18next.com/latest/usetranslation-hook

Example:

import React from 'react';
import { useTranslation } from 'react-i18next';

export function MyComponent() {
  // load multiple namespaces
  // the t function will be set to first namespace as default
  const { t, i18n } = useTranslation(['ns1', 'ns2', 'ns3']);

  t('key'); // will be looked up from namespace ns1
  t('key', { ns: 'ns2' }); // will be looked up from namespace ns2
  return <p>{t('my translated text')}</p>
}

The <Trans> component is used when more complex translations are needed, such as strings with username or email populated in them. This approach will not automatically update in real-time, so component re-render needs to be handled manually.

https://react.i18next.com/latest/trans-component

Example:

import { Trans } from 'react-i18next';

function MyComponent({ person, messages }) {
  const { name } = person;
  const count = messages.length;

  return (
    <Trans i18nKey="userMessagesUnread" count={count}>
      Hello <strong title={t('nameTitle')}>{{name}}</strong>, you have {{count}} unread message. <Link to="/msgs">Go to messages</Link>.
    </Trans>
  );
}

This approach is also useful when you need to support pluralization, which is enabled when supplying the count prop.

https://www.i18next.com/translation-function/plurals

In that case, the pluralized translation strings for the above code example would look like this:

"nameTitle": "This is your name",
"userMessagesUnread_zero": "Hello <1>{{name}}</1>, you have no unread messages.",
"userMessagesUnread_one": "Hello <1>{{name}}</1>, you have {{count}} unread message. <5>Go to message</5>.",
"userMessagesUnread_other": "Hello <1>{{name}}</1>, you have {{count}} unread messages.  <5>Go to messages</5>.",

Pre-Testing TODOs

None, just pull down the branch as normal.

Testing

  1. Pull in the changes to your local copy of this branch and restart Docker.
  2. Toggle your User Preferences back and forth between English and Spanish while spot-checking forms, pages, dropdowns, menus, etc.
  3. Also spot-check auth pages, toasts, and notifications.

Closes Proofhub task: https://bitwiseindustries.proofhub.com/bapplite/#app/todos/project-7536720926/list-263379767795/task-303770912950

coreyshuman commented 1 year ago

Notifications Page

Editor Home Page

Search bar

Changelog

Copyright Line

Change my Email modal

Verify Email Change Page

And I found a few other cases were notifications were not translated. We currently hand notifications in a mixture of places including pages, hooks, and services. It's a bit confusing and hard to tell who should be responsible for what. I'll add a note to take a look at this for possible refactor.

Updated commit will come in later tonight.