Skip to content

Commit c5a3bf1

Browse files
committed
i18n: prepare "util" for message strings, project state information, fix some dropdown labels, vbar, ...
1 parent a30b032 commit c5a3bf1

37 files changed

+860
-338
lines changed

src/packages/frontend/account/other-settings.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -381,13 +381,21 @@ export function OtherSettings(props: Readonly<Props>): JSX.Element {
381381

382382
function render_vertical_fixed_bar_options(): Rendered {
383383
const selected = getValidVBAROption(props.other_settings.get(VBAR_KEY));
384+
const options = Object.fromEntries(
385+
Object.entries(VBAR_OPTIONS).map(([k, v]) => [k, intl.formatMessage(v)]),
386+
);
384387
return (
385-
<LabeledRow label="Vertical Project Bar">
388+
<LabeledRow
389+
label={intl.formatMessage({
390+
id: "account.other-settings.vbar.title",
391+
defaultMessage: "Vertical Project Bar",
392+
})}
393+
>
386394
<div>
387395
<SelectorInput
388396
style={{ marginBottom: "10px" }}
389397
selected={selected}
390-
options={VBAR_OPTIONS}
398+
options={options}
391399
on_change={(value) => {
392400
on_change(VBAR_KEY, value);
393401
track("flyout", { aspect: "layout", how: "account", value });
@@ -397,7 +405,7 @@ export function OtherSettings(props: Readonly<Props>): JSX.Element {
397405
type="secondary"
398406
ellipsis={{ expandable: true, symbol: "more" }}
399407
>
400-
{VBAR_EXPLANATION}
408+
{intl.formatMessage(VBAR_EXPLANATION)}
401409
</Paragraph>
402410
</div>
403411
</LabeledRow>

src/packages/frontend/components/project-state.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@
33
* License: MS-RSL – see LICENSE.md for details
44
*/
55

6-
import { React, useTypedRedux } from "../app-framework";
6+
import { useIntl } from "react-intl";
7+
8+
import { React, useTypedRedux } from "@cocalc/frontend/app-framework";
9+
import { IntlMessage, isIntlMessage } from "@cocalc/frontend/i18n";
10+
import { ProjectStatus } from "@cocalc/frontend/todo-types";
11+
import { ComputeState } from "@cocalc/util/compute-states";
12+
import { KUCALC_COCALC_COM } from "@cocalc/util/db-schema/site-defaults";
713
import { COMPUTE_STATES } from "@cocalc/util/schema";
8-
import { ProjectStatus } from "../todo-types";
914
import { Gap } from "./gap";
1015
import { Icon } from "./icon";
11-
import { KUCALC_COCALC_COM } from "@cocalc/util/db-schema/site-defaults";
1216

1317
interface Props {
1418
state?: ProjectStatus;
@@ -18,6 +22,7 @@ interface Props {
1822
export const ProjectState: React.FC<Props> = (props: Props) => {
1923
const { state, show_desc } = props;
2024

25+
const intl = useIntl();
2126
const kucalc = useTypedRedux("customize", "kucalc");
2227
const showCoCalcCom = kucalc === KUCALC_COCALC_COM;
2328

@@ -29,27 +34,37 @@ export const ProjectState: React.FC<Props> = (props: Props) => {
2934
);
3035
}
3136

32-
function renderDescription({ desc, desc_cocalccom }) {
37+
function renderI18N(msg: string | IntlMessage): string {
38+
if (isIntlMessage(msg)) {
39+
return intl.formatMessage(msg);
40+
} else {
41+
return msg;
42+
}
43+
}
44+
45+
function renderDescription({ desc_cocalccom, desc }: ComputeState) {
3346
if (!show_desc) {
3447
return;
3548
}
3649
const text =
3750
showCoCalcCom && desc_cocalccom != null ? desc_cocalccom : desc;
51+
3852
return (
3953
<span>
40-
<span style={{ fontSize: "11pt" }}>{text}</span>
54+
<span style={{ fontSize: "11pt" }}>{renderI18N(text)}</span>
4155
</span>
4256
);
4357
}
4458

45-
const s = COMPUTE_STATES[state?.get("state") ?? ""];
59+
const current_state = state?.get("state") ?? "";
60+
const s: ComputeState = COMPUTE_STATES[current_state];
4661
if (s == null) {
4762
return <></>;
4863
}
4964
const { display, icon, stable } = s;
5065
return (
5166
<span>
52-
<Icon name={icon} /> {display}
67+
<Icon name={icon} /> {renderI18N(display)}
5368
<Gap />
5469
{!stable && renderSpinner()}
5570
{renderDescription(s)}

src/packages/frontend/frame-editors/frame-tree/title-bar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -885,7 +885,7 @@ export function FrameTitleBar(props: FrameTitleBarProps) {
885885
) {
886886
return;
887887
}
888-
if (props.connection_status == "connected") {
888+
if (props.connection_status === "connected") {
889889
// To reduce clutter show nothing when connected.
890890
// NOTE: Keep this consistent with
891891
// cocalc/src/@cocalc/frontend/project/websocket/websocket-indicator.tsx

src/packages/frontend/i18n/bin/extract.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# The interpolation pattern is a fixed string, because we intentionally trigger ID colissions.
44
# There is just one (unused) string without a unique ID – otherwise we always set an explicit hierarchical ID.
55
# Read the README in this directory for more information.
6-
pnpm exec formatjs extract $(git ls-files '**/*.tsx') i18n/*.ts jupyter/commands.ts \
6+
pnpm exec formatjs extract $(git ls-files '**/*.tsx') i18n/*.ts jupyter/commands.ts ../util/compute-states.ts ../util/i18n/*.ts \
77
--ignore='**/*.d.ts' --ignore='node_modules/*' \
88
--ignore='dist/*' \
99
--out-file i18n/extracted.json \

src/packages/frontend/i18n/common.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,23 @@ export const labels = defineMessages({
533533
id: "labels.language",
534534
defaultMessage: "Language",
535535
},
536+
always_running: {
537+
id: "labels.always_running",
538+
defaultMessage: "Always Running",
539+
},
540+
idle_timeout: {
541+
id: "labels.idle_timeout",
542+
defaultMessage: "Idle Timeout",
543+
},
544+
uptime: {
545+
id: "labels.uptime",
546+
defaultMessage: "Uptime",
547+
},
548+
more_info: {
549+
id: "labels.more_info",
550+
defaultMessage: "More info",
551+
description: "Short label for showing 'more information' about something",
552+
},
536553
});
537554

538555
export const menu = defineMessages({

src/packages/frontend/i18n/i18n.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { editor, jupyter, labels, menu } from "./common";
2-
import { IntlMessage } from "./types";
2+
import type { IntlMessage } from "./index";
33

44
export type Data = { [key in string]: IntlMessage };
55

src/packages/frontend/i18n/index.ts

Lines changed: 12 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,33 @@
66
import {
77
createIntl,
88
createIntlCache,
9-
defineMessage,
109
IntlShape,
1110
MessageFormatElement,
1211
} from "react-intl";
1312

14-
import { NAMES, OTHER_SETTINGS_LOCALE_KEY } from "@cocalc/util/i18n";
1513
import { AccountState } from "@cocalc/frontend/account/types";
1614
import { redux } from "@cocalc/frontend/app-framework";
1715
import {
1816
DEFAULT_LOCALE,
1917
KEEP_EN_LOCALE,
2018
Locale,
2119
} from "@cocalc/util/consts/locale";
20+
import {
21+
isIntlMessage,
22+
LOCALIZATIONS,
23+
OTHER_SETTINGS_LOCALE_KEY,
24+
} from "@cocalc/util/i18n";
25+
import type { IntlMessage } from "@cocalc/util/i18n/types";
2226
import { unreachable } from "@cocalc/util/misc";
23-
import { IntlMessage, isIntlMessage } from "./types";
2427

2528
export { dialogs, editor, jupyter, labels, menu } from "./common";
2629

27-
export { DEFAULT_LOCALE, isIntlMessage, OTHER_SETTINGS_LOCALE_KEY };
30+
export {
31+
DEFAULT_LOCALE,
32+
isIntlMessage,
33+
OTHER_SETTINGS_LOCALE_KEY,
34+
LOCALIZATIONS,
35+
};
2836

2937
export type { IntlMessage, Locale };
3038

@@ -103,160 +111,3 @@ export async function getIntl(): Promise<IntlShape> {
103111
const messages: Messages = await loadLocaleMessages(locale);
104112
return createIntl({ locale, messages }, cache);
105113
}
106-
107-
// The ordering is a bit "opinionated". The top languages are European ones, and German has the best quality translations.
108-
// Then come other European languges, kind of alphabetical.
109-
// Then, the Asian group starts with Chinese, as the largest group.
110-
export const LOCALIZATIONS: {
111-
[key in Locale]: {
112-
name: string;
113-
flag: string;
114-
native: string;
115-
trans: IntlMessage;
116-
};
117-
} = {
118-
en: {
119-
name: NAMES.en,
120-
flag: "🇺🇸",
121-
native: "English",
122-
trans: defineMessage({
123-
id: "i18n.localization.lang.english",
124-
defaultMessage: "English",
125-
}),
126-
},
127-
de: {
128-
name: NAMES.de,
129-
flag: "🇩🇪",
130-
native: "Deutsch",
131-
trans: defineMessage({
132-
id: "i18n.localization.lang.german",
133-
defaultMessage: "German",
134-
}),
135-
},
136-
es: {
137-
name: NAMES.es,
138-
flag: "🇪🇸",
139-
native: "Español",
140-
trans: defineMessage({
141-
id: "i18n.localization.lang.spanish",
142-
defaultMessage: "Spanish",
143-
}),
144-
},
145-
fr: {
146-
name: NAMES.fr,
147-
flag: "🇫🇷",
148-
native: "Français",
149-
trans: defineMessage({
150-
id: "i18n.localization.lang.french",
151-
defaultMessage: "French",
152-
}),
153-
},
154-
it: {
155-
name: NAMES.it,
156-
flag: "🇮🇹",
157-
native: "Italiano",
158-
trans: defineMessage({
159-
id: "i18n.localization.lang.italian",
160-
defaultMessage: "Italian",
161-
}),
162-
},
163-
pl: {
164-
name: NAMES.pl,
165-
flag: "🇵🇱",
166-
native: "Polski",
167-
trans: defineMessage({
168-
id: "i18n.localization.lang.polish",
169-
defaultMessage: "Polish",
170-
}),
171-
},
172-
hu: {
173-
name: NAMES.hu,
174-
flag: "🇭🇺",
175-
native: "Magyar",
176-
trans: defineMessage({
177-
id: "i18n.localization.lang.hungarian",
178-
defaultMessage: "Hungarian",
179-
}),
180-
},
181-
ar: {
182-
name: NAMES.ar,
183-
flag: "🇪🇬",
184-
native: "العربية",
185-
trans: defineMessage({
186-
id: "i18n.localization.lang.arabic",
187-
defaultMessage: "Arabic",
188-
}),
189-
},
190-
pt: {
191-
name: NAMES.pt,
192-
flag: "🇵🇹",
193-
native: "Português",
194-
trans: defineMessage({
195-
id: "i18n.localization.lang.portuguese",
196-
defaultMessage: "Portuguese",
197-
}),
198-
},
199-
tr: {
200-
name: NAMES.tr,
201-
flag: "🇹🇷",
202-
native: "Türkçe",
203-
trans: defineMessage({
204-
id: "i18n.localization.lang.turkish",
205-
defaultMessage: "Turkish",
206-
}),
207-
},
208-
he: {
209-
name: NAMES.he,
210-
flag: "🇮🇱",
211-
native: "עִבְרִית",
212-
trans: defineMessage({
213-
id: "i18n.localization.lang.hebrew",
214-
defaultMessage: "Hebrew",
215-
}),
216-
},
217-
zh: {
218-
name: NAMES.zh,
219-
flag: "🇨🇳",
220-
native: "中文",
221-
trans: defineMessage({
222-
id: "i18n.localization.lang.chinese",
223-
defaultMessage: "Chinese",
224-
}),
225-
},
226-
ja: {
227-
name: NAMES.ja,
228-
flag: "🇯🇵",
229-
native: "日本語",
230-
trans: defineMessage({
231-
id: "i18n.localization.lang.japanese",
232-
defaultMessage: "Japanese",
233-
}),
234-
},
235-
hi: {
236-
name: NAMES.hi,
237-
flag: "🇮🇳",
238-
native: "हिन्दी",
239-
trans: defineMessage({
240-
id: "i18n.localization.lang.hindi",
241-
defaultMessage: "Hindi",
242-
}),
243-
},
244-
ko: {
245-
name: NAMES.ko,
246-
flag: "🇰🇷",
247-
native: "한국어",
248-
trans: defineMessage({
249-
id: "i18n.localization.lang.korean",
250-
defaultMessage: "Korean",
251-
}),
252-
},
253-
ru: {
254-
name: NAMES.ru,
255-
flag: "🇷🇺",
256-
native: "Русский",
257-
trans: defineMessage({
258-
id: "i18n.localization.lang.russian",
259-
defaultMessage: "Russian",
260-
}),
261-
},
262-
} as const;

0 commit comments

Comments
 (0)