Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
51 changes: 51 additions & 0 deletions .github/workflows/i18n-sync-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: i18n sync check

on:
workflow_dispatch:
pull_request:
branches:
- "preview"
types:
- "opened"
- "synchronize"
- "reopened"
- "ready_for_review"
paths:
- "packages/i18n/**"
- ".github/workflows/i18n-sync-check.yml"
push:
branches:
- "preview"
paths:
- "packages/i18n/**"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
sync-check:
name: check:sync
runs-on: ubuntu-latest
timeout-minutes: 5
if: github.event_name == 'push' || github.event.pull_request.draft == false
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 1
filter: blob:none

- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: "22.18.0"

- name: Enable Corepack and pnpm
run: corepack enable pnpm

- name: Run sync check
run: pnpm dlx tsx packages/i18n/scripts/sync-check.ts --ci
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,7 @@ build/
.react-router/
temp/
scripts/
!packages/i18n/scripts/

# i18n auto-generated types (regenerated on every build)
packages/i18n/src/types/keys.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ const CreatedVsResolved = observer(function CreatedVsResolved() {
}}
yAxis={{
key: "count",
label: t("common.no_of", { entity: isEpic ? t("epics") : t("work_items") }),
label: t("common.no_of", { entity: isEpic ? t("common.epics") : t("work_items") }),
offset: -60,
dx: -24,
}}
Expand Down
4 changes: 2 additions & 2 deletions apps/web/core/components/core/modals/change-email-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ export const ChangeEmailModal = observer(function ChangeEmailModal(props: Props)
await signOut().catch(() =>
setToast({
type: TOAST_TYPE.ERROR,
title: t("sign_out.toast.error.title"),
message: t("sign_out.toast.error.message"),
title: t("auth.sign_out.toast.error.title"),
message: t("auth.sign_out.toast.error.message"),
})
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export const CustomThemeSelector = observer(function CustomThemeSelector() {
<div className="mt-5 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
{/* Save Theme Button */}
<Button variant="primary" size="lg" type="submit" loading={isSubmitting || isLoadingPalette}>
{isSubmitting ? t("creating_theme") : isLoadingPalette ? "Generating" : t("set_theme")}
{isSubmitting ? t("common.saving") : isLoadingPalette ? "Generating" : t("set_theme")}
</Button>
{/* Import/Export Section */}
<CustomThemeDownloadConfigButton getValues={getValues} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export const IssueDetailQuickActions = observer(function IssueDetailQuickActions
router.push(redirectionPath);
} catch (_error) {
setToast({
title: t("toast.error "),
title: t("toast.error"),
type: TOAST_TYPE.ERROR,
message: t("entity.delete.failed", { entity: t("issue.label", { count: 1 }) }),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export const ModuleAnalyticsProgress = observer(function ModuleAnalyticsProgress
{isModuleDateValid ? (
<div className="relative flex w-full items-center justify-between gap-2">
<Disclosure.Button className="relative flex w-full items-center gap-2">
<div className="text-13 font-medium text-secondary">{t("progress")}</div>
<div className="text-13 font-medium text-secondary">{t("common.progress")}</div>
{progressHeaderPercentage > 0 && (
<div className="bg-amber-500/20 text-amber-500 flex h-5 w-9 items-center justify-center rounded-sm text-11 font-medium">{`${progressHeaderPercentage}%`}</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ export const CustomizeNavigationDialog = observer(function CustomizeNavigationDi

{/* Workspace Section */}
<div className="flex flex-col gap-2">
<h3 className="text-13 font-semibold text-placeholder">{t("workspace")}</h3>
<h3 className="text-13 font-semibold text-placeholder">{t("common.workspace")}</h3>
<div className="rounded-md border border-subtle bg-surface-2 py-2">
{/* Pinned Items - Draggable */}
<Sortable
Expand Down
4 changes: 2 additions & 2 deletions apps/web/core/components/power-k/config/account-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export const usePowerKAccountCommands = (): TPowerKCommandConfig[] => {
signOut().catch(() =>
setToast({
type: TOAST_TYPE.ERROR,
title: t("sign_out.toast.error.title"),
message: t("sign_out.toast.error.message"),
title: t("auth.sign_out.toast.error.title"),
message: t("auth.sign_out.toast.error.message"),
})
);
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export function ProfilePriorityDistribution({ userProfile }: Props) {
]}
xAxis={{
key: "name",
label: t("profile.stats.priority_distribution.priority"),
label: t("common.priority"),
}}
yAxis={{
key: "count",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export function ProjectAppliedFiltersList(props: Props) {
{/* Applied display filters */}
{appliedDisplayFilters.length > 0 && (
<Tag key="project_display_filters">
<span className="text-11 text-tertiary">{t("projects.label", { count: 2 })}</span>
<span className="text-11 text-tertiary">{t("common.projects")}</span>
<AppliedProjectDisplayFilters
editable={isEditingAllowed}
values={appliedDisplayFilters}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const APITokensProfileSettings = observer(function APITokensProfileSettin
<div className="size-full">
<CreateApiTokenModal isOpen={isCreateTokenModalOpen} onClose={() => setIsCreateTokenModalOpen(false)} />
<ProfileSettingsHeading
title={t("account_settings.api_tokens.heading")}
title={t("account_settings.api_tokens.title")}
description={t("account_settings.api_tokens.description")}
control={
<Button variant="primary" size="lg" onClick={() => setIsCreateTokenModalOpen(true)}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,17 @@ export const SecurityProfileSettings = observer(function SecurityProfileSettings

setToast({
type: TOAST_TYPE.ERROR,
title: errorInfo?.title ?? t("auth.common.password.toast.error.title"),
title: errorInfo?.title ?? t("auth.common.password.toast.change_password.error.title"),
message:
typeof errorInfo?.message === "string" ? errorInfo.message : t("auth.common.password.toast.error.message"),
typeof errorInfo?.message === "string"
? errorInfo.message
: t("auth.common.password.toast.change_password.error.message"),
});

if (code && passwordErrors.includes(code as EAuthenticationErrorCodes)) {
setError("new_password", {
type: "manual",
message: errorInfo?.message?.toString() || t("auth.common.password.toast.error.message"),
message: errorInfo?.message?.toString() || t("auth.common.password.toast.change_password.error.message"),
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import { Activity, Bell, CircleUser, KeyRound, LockIcon, Settings2 } from "lucid
import { observer } from "mobx-react";
import { useParams } from "react-router";
// plane imports
import { GROUPED_PROFILE_SETTINGS, PROFILE_SETTINGS_CATEGORIES } from "@plane/constants";
import {
GROUPED_PROFILE_SETTINGS,
PROFILE_SETTINGS_CATEGORIES,
PROFILE_SETTINGS_CATEGORY_LABELS,
} from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import type { ISvgIcons } from "@plane/propel/icons";
import type { TProfileSettingsTabs } from "@plane/types";
Expand Down Expand Up @@ -50,7 +54,9 @@ export const ProfileSettingsSidebarItemCategories = observer(function ProfileSet

return (
<div key={category} className="shrink-0">
<div className="p-2 text-caption-md-medium text-tertiary capitalize">{t(category)}</div>
<div className="p-2 text-caption-md-medium text-tertiary capitalize">
{t(PROFILE_SETTINGS_CATEGORY_LABELS[category])}
</div>
<div className="flex flex-col">
{categoryItems.map((item) => (
<SettingsSidebarItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const ProfileSettingsSidebarWorkspaceOptions = observer(function ProfileS

return (
<div className="shrink-0">
<div className="p-2 text-caption-md-medium text-tertiary capitalize">{t("workspace")}</div>
<div className="p-2 text-caption-md-medium text-tertiary capitalize">{t("common.workspace")}</div>
<div className="flex flex-col">
{Object.values(workspaces).map((workspace) => (
<SettingsSidebarItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ import { observer } from "mobx-react";
import { usePathname } from "next/navigation";
import { useParams } from "react-router";
// plane imports
import { EUserPermissionsLevel, GROUPED_PROJECT_SETTINGS, PROJECT_SETTINGS_CATEGORIES } from "@plane/constants";
import {
EUserPermissionsLevel,
GROUPED_PROJECT_SETTINGS,
PROJECT_SETTINGS_CATEGORIES,
PROJECT_SETTINGS_CATEGORY_LABELS,
} from "@plane/constants";
import { useTranslation } from "@plane/i18n";
// components
import { SettingsSidebarItem } from "@/components/settings/sidebar/item";
Expand Down Expand Up @@ -45,7 +50,9 @@ export const ProjectSettingsSidebarItemCategories = observer(function ProjectSet

return (
<div key={category} className="shrink-0 py-3 first:pt-0 last:pb-0">
<div className="p-2 text-caption-md-medium text-tertiary capitalize">{t(category)}</div>
<div className="p-2 text-caption-md-medium text-tertiary capitalize">
{t(PROJECT_SETTINGS_CATEGORY_LABELS[category])}
</div>
<div className="flex flex-col">
{accessibleItems.map((item) => {
const isItemActive =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ import { observer } from "mobx-react";
import { usePathname } from "next/navigation";
import { useParams } from "react-router";
// plane imports
import { EUserPermissionsLevel, GROUPED_WORKSPACE_SETTINGS, WORKSPACE_SETTINGS_CATEGORIES } from "@plane/constants";
import {
EUserPermissionsLevel,
GROUPED_WORKSPACE_SETTINGS,
WORKSPACE_SETTINGS_CATEGORIES,
WORKSPACE_SETTINGS_CATEGORY_LABELS,
} from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { joinUrlPath } from "@plane/utils";
// components
Expand Down Expand Up @@ -39,7 +44,9 @@ export const WorkspaceSettingsSidebarItemCategories = observer(function Workspac

return (
<div key={category} className="shrink-0 py-3 first:pt-0 last:pb-0">
<div className="p-2 text-caption-md-medium text-tertiary capitalize">{t(category)}</div>
<div className="p-2 text-caption-md-medium text-tertiary capitalize">
{t(WORKSPACE_SETTINGS_CATEGORY_LABELS[category])}
</div>
<div className="flex flex-col">
{accessibleItems.map((item) => {
const isItemActive =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const NotificationItemSnoozeOption = observer(function NotificationItemSn
await unSnoozeNotification(workspaceSlug);
setToast({
title: `${t("common.success")}!`,
message: t("notification.toasts.un_snoozed"),
message: t("notification.toasts.unsnoozed"),
type: TOAST_TYPE.SUCCESS,
});
} catch (e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export const SidebarMenuItems = observer(function SidebarMenuItems() {
: "aria_labels.app_sidebar.open_workspace_menu"
)}
>
<span className="text-13 font-semibold">{t("workspace")}</span>
<span className="text-13 font-semibold">{t("common.workspace")}</span>
</Disclosure.Button>
<div className="pointer-events-none flex items-center opacity-0 group-hover:pointer-events-auto group-hover:opacity-100">
<Disclosure.Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ export const UserMenuRoot = observer(function UserMenuRoot() {
signOut().catch(() =>
setToast({
type: TOAST_TYPE.ERROR,
title: t("sign_out.toast.error.title"),
message: t("sign_out.toast.error.message"),
title: t("auth.sign_out.toast.error.title"),
message: t("auth.sign_out.toast.error.message"),
})
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const SidebarWorkspaceMenuHeader = observer(function SidebarWorkspaceMenu
className="sticky top-0 z-10 flex w-full flex-1 items-center justify-between gap-1 py-1.5 text-13 font-semibold text-placeholder"
onClick={() => toggleWorkspaceMenu(!isWorkspaceMenuOpen)}
>
<span>{t("workspace")}</span>
<span>{t("common.workspace")}</span>
</Disclosure.Button>
<CustomMenu
customButton={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ export const WorkspaceMenuRoot = observer(function WorkspaceMenuRoot(props: Work
await signOut().catch(() =>
setToast({
type: TOAST_TYPE.ERROR,
title: t("sign_out.toast.error.title"),
message: t("sign_out.toast.error.message"),
title: t("auth.sign_out.toast.error.title"),
message: t("auth.sign_out.toast.error.message"),
})
);
};
Expand Down
9 changes: 0 additions & 9 deletions apps/web/core/lib/wrappers/store-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import { useEffect, useRef } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import { useTheme } from "next-themes";
import type { TLanguage } from "@plane/i18n";
import { useTranslation } from "@plane/i18n";
// helpers
import { applyCustomTheme, clearCustomTheme } from "@plane/utils";
// hooks
Expand All @@ -32,8 +30,6 @@ function StoreWrapper(props: TStoreWrapper) {
const { setQuery } = useRouterParams();
const { sidebarCollapsed, toggleSidebar } = useAppTheme();
const { data: userProfile } = useUserProfile();
const { changeLanguage } = useTranslation();

// Track if we've initialized theme from server (one-time only)
const hasInitializedThemeRef = useRef(false);
// Track current user to reset on logout/login
Expand Down Expand Up @@ -107,11 +103,6 @@ function StoreWrapper(props: TStoreWrapper) {
previousThemeRef.current = currentTheme;
}, [userProfile?.theme]);

useEffect(() => {
if (!userProfile?.language) return;
changeLanguage(userProfile?.language as TLanguage);
}, [userProfile?.language, changeLanguage]);

useEffect(() => {
if (!params) return;
setQuery(params);
Expand Down
4 changes: 2 additions & 2 deletions apps/web/core/store/root.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { enableStaticRendering } from "mobx-react";
// plane imports
import { FALLBACK_LANGUAGE, LANGUAGE_STORAGE_KEY } from "@plane/i18n";
import { FALLBACK_LANGUAGE, setLanguage } from "@plane/i18n";
import type { IWorkItemFilterStore } from "@plane/shared-state";
import { WorkItemFilterStore } from "@plane/shared-state";
// plane web store
Expand Down Expand Up @@ -137,7 +137,7 @@ export class CoreRootStore {
resetOnSignOut() {
// handling the system theme when user logged out from the app
localStorage.setItem("theme", "system");
localStorage.setItem(LANGUAGE_STORAGE_KEY, FALLBACK_LANGUAGE);
void setLanguage(FALLBACK_LANGUAGE);
this.router = new RouterStore();
this.commandPalette = new CommandPaletteStore();
this.instance = new InstanceStore();
Expand Down
9 changes: 9 additions & 0 deletions apps/web/core/store/user/profile.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

import { cloneDeep, set } from "lodash-es";
import { action, makeObservable, observable, runInAction } from "mobx";
// plane imports
import { setLanguage } from "@plane/i18n";
import type { TLanguage } from "@plane/i18n";
// types
import type { IUserTheme, TUserProfile } from "@plane/types";
import { EStartOfTheWeek } from "@plane/types";
Expand Down Expand Up @@ -108,6 +111,9 @@ export class ProfileStore implements IUserProfileStore {
this.isLoading = false;
this.data = userProfile;
});
if (userProfile.language) {
void setLanguage(userProfile.language as TLanguage);
}
return userProfile;
} catch (error) {
runInAction(() => {
Expand All @@ -132,6 +138,9 @@ export class ProfileStore implements IUserProfileStore {
if (currentUserProfileData) {
this.mutateUserProfile(data);
}
if (data.language) {
void setLanguage(data.language as TLanguage);
}
const userProfile = await this.userService.updateCurrentUserProfile(data);
return userProfile;
} catch {
Expand Down
5 changes: 5 additions & 0 deletions packages/constants/src/settings/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ export const PROFILE_SETTINGS_CATEGORIES: PROFILE_SETTINGS_CATEGORY[] = [
PROFILE_SETTINGS_CATEGORY.DEVELOPER,
];

export const PROFILE_SETTINGS_CATEGORY_LABELS: Record<PROFILE_SETTINGS_CATEGORY, string> = {
[PROFILE_SETTINGS_CATEGORY.YOUR_PROFILE]: "common.your_profile",
[PROFILE_SETTINGS_CATEGORY.DEVELOPER]: "common.developer",
};

export const PROFILE_SETTINGS: Record<
TProfileSettingsTabs,
{
Expand Down
Loading
Loading