Skip to main content

Internationalization

Are you interested in bringing Actual to a broader audience by translating and localizing the app into new languages? Great! This guide will show you how to get started. Adding translations can be a great first step into contributing to open-source software.

Translating existing strings

Actual uses Weblate to manage all translations. In particular, we use Weblate's "suggestions" system to maintain high-quality translations.

Pull Requests

We do not accept GitHub Pull Requests modifying translation files—any PRs in the translations repository will be automatically closed.

Suggesting a new translation

To suggest a translation for a language you are familiar with, just find a string you'd like to translate, fill in a translation, and hit "Suggest." This will then give others the opportunity to vote on the most accurate translation. Check out the Weblate docs for more details.

Please only fill in translations that you are confident in, and be sure to include the same formatting markers as the English text (example: <1>English</1> -> <1>Anglais</1>).

Voting on existing suggestions

To help vote on high-quality strings, click on "Strings with suggestions" from a language page, and use the thumbs-up or thumbs-down icons to vote. If you click thumbs-down, it's always helpful to provide a reason so that the original translator can improve.

Marking new strings for translation

Developing translated software requires that every text shown to the user passes through a translation function. Actual uses i18next to achieve this goal.

Strings in Actual are translated by their natural English language version. The project does not use artificial keys to select the appropriate translated text from the message catalog.

Using natural language keys

Consider the text "Welcome to Actual Budget". When marking strings for translation using natural language, this string also becomes the key used to translate it into other languages. This is in contrast to a commonly used alternative of using artificial keys, which would look like "text_welcome". Actual Budget does not use these keys.

When preparing your translatable strings, please use complete units to translate. If there is a cohesive paragraph with multiple sentences, mark the entire paragraph as one translatable string. Do not split it into multiple texts.

The translation framework provides two primary methods for translation:

t() Function

The t() function is the simplest form of translation, it accepts the text to be translated and an optional object with interpolation variables. We're using the i18next singleton instance. You can use the hook within React components to get a reference to it.

import { useTranslation } from 'react-i18next';

function MyComponent() {
const { t } = useTranslation();

return <button aria-label={t('Hello World')} />;
}

At runtime, it will select the translation from the currently active locale. You can also add variables to interpolate:

const withPlaceholder = t('Welcome home, {{name}}!', { name: 'Anita' });

For pluralization, please use the plural form for the default text. The placeholder variable must be named count:

const manyItems = t('You selected {{count}} items', { count: 4 });

i18next will take care of proper pluralization rules in the currently active language and provide multiple different forms if required.

<Trans> Component

When translating text with simple HTML tags or other React components, we ask that you use the <Trans> component.

import { Trans } from 'react-i18next';

function MyComponent() {
return (
<Trans>
Click <Link href={url}>here</Link> to become a millionaire.
</Trans>
);
}

Interpolation with <Trans>

Handling placeholder variables and pluralization within the <Trans> React component is handled slightly differently. For more details, please refer to i18next-react's documentation.

import { Trans } from 'react-i18next';

function MyComponent() {
const person = { name: "Pedro" };
// You can use an object literal within <Trans>!
return (
<Trans>
Your name is {{ name: person.name }}.
</Trans>
);
// This will result in the translation string:
// "Your name is {{name}}."
}