Bluesky app fork with some witchin' additions 💫

Internationalization#

We want the official Bluesky app to be supported in as many languages as possible. If you want to help us translate the app, please get involved on Crowdin or open an issue on the Bluesky app repo on GitHub.

Tools#

Translators#

Much of the app is translated by community contributions. (We <3 our translators!) If you want to participate in the translation of the app, read this section.

Using Crowdin#

Crowdin is our primary tool for managing translations. There are two roles:

  • Proof-readers. Can create new translations and approve submitted translations.
  • Translators. Can create new translations.

All translations must be approved by proof-readers before they are accepted into the app.

Using other platforms#

You may contribute PRs separately from Crowdin, however we strongly recommend using Crowdin to avoid conflicts.

Code of conduct on Crowdin#

Please treat everyone with respect. Proof-readers are given final say on translations. Translators who frequently come into conflict with other translators, or who contribute noticably incorrect translations, will have their membership to the Crowdin project revoked.

Adding a new language#

You can request a new language be added to the project by clicking Request New Language on Crowdin or you can create a GitHub issue.

Please only request a new language when you are certain you will be able to contribute a substantive portion of translations for the language.

Maintainers#

Install the Crowdin CLI. You will need to configure your API token to access the project.

English source-file sync with Crowdin#

Every night, a GitHub action will run yarn intl:extract to update the english messages.po file. This will be automatically synced with Crowdin. Crowdin should notify all subscribed users of new translations.

Release process#

  1. Pull main and create a branch.
  2. Run yarn intl:pull to fetch all translation updates from Crowdin. Commit.
  3. Run yarn intl:extract:all to ensure all .po files are synced with the current state of the code. Commit.
  4. Create a PR, ensure the translations all look correct, and merge.
  5. If needed:
  6. Merge all approved translation PRs (contributions from outside crowdin).
  7. Run yarn intl:push to sync Crowdin with the state of the repo.

Testing the translations in Crowdin#

You can run yarn intl:pull to pull the currently-approved translations from Crowdin.

Developers#

Adding new strings#

When adding a new string, do it as follows:

// Before
import { Text } from "react-native";

<Text>Hello World</Text>
// After
import { Text } from "react-native";
import { Trans } from "@lingui/macro";

<Text><Trans>Hello World</Trans></Text>

The <Trans> macro will extract the string and add it to the catalog. It is not really a component, but a macro. Further reading here

However sometimes you will run into this case:

// Before
import { Text } from "react-native";

const text = "Hello World";
<Text accessibilityLabel="Label is here">{text}</Text>

In this case, you can use the useLingui() hook:

import { msg } from "@lingui/macro";
import { useLingui } from "@lingui/react";

const { _ } = useLingui();
return <Text accessibilityLabel={_(msg`Label is here`)}>{text}</Text>

If you want to do this outside of a React component, you can use the t macro instead (note: this won't react to changes if the locale is switched dynamically within the app):

import { t } from "@lingui/macro";

const text = t`Hello World`;

We can then run yarn intl:extract to update the catalog in src/locale/locales/{locale}/messages.po. This will add the new string to the catalog. We can then run yarn intl:compile to update the translation files in src/locale/locales/{locale}/messages.js. This will add the new string to the translation files. The configuration for translations is defined in lingui.config.js

So the workflow is as follows:

  1. Wrap messages in Trans macro
  2. Run yarn intl:extract command to generate message catalogs
  3. Translate message catalogs (send them to translators usually)
  4. Run yarn intl:compile to create runtime catalogs
  5. Load runtime catalog
  6. Enjoy translated app!

Common pitfalls#

These pitfalls are memoization pitfalls that will cause the components to not re-render when the locale is changed -- causing stale translations to be shown.

import { msg } from "@lingui/macro";
import { i18n } from "@lingui/core";

const welcomeMessage = msg`Welcome!`;

// ❌ Bad! This code won't work
export function Welcome() {
  const buggyWelcome = useMemo(() => {
    return i18n._(welcomeMessage);
  }, []);

  return <div>{buggyWelcome}</div>;
}

// ❌ Bad! This code won't work either because the reference to i18n does not change
export function Welcome() {
  const { i18n } = useLingui();

  const buggyWelcome = useMemo(() => {
    return i18n._(welcomeMessage);
  }, [i18n]);

  return <div>{buggyWelcome}</div>;
}

// ✅ Good! `useMemo` has i18n context in the dependency
export function Welcome() {
  const linguiCtx = useLingui();

  const welcome = useMemo(() => {
    return linguiCtx.i18n._(welcomeMessage);
  }, [linguiCtx]);

  return <div>{welcome}</div>;
}

// 🤩 Better! `useMemo` consumes the `_` function from the Lingui context
export function Welcome() {
  const { _ } = useLingui();

  const welcome = useMemo(() => {
    return _(welcomeMessage);
  }, [_]);

  return <div>{welcome}</div>;
}