Bluesky app fork with some witchin' additions 💫
1# Internationalization 2 3We 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](https://bluesky.crowdin.com/bluesky-social) or open an issue on the [Bluesky app repo on GitHub](https://github.com/bluesky-social/social-app). 4 5## Tools 6 7- We use Crowdin to manage translations. 8 - Bluesky Crowdin: https://bluesky.crowdin.com/bluesky-social 9 - Introduction to Crowdin: https://support.crowdin.com/for-translators/ 10- We use Lingui to implement translations. You can find the documentation [here](https://lingui.dev/). 11 12## Translators 13 14Much 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. 15 16### Using Crowdin 17 18[Crowdin](https://bluesky.crowdin.com/bluesky-social) is our primary tool for managing translations. There are two roles: 19 20- **Proof-readers**. Can create new translations and approve submitted translations. 21- **Translators**. Can create new translations. 22 23All translations must be approved by proof-readers before they are accepted into the app. 24 25### Using other platforms 26 27You may contribute PRs separately from Crowdin, however we strongly recommend using Crowdin to avoid conflicts. 28 29### Code of conduct on Crowdin 30 31Please 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. 32 33### Adding a new language 34 35You can request a new language be added to the project by clicking **Request New Language** on [Crowdin](https://bluesky.crowdin.com/bluesky-social) or you can create a [GitHub issue](https://github.com/bluesky-social/social-app/issues). 36 37Please only request a new language when you are certain you will be able to contribute a substantive portion of translations for the language. 38 39## Maintainers 40 41Install the [Crowdin CLI](https://crowdin.github.io/crowdin-cli/). You will need to [configure your API token](https://crowdin.github.io/crowdin-cli/configuration) to access the project. 42 43### English source-file sync with Crowdin 44 45Every 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. 46 47### Release process 48 491. Pull main and create a branch. 501. Run `yarn intl:pull` to fetch all translation updates from Crowdin. Commit. 511. Run `yarn intl:extract:all` to ensure all `.po` files are synced with the current state of the code. Commit. 521. Create a PR, ensure the translations all look correct, and merge. 531. If needed: 54 1. Merge all approved translation PRs (contributions from outside crowdin). 55 1. Run `yarn intl:push` to sync Crowdin with the state of the repo. 56 57### Testing the translations in Crowdin 58 59You can run `yarn intl:pull` to pull the currently-approved translations from Crowdin. 60 61## Developers 62 63### Adding new strings 64 65When adding a new string, do it as follows: 66```jsx 67// Before 68import { Text } from "react-native"; 69 70<Text>Hello World</Text> 71``` 72 73```jsx 74// After 75import { Text } from "react-native"; 76import { Trans } from "@lingui/macro"; 77 78<Text><Trans>Hello World</Trans></Text> 79``` 80 81The `<Trans>` macro will extract the string and add it to the catalog. It is not really a component, but a macro. Further reading [here](https://lingui.dev/ref/macro.html) 82 83However sometimes you will run into this case: 84```jsx 85// Before 86import { Text } from "react-native"; 87 88const text = "Hello World"; 89<Text accessibilityLabel="Label is here">{text}</Text> 90``` 91In this case, you can use the `useLingui()` hook: 92```jsx 93import { msg } from "@lingui/macro"; 94import { useLingui } from "@lingui/react"; 95 96const { _ } = useLingui(); 97return <Text accessibilityLabel={_(msg`Label is here`)}>{text}</Text> 98``` 99 100If 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): 101```jsx 102import { t } from "@lingui/macro"; 103 104const text = t`Hello World`; 105``` 106 107We 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. 108We 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. 109The configuration for translations is defined in `lingui.config.js` 110 111So the workflow is as follows: 1121. Wrap messages in Trans macro 1132. Run `yarn intl:extract` command to generate message catalogs 1143. Translate message catalogs (send them to translators usually) 1154. Run `yarn intl:compile` to create runtime catalogs 1165. Load runtime catalog 1176. Enjoy translated app! 118 119### Common pitfalls 120 121These pitfalls are memoization pitfalls that will cause the components to not re-render when the locale is changed -- causing stale translations to be shown. 122 123```jsx 124import { msg } from "@lingui/macro"; 125import { i18n } from "@lingui/core"; 126 127const welcomeMessage = msg`Welcome!`; 128 129// ❌ Bad! This code won't work 130export function Welcome() { 131 const buggyWelcome = useMemo(() => { 132 return i18n._(welcomeMessage); 133 }, []); 134 135 return <div>{buggyWelcome}</div>; 136} 137 138// ❌ Bad! This code won't work either because the reference to i18n does not change 139export function Welcome() { 140 const { i18n } = useLingui(); 141 142 const buggyWelcome = useMemo(() => { 143 return i18n._(welcomeMessage); 144 }, [i18n]); 145 146 return <div>{buggyWelcome}</div>; 147} 148 149// ✅ Good! `useMemo` has i18n context in the dependency 150export function Welcome() { 151 const linguiCtx = useLingui(); 152 153 const welcome = useMemo(() => { 154 return linguiCtx.i18n._(welcomeMessage); 155 }, [linguiCtx]); 156 157 return <div>{welcome}</div>; 158} 159 160// 🤩 Better! `useMemo` consumes the `_` function from the Lingui context 161export function Welcome() { 162 const { _ } = useLingui(); 163 164 const welcome = useMemo(() => { 165 return _(welcomeMessage); 166 }, [_]); 167 168 return <div>{welcome}</div>; 169} 170```