Thank you for showing an interest in contributing to Plane! All kinds of contributions are valuable to us. In this guide, we will cover how you can quickly onboard and make your first contribution.
Before submitting a new issue, please search the issues tab. Maybe an issue or discussion already exists and might inform you of workarounds. Otherwise, you can give new information.
While we want to fix all the issues, before fixing a bug we need to be able to reproduce and confirm it. Please provide us with a minimal reproduction scenario using a repository or Gist. Having a live, reproducible scenario gives us the information without asking questions back & forth with additional questions like:
- 3rd-party libraries being used and their versions
- a use-case that fails
Without said minimal reproduction, we won't be able to investigate all issues, and the issue might not be resolved.
You can open a new issue with this issue form.
- Node.js version v16.18.0
- Python version 3.8+
- Postgres version v14
- Redis version v6.2.7
The project is a monorepo, with backend api and frontend in a single repo.
The backend is a django project which is kept inside apiserver
- Clone the repo
git clone https://github.com/makeplane/plane.git [folder-name]
cd [folder-name]
chmod +x setup.sh
- Run setup.sh
./setup.sh
- Start the containers
docker compose -f docker-compose-local.yml up
If a feature is missing, you can directly request a new one here. You also can do the same by choosing "🚀 Feature" when raising a New Issue on our GitHub Repository. If you would like to implement it, an issue with your proposal must be submitted first, to be sure that we can use it. Please consider the guidelines given below.
To ensure consistency throughout the source code, please keep these rules in mind as you are working:
- All features or bug fixes must be tested by one or more specs (unit-tests).
- We use Eslint default rule guide, with minor changes. An automated formatter is available using prettier.
- Try Plane Cloud and the self hosting platform and give feedback
- Add new integrations
- Add or update translations
- Help with open issues or create your own
- Share your thoughts and suggestions with us
- Help create tutorials and blog posts
- Request a feature by submitting a proposal
- Report a bug
- Improve documentation - fix incomplete or missing docs, bad wording, examples or explanations.
This guide is designed to help contributors understand how to add or update translations in the application.
Translations are organized by language in the locales directory. Each language has its own folder containing JSON files for translations. Here's how it looks:
packages/i18n/src/locales/
├── en/
│ ├── core.json # Critical translations
│ └── translations.json
├── fr/
│ └── translations.json
└── [language]/
└── translations.json
To keep translations organized, we use a nested structure for keys. This makes it easier to manage and locate specific translations. For example:
{
"issue": {
"label": "Work item",
"title": {
"label": "Work item title"
}
}
}
We use IntlMessageFormat to handle dynamic content, such as variables and pluralization. Here's how to format your translations:
-
Simple variables
{ "greeting": "Hello, {name}!" }
-
Pluralization
{ "items": "{count, plural, one {Work item} other {Work items}}" }
-
Locate the key in
locales/<language>/translations.json
. -
Update the value while ensuring the key structure remains intact.
-
Preserve any existing ICU formats (e.g., variables, pluralization).
-
When introducing a new key, ensure it is added to all language files, even if translations are not immediately available. Use English as a placeholder if needed.
-
Keep the nesting structure consistent across all languages.
-
If the new key requires dynamic content (e.g., variables or pluralization), ensure the ICU format is applied uniformly across all languages.
Adding a new language involves several steps to ensure it integrates seamlessly with the project. Follow these instructions carefully:
-
Update type definitions
Add the new language to the TLanguage type in the language definitions file:// types/language.ts export type TLanguage = "en" | "fr" | "your-lang";
-
Add language configuration
Include the new language in the list of supported languages:// constants/language.ts export const SUPPORTED_LANGUAGES: ILanguageOption[] = [ { label: "English", value: "en" }, { label: "Your Language", value: "your-lang" } ];
-
Create translation files
-
Create a new folder for your language under locales (e.g.,
locales/your-lang/
). -
Add a
translations.json
file inside the folder. -
Copy the structure from an existing translation file and translate all keys.
-
-
Update import logic
Modify the language import logic to include your new language:private importLanguageFile(language: TLanguage): Promise<any> { switch (language) { case "your-lang": return import("../locales/your-lang/translations.json"); // ... } }
Before submitting your contribution, please ensure the following:
- All translation keys exist in every language file.
- Nested structures match across all language files.
- ICU message formats are correctly implemented.
- All languages load without errors in the application.
- Dynamic values and pluralization work as expected.
- There are no missing or untranslated keys.
- When in doubt, refer to the English translations for context.
- Verify pluralization works with different numbers.
- Ensure dynamic values (e.g.,
{name}
) are correctly interpolated. - Double-check that nested key access paths are accurate.
Happy translating! 🌍✨
Questions, suggestions, and thoughts are most welcome. We can also be reached in our Discord Server.