CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/frontend/account/i18n-selector.tsx
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
/*
7
Basically a drop-down to change the langauge (i18n localization)
8
*/
9
10
import { DownOutlined } from "@ant-design/icons";
11
import { Button, Dropdown, MenuProps, Modal, Space, Tooltip } from "antd";
12
import { SizeType } from "antd/es/config-provider/SizeContext";
13
import { useState } from "react";
14
import { defineMessage, useIntl } from "react-intl";
15
16
import { redux, useTypedRedux } from "@cocalc/frontend/app-framework";
17
import { I18N_HINT_ACCOUNT_SETTINGS } from "@cocalc/frontend/app/i18n-banner";
18
import { useLocalizationCtx } from "@cocalc/frontend/app/localize";
19
import { Icon, Paragraph } from "@cocalc/frontend/components";
20
import {
21
getLocale,
22
labels,
23
Locale,
24
LOCALIZATIONS,
25
OTHER_SETTINGS_LOCALE_KEY,
26
} from "@cocalc/frontend/i18n";
27
import { KEEP_EN_LOCALE } from "@cocalc/util/consts/locale";
28
29
interface Props {
30
isWide?: boolean;
31
size?: SizeType;
32
confirm?: boolean;
33
}
34
35
export const I18N_TITLE = defineMessage({
36
id: "account.account_page.translation.info.title",
37
defaultMessage: "Translation Information",
38
description: "Title of translation information modal",
39
});
40
41
export const I18N_MESSAGE = defineMessage({
42
id: "account.account_page.translation.info.content",
43
defaultMessage: `
44
We're excited to start offering our application in multiple languages! Here's what you need to know:
45
46
<ul>
47
<li><b>Work in Progress</b>: Our translation effort is just beginning. Many parts of the application are not yet translated.</li>
48
<li><b>Gradual Improvement</b>: We're continuously working to expand our language coverage. You'll see more content translated over time.</li>
49
<li><b>Your Help is Welcome</b>: We value our community's input. If you're fluent in multiple languages and would like to contribute to our translation efforts, we'd love to hear from you!</li>
50
<li><b>Contact Us</b>: To learn more about contributing to translations or to report any issues, please reach out to our support team.</li>
51
</ul>
52
53
Thank you for your patience and understanding as we work to make our application accessible to a global audience!`,
54
description: "Content of translation information modal",
55
});
56
57
export function I18NSelector(props: Readonly<Props>) {
58
const { isWide = true, size, confirm = false } = props;
59
60
const intl = useIntl();
61
const { setLocale, locale } = useLocalizationCtx();
62
63
const other_settings = useTypedRedux("account", "other_settings");
64
65
const i18n_enabled = useTypedRedux("customize", "i18n");
66
const [langOpen, setLangOpen] = useState<boolean>(false);
67
68
if (
69
i18n_enabled == null ||
70
i18n_enabled.isEmpty() ||
71
(i18n_enabled.size === 1 && i18n_enabled.includes("en"))
72
) {
73
return null;
74
}
75
76
const i18n: Locale = getLocale(other_settings);
77
78
const items: MenuProps["items"] =
79
Object.entries(LOCALIZATIONS)
80
.filter(([key, _]) => i18n_enabled.includes(key as any))
81
.map(([key, { name, trans, native, flag }]) => {
82
const other = key === locale ? name : intl.formatMessage(trans);
83
return { key, label: `${flag} ${native} (${other})` };
84
}) ?? [];
85
86
items.push({ type: "divider" });
87
items.push({
88
key: "help",
89
label: (
90
<Space>
91
<Icon name="translation-outlined" />
92
{intl.formatMessage({
93
id: "account.account_page.translation.info.label",
94
defaultMessage: "Translation Info...",
95
description: "Label of translation information modal in dropdown",
96
})}
97
</Space>
98
),
99
onClick: () =>
100
Modal.info({
101
width: "min(90vw, 600px)",
102
title: intl.formatMessage(I18N_TITLE),
103
content: <Paragraph>{intl.formatMessage(I18N_MESSAGE)}</Paragraph>,
104
}),
105
});
106
107
function changeLocale(key) {
108
const loc = key === "en" ? KEEP_EN_LOCALE : key;
109
redux
110
.getActions("account")
111
.set_other_settings(OTHER_SETTINGS_LOCALE_KEY, loc);
112
setLocale(loc);
113
}
114
115
const menu: MenuProps = {
116
items,
117
style: { maxHeight: "75vh", overflow: "auto" },
118
onClick: ({ key }) => {
119
if (key in LOCALIZATIONS) {
120
if (confirm) {
121
Modal.confirm({
122
onOk: () => changeLocale(key),
123
title: intl.formatMessage(
124
{
125
id: "account.account_page.translation.change.title",
126
defaultMessage: "Change language to {lang}?",
127
},
128
{
129
lang: `${LOCALIZATIONS[key].native} (${LOCALIZATIONS[key].name})`,
130
},
131
),
132
content: I18N_HINT_ACCOUNT_SETTINGS,
133
});
134
} else {
135
changeLocale(key);
136
}
137
}
138
},
139
};
140
141
const lang_icon = LOCALIZATIONS[i18n]?.flag;
142
143
const title =
144
i18n in LOCALIZATIONS
145
? intl.formatMessage(LOCALIZATIONS[i18n].trans)
146
: i18n;
147
148
const cur = `${title} (${LOCALIZATIONS[i18n]?.name ?? i18n})`;
149
const msg = intl.formatMessage(labels.account_language_tooltip);
150
const tooltip = (
151
<>
152
{cur}
153
<br />
154
{msg}
155
{labels.account_language_tooltip.defaultMessage != msg ? (
156
<>
157
<br />({labels.account_language_tooltip.defaultMessage})
158
</>
159
) : undefined}
160
</>
161
);
162
163
return (
164
<Tooltip title={langOpen ? undefined : tooltip} trigger={["hover"]}>
165
<Dropdown
166
menu={menu}
167
trigger={["click"]}
168
onOpenChange={(open) => setLangOpen(open)}
169
>
170
<Button size={size}>
171
<Space>
172
{lang_icon}
173
{isWide ? title : undefined}
174
<DownOutlined />
175
</Space>
176
</Button>
177
</Dropdown>
178
</Tooltip>
179
);
180
}
181
182