@gradio/core 1.1.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/src/Blocks.svelte +33 -2
  3. package/dist/src/_init.js +2 -2
  4. package/dist/src/api_docs/ApiBanner.svelte +6 -2
  5. package/dist/src/api_docs/ApiBanner.svelte.d.ts +1 -1
  6. package/dist/src/api_docs/ApiDocs.svelte +41 -25
  7. package/dist/src/api_docs/CodeSnippet.svelte +67 -170
  8. package/dist/src/api_docs/CodeSnippet.svelte.d.ts +2 -6
  9. package/dist/src/api_docs/CopyMarkdown.svelte +7 -2
  10. package/dist/src/api_docs/CopyMarkdown.svelte.d.ts +1 -1
  11. package/dist/src/api_docs/InstallSnippet.svelte +6 -1
  12. package/dist/src/api_docs/InstallSnippet.svelte.d.ts +1 -1
  13. package/dist/src/api_docs/ParametersSnippet.svelte +6 -1
  14. package/dist/src/api_docs/ParametersSnippet.svelte.d.ts +1 -1
  15. package/dist/src/api_docs/RecordingSnippet.svelte +6 -1
  16. package/dist/src/api_docs/RecordingSnippet.svelte.d.ts +1 -1
  17. package/dist/src/api_docs/ResponseSnippet.svelte +6 -1
  18. package/dist/src/api_docs/ResponseSnippet.svelte.d.ts +1 -1
  19. package/dist/src/api_docs/Settings.svelte +2 -7
  20. package/dist/src/api_docs/SettingsBanner.svelte +0 -3
  21. package/dist/src/api_docs/SkillSnippet.svelte +125 -0
  22. package/dist/src/api_docs/SkillSnippet.svelte.d.ts +20 -0
  23. package/dist/src/api_docs/img/skill.svg +10 -0
  24. package/dist/src/api_docs/utils.d.ts +0 -1
  25. package/dist/src/api_docs/utils.js +0 -22
  26. package/dist/src/dependency.d.ts +3 -1
  27. package/dist/src/dependency.js +39 -1
  28. package/dist/src/gradio_helper.js +5 -13
  29. package/dist/src/i18n.d.ts +1 -3
  30. package/dist/src/i18n.js +6 -53
  31. package/dist/src/init.svelte.js +81 -82
  32. package/package.json +49 -49
  33. package/src/Blocks.svelte +33 -2
  34. package/src/_init.ts +2 -2
  35. package/src/api_docs/ApiBanner.svelte +6 -2
  36. package/src/api_docs/ApiDocs.svelte +41 -25
  37. package/src/api_docs/CodeSnippet.svelte +67 -170
  38. package/src/api_docs/CopyMarkdown.svelte +7 -2
  39. package/src/api_docs/InstallSnippet.svelte +6 -1
  40. package/src/api_docs/ParametersSnippet.svelte +6 -1
  41. package/src/api_docs/RecordingSnippet.svelte +6 -1
  42. package/src/api_docs/ResponseSnippet.svelte +6 -1
  43. package/src/api_docs/Settings.svelte +2 -7
  44. package/src/api_docs/SettingsBanner.svelte +0 -3
  45. package/src/api_docs/SkillSnippet.svelte +125 -0
  46. package/src/api_docs/img/skill.svg +10 -0
  47. package/src/api_docs/utils.ts +0 -25
  48. package/src/dependency.ts +39 -1
  49. package/src/gradio_helper.ts +5 -17
  50. package/src/i18n.test.ts +41 -9
  51. package/src/i18n.ts +9 -62
  52. package/src/init.svelte.ts +95 -99
@@ -5,7 +5,12 @@
5
5
  export let is_running: boolean;
6
6
  export let endpoint_returns: any;
7
7
  export let js_returns: any;
8
- export let current_language: "python" | "javascript" | "bash" | "mcp";
8
+ export let current_language:
9
+ | "python"
10
+ | "javascript"
11
+ | "bash"
12
+ | "skill"
13
+ | "mcp";
9
14
  </script>
10
15
 
11
16
  <h4>
@@ -15,7 +15,7 @@ declare const ParametersSnippet: $$__sveltets_2_IsomorphicComponent<{
15
15
  is_running: boolean;
16
16
  endpoint_returns: any;
17
17
  js_returns: any;
18
- current_language: "python" | "javascript" | "bash" | "mcp";
18
+ current_language: "python" | "javascript" | "bash" | "skill" | "mcp";
19
19
  }, {
20
20
  [evt: string]: CustomEvent<any>;
21
21
  }, {}, {}, string>;
@@ -9,7 +9,12 @@
9
9
  export let short_root: string;
10
10
  export let root: string;
11
11
  export let api_prefix = "";
12
- export let current_language: "python" | "javascript" | "bash" | "mcp";
12
+ export let current_language:
13
+ | "python"
14
+ | "javascript"
15
+ | "bash"
16
+ | "skill"
17
+ | "mcp";
13
18
  export let username: string | null;
14
19
 
15
20
  let python_code: HTMLElement;
@@ -17,7 +17,7 @@ declare const RecordingSnippet: $$__sveltets_2_IsomorphicComponent<{
17
17
  short_root: string;
18
18
  root: string;
19
19
  api_prefix?: string;
20
- current_language: "python" | "javascript" | "bash" | "mcp";
20
+ current_language: "python" | "javascript" | "bash" | "skill" | "mcp";
21
21
  username: string | null;
22
22
  api_calls?: Payload[];
23
23
  }, {
@@ -4,7 +4,12 @@
4
4
  export let is_running: boolean;
5
5
  export let endpoint_returns: any;
6
6
  export let js_returns: any;
7
- export let current_language: "python" | "javascript" | "bash" | "mcp";
7
+ export let current_language:
8
+ | "python"
9
+ | "javascript"
10
+ | "bash"
11
+ | "skill"
12
+ | "mcp";
8
13
  </script>
9
14
 
10
15
  <h4>
@@ -15,7 +15,7 @@ declare const ResponseSnippet: $$__sveltets_2_IsomorphicComponent<{
15
15
  is_running: boolean;
16
16
  endpoint_returns: any;
17
17
  js_returns: any;
18
- current_language: "python" | "javascript" | "bash" | "mcp";
18
+ current_language: "python" | "javascript" | "bash" | "skill" | "mcp";
19
19
  }, {
20
20
  [evt: string]: CustomEvent<any>;
21
21
  }, {}, {}, string>;
@@ -6,6 +6,7 @@
6
6
  import { BaseCheckbox as Checkbox } from "@gradio/checkbox";
7
7
  import { language_choices, changeLocale } from "../i18n";
8
8
  import { locale, _ } from "svelte-i18n";
9
+ import { get } from "svelte/store";
9
10
  import record from "./img/record.svg";
10
11
 
11
12
  let {
@@ -52,15 +53,9 @@
52
53
  };
53
54
  });
54
55
 
55
- let current_locale: string = $state("en");
56
+ let current_locale: string = $state(get(locale) ?? "en");
56
57
  let current_theme: "light" | "dark" | "system" = $state("system");
57
58
 
58
- locale.subscribe((value) => {
59
- if (value) {
60
- current_locale = value;
61
- }
62
- });
63
-
64
59
  function handleLanguageChange(value: string): void {
65
60
  const new_locale = value;
66
61
  changeLocale(new_locale);
@@ -2,11 +2,8 @@
2
2
  import { _ } from "svelte-i18n";
3
3
  import settings_logo from "./img/settings-logo.svg";
4
4
  import Clear from "./img/clear.svelte";
5
- import { setupi18n } from "../i18n";
6
5
 
7
6
  let { root, onclose }: { root: string; onclose?: () => void } = $props();
8
-
9
- setupi18n();
10
7
  </script>
11
8
 
12
9
  <h2>
@@ -0,0 +1,125 @@
1
+ <script lang="ts">
2
+ import { Block } from "@gradio/atoms";
3
+ import CopyButton from "./CopyButton.svelte";
4
+
5
+ export let space_id: string | null;
6
+
7
+ $: effective_space_id = space_id || "";
8
+ $: skill_id = effective_space_id.replace("/", "-");
9
+ $: install_gradio = "pip install --upgrade gradio";
10
+ $: install_cmd_claude = `gradio skills add ${effective_space_id} --claude`;
11
+ $: install_cmd_cursor = `gradio skills add ${effective_space_id} --cursor`;
12
+ $: install_cmd_codex = `gradio skills add ${effective_space_id} --codex`;
13
+
14
+ $: skill_preview = `---
15
+ name: ${skill_id}
16
+ description: Use the ${effective_space_id} Gradio Space via API. Provides Python, JavaScript, and cURL usage examples.
17
+ ---
18
+
19
+ # ${effective_space_id}
20
+
21
+ This skill describes how to use the ${effective_space_id} Gradio Space programmatically.
22
+
23
+ ## API Endpoints
24
+ ...`;
25
+ </script>
26
+
27
+ <div class="skill-content">
28
+ <p class="padded">1. Make sure you are using the latest version of Gradio:</p>
29
+ <Block>
30
+ <code>
31
+ <div class="copy">
32
+ <CopyButton code={install_gradio} />
33
+ </div>
34
+ <div>
35
+ <pre>$ {install_gradio}</pre>
36
+ </div>
37
+ </code>
38
+ </Block>
39
+
40
+ <p class="padded">
41
+ 2. Install the usage of this Space as a Skill for your coding agent by
42
+ running this in your terminal:
43
+ </p>
44
+ <Block>
45
+ <code>
46
+ <div class="copy">
47
+ <CopyButton code={install_cmd_claude} />
48
+ </div>
49
+ <div>
50
+ <pre>$ {install_cmd_claude}</pre>
51
+ </div>
52
+ </code>
53
+ </Block>
54
+ <p class="hint">
55
+ Instead of <span class="inline-code">--claude</span>, you can use
56
+ <span class="inline-code">--cursor</span>,
57
+ <span class="inline-code">--codex</span>, or
58
+ <span class="inline-code">--opencode</span>. Combine flags to install for
59
+ multiple agents. Use <span class="inline-code">--global</span> to install user-level
60
+ instead of per-project.
61
+ </p>
62
+
63
+ <p class="padded">
64
+ 3. This will add a skill to your coding agent that describes how to use this
65
+ Space via Python, JavaScript, and cURL API. The skill will look like this:
66
+ </p>
67
+ <Block>
68
+ <code>
69
+ <div class="copy">
70
+ <CopyButton code={skill_preview} />
71
+ </div>
72
+ <div>
73
+ <pre>{skill_preview}</pre>
74
+ </div>
75
+ </code>
76
+ </Block>
77
+ </div>
78
+
79
+ <style>
80
+ .skill-content {
81
+ margin-top: var(--size-2);
82
+ }
83
+
84
+ p.padded {
85
+ padding: 15px 0px;
86
+ font-size: var(--text-lg);
87
+ }
88
+
89
+ .hint {
90
+ margin-top: var(--size-2);
91
+ color: var(--body-text-color);
92
+ opacity: 0.8;
93
+ font-size: var(--text-md);
94
+ line-height: 1.6;
95
+ }
96
+
97
+ .inline-code {
98
+ display: inline;
99
+ font-family: var(--font-mono);
100
+ font-size: var(--text-md);
101
+ background: var(--background-fill-secondary);
102
+ padding: 1px 4px;
103
+ border-radius: var(--radius-sm);
104
+ }
105
+
106
+ code pre {
107
+ overflow-x: auto;
108
+ color: var(--body-text-color);
109
+ font-family: var(--font-mono);
110
+ tab-size: 2;
111
+ }
112
+
113
+ code {
114
+ position: relative;
115
+ display: block;
116
+ }
117
+
118
+ .copy {
119
+ position: absolute;
120
+ top: 0;
121
+ right: 0;
122
+ margin-top: -5px;
123
+ margin-right: -5px;
124
+ }
125
+ </style>
@@ -0,0 +1,20 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: Props & {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const SkillSnippet: $$__sveltets_2_IsomorphicComponent<{
15
+ space_id: string | null;
16
+ }, {
17
+ [evt: string]: CustomEvent<any>;
18
+ }, {}, {}, string>;
19
+ type SkillSnippet = InstanceType<typeof SkillSnippet>;
20
+ export default SkillSnippet;
@@ -0,0 +1,10 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="14 22 122 106" style="background-color: #ffffff;">
2
+ <rect x="22" y="28" width="106" height="94" rx="8" fill="none" stroke="#374151" stroke-width="6"/>
3
+ <rect x="22" y="28" width="106" height="20" rx="8" fill="#374151" fill-opacity="0.08"/>
4
+ <circle cx="38" cy="38" r="4" fill="#ff5f57"/>
5
+ <circle cx="52" cy="38" r="4" fill="#febc2e"/>
6
+ <circle cx="66" cy="38" r="4" fill="#28c840"/>
7
+ <text x="38" y="76" fill="#374151" font-family="monospace" font-size="22" font-weight="bold">&gt;_</text>
8
+ <rect x="38" y="88" width="50" height="4" rx="2" fill="#374151" fill-opacity="0.35"/>
9
+ <rect x="38" y="100" width="35" height="4" rx="2" fill="#374151" fill-opacity="0.2"/>
10
+ </svg>
@@ -1,4 +1,3 @@
1
1
  export declare function represent_value(value: string, type: string | undefined, lang?: "js" | "py" | "bash" | null): string | null | number | boolean | Record<string, unknown>;
2
- export declare function is_potentially_nested_file_data(obj: any): boolean;
3
2
  export declare function format_latency(val: number): string;
4
3
  export declare function get_color_from_success_rate(success_rate: number): string;
@@ -48,28 +48,6 @@ export function represent_value(value, type, lang = null) {
48
48
  }
49
49
  return stringify_except_file_function(value);
50
50
  }
51
- export function is_potentially_nested_file_data(obj) {
52
- if (typeof obj === "object" && obj !== null) {
53
- if (obj.hasOwnProperty("url") && obj.hasOwnProperty("meta")) {
54
- if (typeof obj.meta === "object" &&
55
- obj.meta !== null &&
56
- obj.meta._type === "gradio.FileData") {
57
- return true;
58
- }
59
- }
60
- }
61
- if (typeof obj === "object" && obj !== null) {
62
- for (let key in obj) {
63
- if (typeof obj[key] === "object") {
64
- let result = is_potentially_nested_file_data(obj[key]);
65
- if (result) {
66
- return true;
67
- }
68
- }
69
- }
70
- }
71
- return false;
72
- }
73
51
  function simplify_file_data(obj) {
74
52
  if (typeof obj === "object" && obj !== null && !Array.isArray(obj)) {
75
53
  if ("url" in obj &&
@@ -85,8 +85,10 @@ export declare class DependencyManager {
85
85
  get_state_cb: GetStateCallback;
86
86
  rerender_cb: RerenderCallback;
87
87
  log_cb: LogCallback;
88
+ on_connection_lost_cb: () => void;
88
89
  loading_stati: LoadingStatus;
89
- constructor(dependencies: IDependency[], client: Client, update_state_cb: (id: number, state: Record<string, unknown>, check_visibility?: boolean) => Promise<void>, get_state_cb: (id: number) => Promise<Record<string, unknown> | null>, rerender_cb: (components: ComponentMeta[], layout: LayoutNode) => void, log_cb: (title: string, message: string, fn_index: number, type: ToastMessage["type"], duration?: number | null, visible?: boolean) => void, add_to_api_calls: (payload: Payload) => void);
90
+ connection_lost: boolean;
91
+ constructor(dependencies: IDependency[], client: Client, update_state_cb: (id: number, state: Record<string, unknown>, check_visibility?: boolean) => Promise<void>, get_state_cb: (id: number) => Promise<Record<string, unknown> | null>, rerender_cb: (components: ComponentMeta[], layout: LayoutNode) => void, log_cb: (title: string, message: string, fn_index: number, type: ToastMessage["type"], duration?: number | null, visible?: boolean) => void, add_to_api_calls: (payload: Payload) => void, on_connection_lost_cb: () => void);
90
92
  reload(dependencies: IDependency[], update_state: UpdateStateCallback, get_state: GetStateCallback, rerender: RerenderCallback, client: Client): void;
91
93
  register_loading_stati(deps: Map<number, Dependency>): void;
92
94
  clear_loading_status(component_id: number): void;
@@ -118,13 +118,16 @@ export class DependencyManager {
118
118
  get_state_cb;
119
119
  rerender_cb;
120
120
  log_cb;
121
+ on_connection_lost_cb;
121
122
  loading_stati = new LoadingStatus();
122
- constructor(dependencies, client, update_state_cb, get_state_cb, rerender_cb, log_cb, add_to_api_calls) {
123
+ connection_lost = false;
124
+ constructor(dependencies, client, update_state_cb, get_state_cb, rerender_cb, log_cb, add_to_api_calls, on_connection_lost_cb) {
123
125
  this.add_to_api_calls = add_to_api_calls;
124
126
  this.log_cb = log_cb;
125
127
  this.update_state_cb = update_state_cb;
126
128
  this.get_state_cb = get_state_cb;
127
129
  this.rerender_cb = rerender_cb;
130
+ this.on_connection_lost_cb = on_connection_lost_cb;
128
131
  this.client = client;
129
132
  this.reload(dependencies, update_state_cb, get_state_cb, rerender_cb, client);
130
133
  }
@@ -180,6 +183,8 @@ export class DependencyManager {
180
183
  * @returns a value if there is no backend fn, a 'submission' if there is a backend fn, or null if there is no dependency
181
184
  */
182
185
  async dispatch(event_meta) {
186
+ if (this.connection_lost)
187
+ return;
183
188
  let deps;
184
189
  if (event_meta.type === "fn") {
185
190
  const dep = this.dependencies_by_fn.get(event_meta.fn_index);
@@ -314,6 +319,19 @@ export class DependencyManager {
314
319
  this.update_loading_stati_state();
315
320
  }
316
321
  else if (result.stage === "error") {
322
+ if (result.broken || result.session_not_found) {
323
+ if (!this.connection_lost) {
324
+ this.connection_lost = true;
325
+ this.on_connection_lost_cb();
326
+ }
327
+ this.loading_stati.update({
328
+ status: "complete",
329
+ fn_index: dep.id,
330
+ stream_state: null
331
+ });
332
+ this.update_loading_stati_state();
333
+ break submit_loop;
334
+ }
317
335
  if (Array.isArray(result?.message)) {
318
336
  result.message.forEach((m, i) => {
319
337
  this.update_state_cb(dep.inputs[i], {
@@ -567,6 +585,26 @@ export class DependencyManager {
567
585
  });
568
586
  this.update_loading_stati_state();
569
587
  this.submissions.delete(id);
588
+ // Need to trigger any dependencies that are waiting for this one to complete
589
+ const { failure, all } = this.dependencies_by_fn
590
+ .get(id)
591
+ ?.get_triggers() || { failure: [], all: [] };
592
+ failure.forEach((dep_id) => {
593
+ this.dispatch({
594
+ type: "fn",
595
+ fn_index: dep_id,
596
+ event_data: null,
597
+ target_id: id
598
+ });
599
+ });
600
+ all.forEach((dep_id) => {
601
+ this.dispatch({
602
+ type: "fn",
603
+ fn_index: dep_id,
604
+ event_data: null,
605
+ target_id: id
606
+ });
607
+ });
570
608
  }
571
609
  }
572
610
  }
@@ -1,28 +1,20 @@
1
- import { all_common_keys } from "./i18n";
2
1
  import { _ } from "svelte-i18n";
3
2
  import { get, derived } from "svelte/store";
4
3
  export { Gradio } from "@gradio/utils";
4
+ import { I18N_MARKER, translate_i18n_marker } from "@gradio/utils";
5
5
  export function formatter(value) {
6
6
  if (value == null) {
7
7
  return "";
8
8
  }
9
9
  const string_value = String(value);
10
10
  const translate = get(_);
11
- let direct_translation = translate(string_value);
11
+ if (string_value.includes(I18N_MARKER)) {
12
+ return translate_i18n_marker(string_value, translate);
13
+ }
14
+ const direct_translation = translate(string_value);
12
15
  if (direct_translation !== string_value) {
13
16
  return direct_translation;
14
17
  }
15
- const lower_value = string_value.toLowerCase();
16
- for (const common_key of all_common_keys) {
17
- const key_name = common_key.substring(common_key.indexOf(".") + 1);
18
- if (lower_value === key_name) {
19
- const translation = translate(common_key);
20
- if (translation !== common_key) {
21
- return translation;
22
- }
23
- break;
24
- }
25
- }
26
18
  return string_value;
27
19
  }
28
20
  export const reactive_formatter = derived(_, () => formatter);
@@ -15,11 +15,9 @@ export interface LangsRecord {
15
15
  };
16
16
  }
17
17
  export declare function is_translation_metadata(obj: any): obj is I18nData;
18
- export declare const i18n_marker = "__i18n__";
19
- export declare function translate_if_needed(value: any): string;
18
+ export { I18N_MARKER as i18n_marker } from "@gradio/utils";
20
19
  export declare function process_langs(): LangsRecord;
21
20
  export declare const language_choices: [string, string][];
22
- export declare let all_common_keys: Set<string>;
23
21
  export declare function setupi18n(custom_translations?: Record<string, Record<string, string>>, preferred_locale?: string): Promise<void>;
24
22
  export declare function changeLocale(new_locale: string): void;
25
23
  export declare function get_initial_locale(browser_locale: string | null, available_locales: string[], fallback_locale?: string): string;
package/dist/src/i18n.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { addMessages, init, getLocaleFromNavigator, locale, register, waitLocale } from "svelte-i18n";
2
- import { formatter } from "./gradio_helper";
3
2
  import { loading } from "./lang/loading";
4
3
  const lang_map = {
5
4
  ar: "العربية",
@@ -43,55 +42,7 @@ export function is_translation_metadata(obj) {
43
42
  typeof obj.key === "string";
44
43
  return result;
45
44
  }
46
- export const i18n_marker = "__i18n__";
47
- // handles strings with embedded JSON metadata of shape "__i18n__{"key": "some.key"}"
48
- export function translate_if_needed(value) {
49
- if (typeof value !== "string") {
50
- return value;
51
- }
52
- const marker_index = value.indexOf(i18n_marker);
53
- if (marker_index === -1) {
54
- return value;
55
- }
56
- try {
57
- const before_marker = marker_index > 0 ? value.substring(0, marker_index) : "";
58
- const after_marker_index = marker_index + i18n_marker.length;
59
- const json_start = value.indexOf("{", after_marker_index);
60
- let json_end = -1;
61
- let bracket_count = 0;
62
- for (let i = json_start; i < value.length; i++) {
63
- if (value[i] === "{")
64
- bracket_count++;
65
- if (value[i] === "}")
66
- bracket_count--;
67
- if (bracket_count === 0) {
68
- json_end = i + 1;
69
- break;
70
- }
71
- }
72
- if (json_end === -1) {
73
- console.error("Could not find end of JSON in i18n string");
74
- return value;
75
- }
76
- const metadata_json = value.substring(json_start, json_end);
77
- const after_json = json_end < value.length ? value.substring(json_end) : "";
78
- try {
79
- const metadata = JSON.parse(metadata_json);
80
- if (metadata && metadata.key) {
81
- const translated = formatter(metadata.key);
82
- return before_marker + translated + after_json;
83
- }
84
- }
85
- catch (jsonError) {
86
- console.error("Error parsing i18n JSON:", jsonError);
87
- }
88
- return value;
89
- }
90
- catch (e) {
91
- console.error("Error processing translation:", e);
92
- return value;
93
- }
94
- }
45
+ export { I18N_MARKER as i18n_marker } from "@gradio/utils";
95
46
  export function process_langs() {
96
47
  const lazy_langs = Object.fromEntries(Object.entries(langs).map(([path, mod]) => [
97
48
  path.split("/").pop().split(".")[0],
@@ -105,7 +56,6 @@ export function process_langs() {
105
56
  const processed_langs = process_langs();
106
57
  const available_locales = Object.keys(processed_langs);
107
58
  export const language_choices = Object.entries(processed_langs).map(([code]) => [lang_map[code] || code, code]);
108
- export let all_common_keys = new Set();
109
59
  let i18n_initialized = false;
110
60
  let previous_translations;
111
61
  function get_lang_from_preferred_locale(header) {
@@ -125,10 +75,13 @@ export async function setupi18n(custom_translations, preferred_locale) {
125
75
  if (i18n_initialized && !should_reinitialize) {
126
76
  return;
127
77
  }
128
- previous_translations = custom_translations;
78
+ const translations_to_use = custom_translations ?? previous_translations ?? {};
79
+ if (custom_translations !== undefined) {
80
+ previous_translations = custom_translations;
81
+ }
129
82
  load_translations({
130
83
  processed_langs,
131
- custom_translations: custom_translations ?? {}
84
+ custom_translations: translations_to_use
132
85
  });
133
86
  let initial_locale = null;
134
87
  const browser_locale = getLocaleFromNavigator();