Metadocumentation
open-collective
open-collective
  • Welcome
  • About
    • Introduction
    • Terminology
    • Company
    • Pricing
    • Community Guidelines
    • Values
    • Team
    • Investors
    • Contributing
  • Product
    • Features
    • Roadmap
    • Comparison
    • User Profile
    • Currencies
    • Log-in System
  • Collectives
    • FAQ
    • Creating a Collective
    • Quick Start Guide
    • Customize Collective
    • Change Core Contributors
    • Tiers & Goals
    • Add Fiscal Host
    • Change Fiscal Host
    • Transparent Budget
    • Expense Policy
    • Approving Expenses
    • Updates & Comms
    • Events
    • Funding Options
    • Data Export
    • Buttons & Banners
    • Integrations
    • Zero Collective Balance
    • Closing a Collective
  • Financial Contributors
    • FAQ
    • Payments
    • Website Badge
    • Organizations
      • FAQ
      • Bulk Transfers
      • Gift Cards
      • Sustainer Resources
    • Collectives
      • Collective to Collective donations
  • Expenses & Getting Paid
    • FAQ
    • Submitting Expenses
    • Expense Comments
    • Edit an Expense
    • Tax Information
  • Fiscal Hosts
    • FAQ
    • Becoming a Fiscal Host
    • Create a Fiscal Host
    • Fiscal Host Settings
    • Invoices
    • Payouts
    • Host Dashboard
    • Add Funds Manually
    • Refunds
    • Host Fees
    • Local Tax Support
    • Agreement Templates
    • Open Source Collective
  • Contributing
    • Design
      • Design Workflow
      • Design Contribution Guidelines
    • Development
      • Contribution Guide
      • Best Practice Guidelines
      • Bounties
      • API
        • Members
        • Users / Emails
        • Collectives
        • Events
      • README Integration
      • Architecture
      • Postgres Database
      • PayPal
      • Post-Donation Redirect
      • Custom Tweets
      • Manual Reporting
      • Github Permissions
      • Translations
      • Testing with Cypress
      • Collective's locations
    • Documentation
      • Style guide
      • Suggesting changes
    • Translation
  • Internal
    • Scope
    • Team Retreats
    • Brussels Summer Team Retreat
    • Host Admin Manual
    • Newsletter
    • Support
    • Issue Labels
    • DNS Troubleshooting
    • Developer Guidelines
    • Architecture
    • Testing
    • Queries
      • Gift Cards
      • Transactions
      • Ops
      • Collectives
      • Hosts
      • Analytics
      • Emails
    • Projects
      • Maintainerati Berlin 2019
      • Season of Docs 2019
    • The Open Collective Way
      • Values
      • Community Guidelines
      • Core Contributors Guidelines
      • Core Contributors: Communication
      • Core Contributors: Expenses
      • Core contributors: Leave
      • Core Contributors: Compensation
Powered by GitBook
On this page
  • Good practices
  • Use "select" when a value has a limited number of options
  • Don't assume word's order stays the same in other languages
  • Translate links inline
  • FormattedMessage
  • Add a new language
  1. Contributing
  2. Development

Translations

Documenting how we handle translations in the code

PreviousGithub PermissionsNextTesting with Cypress

Last updated 5 years ago

We use to manage our translations. They are extracted from the code to src/lang/${locale}.json files using the npm run build:langs command (CI will notify you if the translation files are outdated). Don't translate the strings directly in the files, we use to manage our translatations.

Good practices

Use "select" when a value has a limited number of options

Example

<FormattedMessage
    id="withSelect"
    defaultMessage="{action, select, delete {Delete this} archive {Archive this} other {Do something with this}}"
    values={{ action: 'delete' }}
/>

// => "Delete this"

<FormattedMessage
    id="withSelect"
    defaultMessage="{action, select, delete {Delete this} archive {Archive this} other {Do something with this}}"
    values={{ action: 'eat' }}
/>

// => "Do something with this"
  • defaultMessage string breakdown:

    • action variable name

    • select keyword

    • delete and archive possible values

    • other all other values will use this key

An exception to this rule: very common enums or the ones with many possible values should be implemented as a separate file listing all values because:

  • Re-usability

  • A map of translations is easier to read than a long select string with tons of options

Don't assume word's order stays the same in other languages

The order of the words may change from a language to another. For this reason we must always pass the values to be replaced in values so their order can later be changed.

Example

// Bad
<div>
    <FormattedMessage id="str" defaultMessage="Pending approval from " />
    <Link route={`/${host.slug}`}>{host.name} </Link>
</div>

// Good
<div>
    <FormattedMessage 
        id="str" 
        defaultMessage="Pending approval from {host}" 
        values ={{ 
          host: <Link route={`/${host.slug}`}>{host.name} </Link>
        }}
    />
</div>

Translate links inline

In some parts of the code we translate links like this:

// Please don't do that!
<FormattedMessage
  id="ReadTheDocs"
  defaultMessage="Please check our {documentationLink} to learn more!"
  values={{
    documentationLink: (
      <a href="https://docs.opencollective.com">
        <FormattedMessage id="documentation" defaultMessage="documentation" />
      </a>
    ),
  }}
/>

This is bad because we're creating two strings and translators loose the context when they translate one. You should do this instead:

<FormattedMessage
  id="ReadTheDocs"
  defaultMessage="Please check our <link>documentation</link> to learn more!"
  values={{
    // eslint-disable-next-line react/display-name
    link: msg => <a href="https://docs.opencollective.com">{msg}</a>,
  }}
/>

FormattedMessage

The FormattedMessage component is the main way to translate strings. To use it, you just need to add the following import:

import { FormattedMessage } from 'react-intl'

Then you just add the component with an unique id and a defaultMessage.

For VSCode users, you can use the following snippet to make your life easier:

{
  "Formatted Message (react-intl)": {
    "scope": "javascript",
    "prefix": "formatted-message",
    "body": "<FormattedMessage id=\"$TM_FILENAME_BASE.$0\" defaultMessage=\"$1\"/>",
    "description": "Put the given string in a FormattedMessage"
  }
}

Add a new language

Add the language on Crowdin

  • Language is ready for translation!

Activate the language in the code

To activate a language on the website, we usually wait to have a correct translated ratio (20-30%).

You will need to:

  • Copy paste the last line in frontend/scripts/translate.js (replace it with your locale code)

translate('it', defaultMessages, diff.updated);
  • Add the locale in src/components/Footer.js for the languages object

See as an example.

Go to , click on Target languages pick the language and click Update

react-intl
Crowdin
i18n-member-role
https://crowdin.com/project/opencollective/settings#translations