feat: language support (#6472)
* chore: ln support modules constants * fix: translation key * chore: empty state refactor (#6404) * chore: asset path helper hook added * chore: detailed and simple empty state component added * chore: section empty state component added * chore: language translation for all empty states * chore: new empty state implementation * improvement: add more translations * improvement: user permissions and workspace draft empty state * chore: update translation structure * chore: inbox empty states * chore: disabled project features empty state * chore: active cycle progress empty state * chore: notification empty state * chore: connections translation * chore: issue comment, relation, bulk delete, and command k empty state translation * chore: project pages empty state and translations * chore: project module and view related empty state * chore: remove project draft related empty state * chore: project cycle, views and archived issues empty state * chore: project cycles related empty state * chore: project settings empty state * chore: profile issue and acitivity empty state * chore: workspace settings realted constants * chore: stickies and home widgets empty state * chore: remove all reference to deprecated empty state component and constnats * chore: add support to ignore theme in resolved asset path hook * chore: minor updates * fix: build errors --------- Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com> * fix: language support fo profile (#6461) * fix: ln support fo profile * fix: merge changes * fix: merge changes * [WEB-3165]feat: language support for issues (#6452) * * chore: moved issue constants to packages * chore: restructured issue constants * improvement: added translations to issue constants * chore: updated translation structure * * chore: updated chinese, spanish and french translation * chore: updated translation for issues mobile header * chore: updated spanish translation * chore: removed translation for issue priorities * fix: build errors * chore: minor updates --------- Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> * chore: migrated filters.ts to packages (#6459) Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> * chore: workspace drafts constant moved to plane constant package * feat: home language support without stickies (#6443) * feat: home language support without stickies * fix: home sidebar * fix: added missing keys * fix: show all btn * fix: recents empty state * chore: translation update * feat: workspace constant language support and refactor (#6462) * chore: workspace constant language support and refactor * chore: workspace constant language support and refactor * chore: code refactor * chore: code refactor * merge conflict * chore: code refactor --------- Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> * chore: tab indices constant moved to plane package (#6464) * chore: notification language support and refactor * chore: ln support for inbox constants (#6432) * chore: ln support for inbox constants * fix: snooze duration * fix: enum * fix: translation keys * fix: inbox status icon * fix: status icon * fix: naming --------- Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> * fix: ln support for views constants (#6431) * fix: ln support for views constants * fix: added translation * fix: translation keys * fix: access * chore: code refactor * chore: ln support workspace projects constants (#6429) * chore: ln support workspace projects constants * fix: translation key * fix: removed state translation * fix: removed state translation * fi: added translations * Chore: theme language support and refactor (#6465) * chore: themes language support and refactor * chore: theme language support and refactor * fix * [WEB-3173] chore: language support for cycles constant file (#6415) * chore: ln support for cycles constant file * fix: added chinese * fix: lint * fix: translation key * fix: build errors * minor updates * chore: minor translation update * chore: minor translation update * refactor: move labels contants to packages * refactor: move swr, file and error related constants to packages * chore: timezones constant moved to plane package * chore: metadata constant code refactor * chore: code refactor * fix: dashboard constants moved * chore: code refactor (#6478) * refactor: spreadsheet constants * chore: drafts language support (#6485) * chore: workspace drafts language support * chore: code refactor * feat: ln support for notifications (#6486) * feat: ln support for notifications * fix: translations * * refactor: moved page constants to packages (#6480) * fix: removed use-client * chore: removed unnecessary commnets * chore: workspace draft language support (#6490) * chore: workspace drafts language support * chore: code refactor * chore: draft language support * Feat constant event tracker (#6479) * fix: event tracjer constants * fix: constants event tracker * feat: language translation - projects list (#6493) * feat: added translation to projects list page * chore: restructured translation file * chore: module language support (#6499) * chore: module language support added * chore: code refactor * chore: workspace views language support (#6492) * chore: workspace views language support * chore: code refactor * feat: custom analytics language support (#6494) * feat: custom analytics language support * fix: key * fix: refactoring --------- Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> * chore: minor improvements * feat: language support for intake (#6498) * feat: language support for intake * fix: key name * refactor: authentications related translations * feat: language support issues (#6501) * enhancement: added translations for issue list view * chore: added translations for issue detail widgets * chore: added missing translations * chore: modified issue to work items * chore: updated translations * Feat: workspace settings language support (#6508) * feat: language support for workspace settings * fix: lint * fix: export title * chore project settings language support (#6502) * chore: project settings language support * chore: code refactor * refactor: workspace creation related translations * chore: renamed issues to work items * fix: build errors * fix: lint * chore: modified translations * chore: remove duplicate * improvement: french translation * chore: chinese translation improvement * fix: japanese translations * chore: added spanish translation * minor improvements * fix: miscelleous language translations * fix: clear_all key * fix: moved user permission constants (#6516) * feat: language support for issues (#6513) * chore: added language support to issue detail widgets * improvement: added translation for issue detail * enhancement: added language trasnlation to issue layouts * chore: translation improvement (#6518) * feat: language support description (#6519) * enhancement: added language support for description * fix: updated keys * chore: renamed issue to work item (#6522) * chore: replace missing issue occurances to work items * fix: build errors * minor improvements * fix: profile links * Feat ln cycles (#6528) * feat: added language support for cycles * feat: added language support for cycles * chore: added core.json * fix: translation keys * fix: translation keys (#6530) * fix: changed sidebar keys * fix: removed extras * fix: updated keys * chore: optimize translation imports * fix: updated keys (#6534) * fix: updated keys * fix-sub work items toasts * chore: add missing translation and minor fixes * chore: code refactor * fix: language support keys (#6553) * minor improvements * minor fixes * fix: remove lucide import from constants package * chore: regenerate all translations * chore: addded chinese and japanese translation files * chore: remove all from translations * fix: added member * fix: language support keys (#6558) * fix: renamed keys * fix: space app * chore: renamed issues to work items * chore: update site manifest * chore: updated translations * fix: lang keys * chore: update translations --------- Co-authored-by: gakshita <akshitagoyal1516@gmail.com> Co-authored-by: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com> Co-authored-by: Akshita Goyal <36129505+gakshita@users.noreply.github.com> Co-authored-by: Vamsi Krishna <46787868+mathalav55@users.noreply.github.com> Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so> Co-authored-by: Vamsi krishna <matalav55@gmail.com> Co-authored-by: Vamsi Krishna <46787868+vamsikrishnamathala@users.noreply.github.com>
This commit is contained in:
parent
e244f48776
commit
d36c3acbf7
693 changed files with 18182 additions and 10485 deletions
171
packages/i18n/src/locales/en/core.json
Normal file
171
packages/i18n/src/locales/en/core.json
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
{
|
||||
"sidebar": {
|
||||
"projects": "Projects",
|
||||
"pages": "Pages",
|
||||
"new_work_item": "New work item",
|
||||
"home": "Home",
|
||||
"your_work": "Your work",
|
||||
"inbox": "Inbox",
|
||||
"workspace": "Workspace",
|
||||
"views": "Views",
|
||||
"analytics": "Analytics",
|
||||
"work_items": "Work items",
|
||||
"cycles": "Cycles",
|
||||
"modules": "Modules",
|
||||
"intake": "Intake",
|
||||
"drafts": "Drafts",
|
||||
"favorites": "Favorites",
|
||||
"pro": "Pro",
|
||||
"upgrade": "Upgrade"
|
||||
},
|
||||
|
||||
"auth": {
|
||||
"common": {
|
||||
"email": {
|
||||
"label": "Email",
|
||||
"placeholder": "name@company.com",
|
||||
"errors": {
|
||||
"required": "Email is required",
|
||||
"invalid": "Email is invalid"
|
||||
}
|
||||
},
|
||||
"password": {
|
||||
"label": "Password",
|
||||
"set_password": "Set a password",
|
||||
"placeholder": "Enter password",
|
||||
"confirm_password": {
|
||||
"label": "Confirm password",
|
||||
"placeholder": "Confirm password"
|
||||
},
|
||||
"current_password": {
|
||||
"label": "Current password"
|
||||
},
|
||||
"new_password": {
|
||||
"label": "New password",
|
||||
"placeholder": "Enter new password"
|
||||
},
|
||||
"change_password": {
|
||||
"label": {
|
||||
"default": "Change password",
|
||||
"submitting": "Changing password"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"match": "Passwords don't match",
|
||||
"empty": "Please enter your password",
|
||||
"length": "Password length should me more than 8 characters",
|
||||
"strength": {
|
||||
"weak": "Password is weak",
|
||||
"strong": "Password is strong"
|
||||
}
|
||||
},
|
||||
"submit": "Set password",
|
||||
"toast": {
|
||||
"change_password": {
|
||||
"success": {
|
||||
"title": "Success!",
|
||||
"message": "Password changed successfully."
|
||||
},
|
||||
"error": {
|
||||
"title": "Error!",
|
||||
"message": "Something went wrong. Please try again."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"unique_code": {
|
||||
"label": "Unique code",
|
||||
"placeholder": "gets-sets-flys",
|
||||
"paste_code": "Paste the code sent to your email",
|
||||
"requesting_new_code": "Requesting new code",
|
||||
"sending_code": "Sending code"
|
||||
},
|
||||
"already_have_an_account": "Already have an account?",
|
||||
"login": "Log in",
|
||||
"create_account": "Create an account",
|
||||
"new_to_plane": "New to Plane?",
|
||||
"back_to_sign_in": "Back to sign in",
|
||||
"resend_in": "Resend in {seconds} seconds",
|
||||
"sign_in_with_unique_code": "Sign in with unique code",
|
||||
"forgot_password": "Forgot your password?"
|
||||
},
|
||||
"sign_up": {
|
||||
"header": {
|
||||
"label": "Create an account to start managing work with your team.",
|
||||
"step": {
|
||||
"email": {
|
||||
"header": "Sign up",
|
||||
"sub_header": ""
|
||||
},
|
||||
"password": {
|
||||
"header": "Sign up",
|
||||
"sub_header": "Sign up using an email-password combination."
|
||||
},
|
||||
"unique_code": {
|
||||
"header": "Sign up",
|
||||
"sub_header": "Sign up using a unique code sent to the email address above."
|
||||
}
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"password": {
|
||||
"strength": "Try setting-up a strong password to proceed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sign_in": {
|
||||
"header": {
|
||||
"label": "Log in to start managing work with your team.",
|
||||
"step": {
|
||||
"email": {
|
||||
"header": "Log in or sign up",
|
||||
"sub_header": ""
|
||||
},
|
||||
"password": {
|
||||
"header": "Log in or sign up",
|
||||
"sub_header": "Use your email-password combination to log in."
|
||||
},
|
||||
"unique_code": {
|
||||
"header": "Log in or sign up",
|
||||
"sub_header": "Log in using a unique code sent to the email address above."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"forgot_password": {
|
||||
"title": "Reset your password",
|
||||
"description": "Enter your user account's verified email address and we will send you a password reset link.",
|
||||
"email_sent": "We sent the reset link to your email address",
|
||||
"send_reset_link": "Send reset link",
|
||||
"errors": {
|
||||
"smtp_not_enabled": "We see that your god hasn't enabled SMTP, we will not be able to send a password reset link"
|
||||
},
|
||||
"toast": {
|
||||
"success": {
|
||||
"title": "Email sent",
|
||||
"message": "Check your inbox for a link to reset your password. If it doesn't appear within a few minutes, check your spam folder."
|
||||
},
|
||||
"error": {
|
||||
"title": "Error!",
|
||||
"message": "Something went wrong. Please try again."
|
||||
}
|
||||
}
|
||||
},
|
||||
"reset_password": {
|
||||
"title": "Set new password",
|
||||
"description": "Secure your account with a strong password"
|
||||
},
|
||||
"set_password": {
|
||||
"title": "Secure your account",
|
||||
"description": "Setting password helps you login securely"
|
||||
},
|
||||
"sign_out": {
|
||||
"toast": {
|
||||
"error": {
|
||||
"title": "Error!",
|
||||
"message": "Failed to sign out. Please try again."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,8 +1,11 @@
|
|||
import IntlMessageFormat from "intl-messageformat";
|
||||
import get from "lodash/get";
|
||||
import { makeAutoObservable } from "mobx";
|
||||
import merge from "lodash/merge";
|
||||
import { makeAutoObservable, runInAction } from "mobx";
|
||||
// constants
|
||||
import { FALLBACK_LANGUAGE, SUPPORTED_LANGUAGES, STORAGE_KEY } from "../constants";
|
||||
// core translations imports
|
||||
import coreEn from "../locales/en/core.json";
|
||||
// types
|
||||
import { TLanguage, ILanguageOption, ITranslations } from "../types";
|
||||
|
||||
|
|
@ -12,42 +15,35 @@ import { TLanguage, ILanguageOption, ITranslations } from "../types";
|
|||
* Uses IntlMessageFormat to format the translations
|
||||
*/
|
||||
export class TranslationStore {
|
||||
// Core translations that are always loaded
|
||||
private coreTranslations: ITranslations = {
|
||||
en: coreEn,
|
||||
};
|
||||
// List of translations for each language
|
||||
private translations: ITranslations = {};
|
||||
// Cache for IntlMessageFormat instances
|
||||
private messageCache: Map<string, IntlMessageFormat> = new Map();
|
||||
// Current language
|
||||
currentLocale: TLanguage = FALLBACK_LANGUAGE;
|
||||
// Loading state
|
||||
isLoading: boolean = true;
|
||||
isInitialized: boolean = false;
|
||||
// Set of loaded languages
|
||||
private loadedLanguages: Set<TLanguage> = new Set();
|
||||
|
||||
/**
|
||||
* Constructor for the TranslationStore class
|
||||
*/
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
// Initialize with core translations immediately
|
||||
this.translations = this.coreTranslations;
|
||||
// Initialize language
|
||||
this.initializeLanguage();
|
||||
// Load all the translations
|
||||
this.loadTranslations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads translations from JSON files and initializes the message cache
|
||||
*/
|
||||
private async loadTranslations() {
|
||||
try {
|
||||
// dynamic import of translations
|
||||
const translations = {
|
||||
en: (await import("../locales/en/translations.json")).default,
|
||||
fr: (await import("../locales/fr/translations.json")).default,
|
||||
es: (await import("../locales/es/translations.json")).default,
|
||||
ja: (await import("../locales/ja/translations.json")).default,
|
||||
"zh-CN": (await import("../locales/zh-CN/translations.json")).default,
|
||||
};
|
||||
this.translations = translations;
|
||||
this.messageCache.clear(); // Clear cache when translations change
|
||||
} catch (error) {
|
||||
console.error("Failed to load translations:", error);
|
||||
}
|
||||
}
|
||||
|
||||
/** Initializes the language based on the local storage or browser language */
|
||||
private initializeLanguage() {
|
||||
if (typeof window === "undefined") return;
|
||||
|
|
@ -62,6 +58,100 @@ export class TranslationStore {
|
|||
this.setLanguage(browserLang);
|
||||
}
|
||||
|
||||
/** Loads the translations for the current language */
|
||||
private async loadTranslations(): Promise<void> {
|
||||
try {
|
||||
// Set initialized to true (Core translations are already loaded)
|
||||
runInAction(() => {
|
||||
this.isInitialized = true;
|
||||
});
|
||||
// Load current and fallback languages in parallel
|
||||
await this.loadPrimaryLanguages();
|
||||
// Load all remaining languages in parallel
|
||||
this.loadRemainingLanguages();
|
||||
} catch (error) {
|
||||
console.error("Failed in translation initialization:", error);
|
||||
runInAction(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async loadPrimaryLanguages(): Promise<void> {
|
||||
try {
|
||||
// Load current and fallback languages in parallel
|
||||
const languagesToLoad = new Set<TLanguage>([this.currentLocale]);
|
||||
// Add fallback language only if different from current
|
||||
if (this.currentLocale !== FALLBACK_LANGUAGE) {
|
||||
languagesToLoad.add(FALLBACK_LANGUAGE);
|
||||
}
|
||||
// Load all primary languages in parallel
|
||||
const loadPromises = Array.from(languagesToLoad).map((lang) => this.loadLanguageTranslations(lang));
|
||||
await Promise.all(loadPromises);
|
||||
// Update loading state
|
||||
runInAction(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to load primary languages:", error);
|
||||
runInAction(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private loadRemainingLanguages(): void {
|
||||
const remainingLanguages = SUPPORTED_LANGUAGES.map((lang) => lang.value).filter(
|
||||
(lang) =>
|
||||
!this.loadedLanguages.has(lang as TLanguage) && lang !== this.currentLocale && lang !== FALLBACK_LANGUAGE
|
||||
);
|
||||
// Load all remaining languages in parallel
|
||||
Promise.all(remainingLanguages.map((lang) => this.loadLanguageTranslations(lang as TLanguage))).catch((error) => {
|
||||
console.error("Failed to load some remaining languages:", error);
|
||||
});
|
||||
}
|
||||
|
||||
private async loadLanguageTranslations(language: TLanguage): Promise<void> {
|
||||
// Skip if already loaded
|
||||
if (this.loadedLanguages.has(language)) return;
|
||||
|
||||
try {
|
||||
const translations = await this.importLanguageFile(language);
|
||||
runInAction(() => {
|
||||
// Use lodash merge for deep merging
|
||||
this.translations[language] = merge({}, this.coreTranslations[language] || {}, translations.default);
|
||||
// Add to loaded languages
|
||||
this.loadedLanguages.add(language);
|
||||
// Clear cache
|
||||
this.messageCache.clear();
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Failed to load translations for ${language}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the translations for the given language
|
||||
* @param language - The language to import the translations for
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
private importLanguageFile(language: TLanguage): Promise<any> {
|
||||
switch (language) {
|
||||
case "en":
|
||||
return import("../locales/en/translations.json");
|
||||
case "fr":
|
||||
return import("../locales/fr/translations.json");
|
||||
case "es":
|
||||
return import("../locales/es/translations.json");
|
||||
case "ja":
|
||||
return import("../locales/ja/translations.json");
|
||||
case "zh-CN":
|
||||
return import("../locales/zh-CN/translations.json");
|
||||
default:
|
||||
throw new Error(`Unsupported language: ${language}`);
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks if the language is valid based on the supported languages */
|
||||
private isValidLanguage(lang: string | null): lang is TLanguage {
|
||||
return lang !== null && this.availableLanguages.some((l) => l.value === lang);
|
||||
|
|
@ -173,20 +263,26 @@ export class TranslationStore {
|
|||
* Sets the current language and updates the translations
|
||||
* @param lng - The new language
|
||||
*/
|
||||
setLanguage(lng: TLanguage): void {
|
||||
async setLanguage(lng: TLanguage): Promise<void> {
|
||||
try {
|
||||
if (!this.isValidLanguage(lng)) {
|
||||
throw new Error(`Invalid language: ${lng}`);
|
||||
}
|
||||
|
||||
// Safeguard in case background loading failed
|
||||
if (!this.loadedLanguages.has(lng)) {
|
||||
await this.loadLanguageTranslations(lng);
|
||||
}
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
localStorage.setItem(STORAGE_KEY, lng);
|
||||
}
|
||||
this.currentLocale = lng;
|
||||
this.messageCache.clear(); // Clear cache when language changes
|
||||
if (typeof window !== "undefined") {
|
||||
document.documentElement.lang = lng;
|
||||
}
|
||||
|
||||
runInAction(() => {
|
||||
this.currentLocale = lng;
|
||||
this.messageCache.clear(); // Clear cache when language changes
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to set language:", error);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue