Skip to content

Commit 6711a43

Browse files
committed
i18n/site-setting: enable some, only english, all are default
1 parent e08807b commit 6711a43

File tree

9 files changed

+74
-10
lines changed

9 files changed

+74
-10
lines changed

src/packages/frontend/account/account-page.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export const AccountPage: React.FC = () => {
8585
const ssh_gateway = useTypedRedux("customize", "ssh_gateway");
8686
const is_commercial = useTypedRedux("customize", "is_commercial");
8787
const get_api_key = useTypedRedux("page", "get_api_key");
88+
const i18n_enabled = useTypedRedux("customize", "i18n");
8889

8990
// for each exclusive domain, tell the user which strategy to use
9091
const exclusive_sso_domains = React.useMemo(() => {
@@ -262,16 +263,22 @@ export const AccountPage: React.FC = () => {
262263
return items;
263264
}
264265

265-
function renderI18N(): JSX.Element {
266+
function renderI18N(): JSX.Element | null {
267+
if (
268+
i18n_enabled.isEmpty() ||
269+
(i18n_enabled.size === 1 && i18n_enabled.includes("en"))
270+
)
271+
return null;
272+
266273
const i18n: Locale = getLocale(other_settings);
267274

268275
const items: MenuProps["items"] =
269-
Object.entries(LOCALIZATIONS).map(
270-
([key, { name, trans, native, flag }]) => {
276+
Object.entries(LOCALIZATIONS)
277+
.filter(([key, _]) => i18n_enabled.includes(key as any))
278+
.map(([key, { name, trans, native, flag }]) => {
271279
const other = key === locale ? name : intl.formatMessage(trans);
272280
return { key, label: `${flag} ${native} (${other})` };
273-
},
274-
) ?? [];
281+
}) ?? [];
275282

276283
items.push({ type: "divider" });
277284
items.push({

src/packages/frontend/account/account-preferences.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import { Form, Switch, Tooltip } from "antd";
77
import { join } from "path";
8+
import { FormattedMessage } from "react-intl";
89

910
import { Col, Row } from "@cocalc/frontend/antd-bootstrap";
1011
import { redux, useTypedRedux } from "@cocalc/frontend/app-framework";
@@ -20,7 +21,6 @@ import { AccountSettings } from "./settings/account-settings";
2021
import ApiKeys from "./settings/api-keys";
2122
import TableError from "./table-error";
2223
import { TerminalSettings } from "./terminal-settings";
23-
import { FormattedMessage } from "react-intl";
2424

2525
export const AccountPreferences: React.FC = () => {
2626
const account_id = useTypedRedux("account", "account_id");

src/packages/frontend/admin/site-settings/row-entry-inner.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ import { USER_SELECTABLE_LANGUAGE_MODELS } from "@cocalc/util/db-schema/llm-util
1515
import {
1616
ConfigValid,
1717
to_list_of_llms,
18+
to_list_of_locale,
1819
} from "@cocalc/util/db-schema/site-defaults";
1920
import { RowEntryInnerProps } from "./row-entry";
21+
import { LOCALE } from "@cocalc/util/consts/locale";
22+
import { LOCALIZATIONS } from "../../i18n";
2023

2124
export function rowEntryStyle(value, valid?: ConfigValid): CSSProperties {
2225
if (
@@ -65,6 +68,29 @@ export function RowEntryInner({
6568
)}
6669
/>
6770
);
71+
} else if (name === "i18n") {
72+
return (
73+
<Select
74+
mode="multiple"
75+
style={{ width: "100%" }}
76+
placeholder="Select user selectable language locale"
77+
optionLabelProp="label"
78+
defaultValue={to_list_of_locale(value, false)}
79+
onChange={(value: Array<string>) => {
80+
onChangeEntry(name, value.join(","));
81+
update();
82+
}}
83+
options={LOCALE.map((l) => {
84+
return { label: LOCALIZATIONS[l].name, value: l };
85+
})}
86+
optionRender={(option) => (
87+
<>
88+
{option.value ? LOCALIZATIONS[option.value].flag : ""}{" "}
89+
{option.label}
90+
</>
91+
)}
92+
/>
93+
);
6894
} else if (Array.isArray(valid)) {
6995
return (
7096
<Select

src/packages/frontend/customize.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import { sanitizeSoftwareEnv } from "@cocalc/util/sanitize-software-envs";
5757
import * as theme from "@cocalc/util/theme";
5858
import { CustomLLMPublic } from "@cocalc/util/types/llm";
5959
import { DefaultQuotaSetting, Upgrades } from "@cocalc/util/upgrades/quota";
60+
import { Locale } from "./i18n";
6061
export { TermsOfService } from "@cocalc/frontend/customize/terms-of-service";
6162

6263
// this sets UI modes for using a kubernetes based back-end
@@ -170,6 +171,8 @@ export interface CustomizeState {
170171
selectable_llms: List<string>;
171172
default_llm?: string;
172173
user_defined_llm: boolean;
174+
175+
i18n: List<Locale>;
173176
}
174177

175178
export class CustomizeStore extends Store<CustomizeState> {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env bash
2-
. ./i18n/common.sh
2+
. ./i18n/bin/common.sh
33
for L in $LANGS; do
44
pnpm exec formatjs compile --ast --format i18n/formatter.js --out-file ./i18n/$L.compiled.json ./i18n/$L.json
55
#rm ./i18n/$L.json # we might want to delete it at some point, but for now it is nice to have a copy of the translated messages
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env bash
2-
. ./i18n/common.sh
2+
. ./i18n/bin/common.sh
33
for L in $LANGS; do
44
simplelocalize download --apiKey $SIMPLELOCALIZE_KEY --downloadPath ./i18n/$L.json --downloadFormat single-language-json --languageKey=$L
55
done

src/packages/util/consts/locale.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
// ATTN: these languages have to match the frontend/package.json script "i18n:download",
77
// be valid for Antd (<AntdConfigProvider localize.../>),
88
// and also harmonize with localize::loadLocaleData
9-
const LOCALE = ["en", "es", "de", "zh"] as const;
9+
export const LOCALE = ["en", "es", "de", "zh"] as const;
1010

1111
export type Locale = (typeof LOCALE)[number];
1212

src/packages/util/db-schema/site-defaults.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
getDefaultLLM,
1717
isValidModel,
1818
} from "./llm-utils";
19+
import { LOCALE } from "../consts/locale";
1920

2021
export type ConfigValid = Readonly<string[]> | ((val: string) => boolean);
2122

@@ -40,6 +41,7 @@ export const TAGS = [
4041
"AI LLM",
4142
"Theme",
4243
"On-Prem",
44+
"I18N",
4345
] as const;
4446

4547
export type Tag = (typeof TAGS)[number];
@@ -83,6 +85,7 @@ export type SiteSettingsKeys =
8385
| "unlicensed_project_timetravel_limit"
8486
| "google_analytics"
8587
| "kucalc"
88+
| "i18n"
8689
| "dns"
8790
| "datastore"
8891
| "ssh_gateway"
@@ -194,6 +197,18 @@ export const onlyNonnegFloat = (val) =>
194197
((v) => onlyFloats(v) && v >= 0)(toFloat(val));
195198
export const onlyPosFloat = (val) =>
196199
((v) => onlyFloats(v) && v > 0)(toFloat(val));
200+
201+
export function to_list_of_locale(val?: string, fallbackAll = true): string[] {
202+
if (!val?.trim()) {
203+
return fallbackAll ? [...LOCALE] : [];
204+
}
205+
const list = val
206+
.split(",")
207+
.map((s) => s.trim())
208+
.filter((v) => LOCALE.includes(v as any));
209+
return list;
210+
}
211+
197212
export function to_list_of_llms(val?: string, fallbackAll = true): string[] {
198213
if (!val?.trim())
199214
return fallbackAll ? [...USER_SELECTABLE_LANGUAGE_MODELS] : [];
@@ -202,7 +217,6 @@ export function to_list_of_llms(val?: string, fallbackAll = true): string[] {
202217
.map((s) => s.trim())
203218
.filter((v) => USER_SELECTABLE_LANGUAGE_MODELS.includes(v as any));
204219
}
205-
206220
export const is_list_of_llms = (val: string) =>
207221
val
208222
.split(",")
@@ -542,6 +556,20 @@ export const site_settings_conf: SiteSettings = {
542556
valid: KUCALC_VALID_VALS,
543557
tags: ["On-Prem"],
544558
},
559+
i18n: {
560+
name: "Internationalization",
561+
desc: "Select, which languages the frontend should offer for users to translate to. Only 'English', no dropdown will be shown. No selection, all available translations are available (default).",
562+
default: "",
563+
valid: LOCALE,
564+
to_val: (v) => to_list_of_locale(v), // note: we store this as a comma separated list
565+
to_display: (val: string | string[]) => {
566+
const list = Array.isArray(val) ? val : to_list_of_locale(val);
567+
return isEqual(list, LOCALE)
568+
? "All translations are available."
569+
: list.join(", ");
570+
},
571+
tags: ["I18N"],
572+
},
545573
google_analytics: {
546574
name: "Google Analytics",
547575
desc: `A Google Analytics GA4 tag for tracking usage of your site ("G-...").`,

0 commit comments

Comments
 (0)