@gradio/core 0.14.0 → 0.15.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,61 @@
1
1
  # @gradio/core
2
2
 
3
+ ## 0.15.0
4
+
5
+ ### Features
6
+
7
+ - [#11027](https://github.com/gradio-app/gradio/pull/11027) [`eff532b`](https://github.com/gradio-app/gradio/commit/eff532b913a3c8f06f10a4f9471d3177e3744053) - Add new `ImageSlider` component. Thanks @pngwn!
8
+
9
+ ### Fixes
10
+
11
+ - [#11049](https://github.com/gradio-app/gradio/pull/11049) [`8d2aa3e`](https://github.com/gradio-app/gradio/commit/8d2aa3e99e165413bae1d8f5f3b4630e870308f5) - Ensure translations work as expected. Thanks @hannahblair!
12
+
13
+ ### Dependency updates
14
+
15
+ - @gradio/code@0.14.2
16
+ - @gradio/paramviewer@0.7.8
17
+ - @gradio/statustracker@0.10.10
18
+ - @gradio/image@0.22.3
19
+ - @gradio/upload@0.16.3
20
+ - @gradio/button@0.4.16
21
+ - @gradio/video@0.14.11
22
+ - @gradio/atoms@0.16.0
23
+ - @gradio/column@0.2.0
24
+ - @gradio/gallery@0.15.16
25
+ - @gradio/plot@0.9.15
26
+ - @gradio/textbox@0.10.10
27
+ - @gradio/checkbox@0.4.20
28
+ - @gradio/file@0.12.16
29
+
30
+ ## 0.14.1
31
+
32
+ ### Fixes
33
+
34
+ - [#11038](https://github.com/gradio-app/gradio/pull/11038) [`fd46e48`](https://github.com/gradio-app/gradio/commit/fd46e48f516a2907c469c7a6d3e67ae9f7068b29) - Be able to dispatch Info messages from a component. Thanks @freddyaboulton!
35
+
36
+ ## 0.14.0
37
+
38
+ ### Dependency updates
39
+
40
+ - @gradio/code@0.14.1
41
+ - @gradio/paramviewer@0.7.7
42
+ - @gradio/statustracker@0.10.9
43
+ - @gradio/tabitem@0.4.3
44
+ - @gradio/tabs@0.4.3
45
+ - @gradio/video@0.14.10
46
+ - @gradio/atoms@0.15.2
47
+ - @gradio/client@1.14.2
48
+ - @gradio/utils@0.10.2
49
+ - @gradio/button@0.4.15
50
+ - @gradio/upload@0.16.2
51
+ - @gradio/image@0.22.2
52
+ - @gradio/gallery@0.15.15
53
+ - @gradio/plot@0.9.14
54
+ - @gradio/textbox@0.10.9
55
+ - @gradio/file@0.12.15
56
+ - @gradio/checkbox@0.4.19
57
+ - @gradio/column@0.2.0
58
+
3
59
  ## 0.14.0
4
60
 
5
61
  ### Features
@@ -534,6 +534,8 @@ async function handle_mount() {
534
534
  messages = [new_message("Error", data, -1, event), ...messages];
535
535
  } else if (event === "warning") {
536
536
  messages = [new_message("Warning", data, -1, event), ...messages];
537
+ } else if (event === "info") {
538
+ messages = [new_message("Info", data, -1, event), ...messages];
537
539
  } else if (event == "clear_status") {
538
540
  update_status(id, "complete", data);
539
541
  } else if (event == "close_stream") {
@@ -0,0 +1,14 @@
1
+ export { Gradio } from "@gradio/utils";
2
+ export type I18nFormatter = typeof formatter;
3
+ /**
4
+ * i18n formatter with fallback to svelte-i18n's format function.
5
+ *
6
+ * @param value - The string to translate or format
7
+ * @returns The translated string
8
+ *
9
+ * This formatter attempts translation in the following order:
10
+ * 1. Direct translation of the input string
11
+ * 2. Checks if input matches any common key names
12
+ * 3. Falls back to svelte-i18n's format function
13
+ */
14
+ export declare function formatter(value: string | null | undefined): string;
@@ -1,4 +1,44 @@
1
- import { format } from "svelte-i18n";
1
+ import { format, _ } from "svelte-i18n";
2
2
  import { get } from "svelte/store";
3
+ import { all_common_keys } from "./i18n";
3
4
  export { Gradio } from "@gradio/utils";
4
- export const formatter = get(format);
5
+ /**
6
+ * i18n formatter with fallback to svelte-i18n's format function.
7
+ *
8
+ * @param value - The string to translate or format
9
+ * @returns The translated string
10
+ *
11
+ * This formatter attempts translation in the following order:
12
+ * 1. Direct translation of the input string
13
+ * 2. Checks if input matches any common key names
14
+ * 3. Falls back to svelte-i18n's format function
15
+ */
16
+ export function formatter(value) {
17
+ if (value == null) {
18
+ return "";
19
+ }
20
+ const string_value = String(value);
21
+ const translate = get(_);
22
+ const initial_formatter = get(format);
23
+ let direct_translation = translate(string_value);
24
+ if (direct_translation !== string_value) {
25
+ return direct_translation;
26
+ }
27
+ const lower_value = string_value.toLowerCase();
28
+ for (const common_key of all_common_keys) {
29
+ const key_name = common_key.substring(common_key.indexOf(".") + 1);
30
+ if (lower_value === key_name) {
31
+ const translation = translate(common_key);
32
+ if (translation !== common_key) {
33
+ return translation;
34
+ }
35
+ break;
36
+ }
37
+ }
38
+ // fall back to the svelte-i18n formatter to maintain compatibility
39
+ const formatted = initial_formatter(string_value);
40
+ if (formatted !== string_value) {
41
+ return formatted;
42
+ }
43
+ return string_value;
44
+ }
@@ -3,6 +3,7 @@ type LangsRecord = Record<string, {
3
3
  }>;
4
4
  export declare function process_langs(): LangsRecord;
5
5
  export declare const language_choices: [string, string][];
6
+ export declare let all_common_keys: Set<string>;
6
7
  export declare function setupi18n(): Promise<void>;
7
8
  export declare function changeLocale(new_locale: string): void;
8
9
  export {};
package/dist/src/i18n.js CHANGED
@@ -1,4 +1,4 @@
1
- import { addMessages, init, getLocaleFromNavigator, locale } from "svelte-i18n";
1
+ import { addMessages, init, getLocaleFromNavigator, locale, _ } from "svelte-i18n";
2
2
  const langs = import.meta.glob("./lang/*.json", {
3
3
  eager: true
4
4
  });
@@ -13,6 +13,7 @@ export function process_langs() {
13
13
  const processed_langs = process_langs();
14
14
  const available_locales = Object.keys(processed_langs);
15
15
  export const language_choices = Object.entries(processed_langs).map(([code, data]) => [data._name || code, code]);
16
+ export let all_common_keys = new Set();
16
17
  for (const lang in processed_langs) {
17
18
  addMessages(lang, processed_langs[lang]);
18
19
  }
@@ -22,13 +23,25 @@ export async function setupi18n() {
22
23
  return;
23
24
  }
24
25
  const browser_locale = getLocaleFromNavigator();
25
- const initial_locale = browser_locale && available_locales.includes(browser_locale)
26
- ? browser_locale
26
+ const normalized_locale = browser_locale?.split("-")[0];
27
+ const initial_locale = normalized_locale && available_locales.includes(normalized_locale)
28
+ ? normalized_locale
27
29
  : "en";
28
30
  await init({
29
31
  fallbackLocale: "en",
30
32
  initialLocale: initial_locale
31
33
  });
34
+ for (const lang_code in processed_langs) {
35
+ if (processed_langs[lang_code] &&
36
+ typeof processed_langs[lang_code] === "object" &&
37
+ processed_langs[lang_code].common &&
38
+ typeof processed_langs[lang_code].common === "object") {
39
+ const common_ns = processed_langs[lang_code].common;
40
+ for (const key in common_ns) {
41
+ all_common_keys.add(`common.${key}`);
42
+ }
43
+ }
44
+ }
32
45
  i18n_initialized = true;
33
46
  }
34
47
  export function changeLocale(new_locale) {
@@ -68,7 +68,8 @@
68
68
  "no_devices": "No devices found",
69
69
  "language": "Language",
70
70
  "display_theme": "Display Theme",
71
- "pwa": "Progressive Web App"
71
+ "pwa": "Progressive Web App",
72
+ "run": "Run"
72
73
  },
73
74
  "dataframe": {
74
75
  "incorrect_format": "Incorrect format, only CSV and TSV files are supported",
@@ -46,6 +46,7 @@
46
46
  "edit": "Editar",
47
47
  "empty": "Vacío",
48
48
  "error": "Error",
49
+ "flag": "Marcar",
49
50
  "hosted_on": "Alojado en",
50
51
  "loading": "Cargando",
51
52
  "logo": "logo",
@@ -45,6 +45,7 @@
45
45
  "download": "Télécharger",
46
46
  "edit": "Modifier",
47
47
  "empty": "Vide",
48
+ "flag": "Marquer",
48
49
  "error": "Erreur",
49
50
  "hosted_on": "Hébergé sur",
50
51
  "loading": "Chargement",
@@ -58,7 +59,8 @@
58
59
  "no_devices": "Aucun périphérique trouvé",
59
60
  "language": "Langue",
60
61
  "display_theme": "Thème d'affichage",
61
- "pwa": "Application web progressive"
62
+ "pwa": "Application web progressive",
63
+ "run": "Exécuter"
62
64
  },
63
65
  "dataframe": {
64
66
  "incorrect_format": "Format incorrect, seuls les fichiers CSV et TSV sont pris en charge",
@@ -0,0 +1,12 @@
1
+ import type { StoryObj } from "@storybook/svelte";
2
+ import I18nMultiLanguageTestComponent from "./I18nMultiLanguageTestComponent.svelte";
3
+ declare const meta: {
4
+ title: string;
5
+ component: typeof I18nMultiLanguageTestComponent;
6
+ parameters: {
7
+ layout: string;
8
+ };
9
+ };
10
+ export default meta;
11
+ type Story = StoryObj<typeof meta>;
12
+ export declare const Default: Story;
@@ -0,0 +1,12 @@
1
+ import I18nMultiLanguageTestComponent from "./I18nMultiLanguageTestComponent.svelte";
2
+ const meta = {
3
+ title: "Core/I18n Multi-Language Test",
4
+ component: I18nMultiLanguageTestComponent,
5
+ parameters: {
6
+ layout: "centered"
7
+ }
8
+ };
9
+ export default meta;
10
+ export const Default = {
11
+ args: {}
12
+ };
@@ -0,0 +1,107 @@
1
+ <script>import { formatter } from "../gradio_helper";
2
+ import I18nTestSetup from "./I18nTestSetup.svelte";
3
+ const testStrings = ["share", "submit", "clear", "flag", "error"];
4
+ </script>
5
+
6
+ <I18nTestSetup />
7
+
8
+ <div class="i18n-multi-language-container">
9
+ <h2>I18n Formatter Test</h2>
10
+
11
+ <div class="translation-table">
12
+ <div class="table-header">
13
+ <div class="col">String Key</div>
14
+ <div class="col">Common Key</div>
15
+ <div class="col">Formatted Output</div>
16
+ </div>
17
+
18
+ {#each testStrings as str}
19
+ <div class="table-row">
20
+ <div class="col key">{str}</div>
21
+ <div class="col key">common.{str}</div>
22
+ <div class="col result">{formatter(str)}</div>
23
+ </div>
24
+ {/each}
25
+ </div>
26
+
27
+ <div class="instructions">
28
+ <h3>Testing</h3>
29
+ Use the&nbsp;<strong>Locale</strong> selector in the toolbar to change languages
30
+ and refresh
31
+ </div>
32
+ </div>
33
+
34
+ <style>
35
+ .i18n-multi-language-container {
36
+ padding: 2rem;
37
+ border-radius: 0.5rem;
38
+ border: 1px solid #e2e8f0;
39
+ margin: 1rem;
40
+ max-width: 700px;
41
+ font-family:
42
+ system-ui,
43
+ -apple-system,
44
+ sans-serif;
45
+ }
46
+
47
+ h2 {
48
+ margin-top: 0;
49
+ margin-bottom: 1rem;
50
+ font-size: 1.5rem;
51
+ font-weight: 600;
52
+ }
53
+
54
+ h3 {
55
+ font-size: 1.1rem;
56
+ font-weight: 600;
57
+ margin-bottom: 0.75rem;
58
+ }
59
+
60
+ .translation-table {
61
+ border: 1px solid #e2e8f0;
62
+ border-radius: 0.5rem;
63
+ overflow: hidden;
64
+ margin-bottom: 1.5rem;
65
+ }
66
+
67
+ .table-header {
68
+ display: flex;
69
+ background-color: #f8fafc;
70
+ font-weight: 600;
71
+ border-bottom: 1px solid #e2e8f0;
72
+ }
73
+
74
+ .table-row {
75
+ display: flex;
76
+ border-bottom: 1px solid #e2e8f0;
77
+ }
78
+
79
+ .table-row:last-child {
80
+ border-bottom: none;
81
+ }
82
+
83
+ .col {
84
+ padding: 0.75rem 1rem;
85
+ flex: 1;
86
+ }
87
+
88
+ .col:not(:last-child) {
89
+ border-right: 1px solid #e2e8f0;
90
+ }
91
+
92
+ .key {
93
+ font-family: monospace;
94
+ background-color: #f8fafc;
95
+ }
96
+
97
+ .result {
98
+ font-weight: 500;
99
+ }
100
+
101
+ .instructions {
102
+ padding: 1rem;
103
+ background-color: #f0f9ff;
104
+ border-radius: 0.5rem;
105
+ font-size: 0.9rem;
106
+ }
107
+ </style>
@@ -0,0 +1,16 @@
1
+ import { SvelteComponent } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ [x: string]: never;
5
+ };
6
+ events: {
7
+ [evt: string]: CustomEvent<any>;
8
+ };
9
+ slots: {};
10
+ };
11
+ export type I18nMultiLanguageTestComponentProps = typeof __propDef.props;
12
+ export type I18nMultiLanguageTestComponentEvents = typeof __propDef.events;
13
+ export type I18nMultiLanguageTestComponentSlots = typeof __propDef.slots;
14
+ export default class I18nMultiLanguageTestComponent extends SvelteComponent<I18nMultiLanguageTestComponentProps, I18nMultiLanguageTestComponentEvents, I18nMultiLanguageTestComponentSlots> {
15
+ }
16
+ export {};
@@ -0,0 +1,29 @@
1
+ <script>import { onMount } from "svelte";
2
+ import { init, _ } from "svelte-i18n";
3
+ export let active = true;
4
+ onMount(async () => {
5
+ if (!active)
6
+ return;
7
+ await init({
8
+ fallbackLocale: "en",
9
+ initialLocale: "en"
10
+ });
11
+ });
12
+ </script>
13
+
14
+ {#if active}
15
+ <div class="i18n-test-setup">
16
+ <p>i18n test setup active - translations loaded for: en, fr, de, es, ar</p>
17
+ </div>
18
+ {/if}
19
+
20
+ <style>
21
+ .i18n-test-setup {
22
+ font-size: 0.75rem;
23
+ color: #94a3b8;
24
+ margin-bottom: 0.5rem;
25
+ padding: 0.5rem;
26
+ background-color: #f1f5f9;
27
+ border-radius: 0.25rem;
28
+ }
29
+ </style>
@@ -0,0 +1,16 @@
1
+ import { SvelteComponent } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ active?: boolean | undefined;
5
+ };
6
+ events: {
7
+ [evt: string]: CustomEvent<any>;
8
+ };
9
+ slots: {};
10
+ };
11
+ export type I18nTestSetupProps = typeof __propDef.props;
12
+ export type I18nTestSetupEvents = typeof __propDef.events;
13
+ export type I18nTestSetupSlots = typeof __propDef.slots;
14
+ export default class I18nTestSetup extends SvelteComponent<I18nTestSetupProps, I18nTestSetupEvents, I18nTestSetupSlots> {
15
+ }
16
+ export {};
package/package.json CHANGED
@@ -1,67 +1,68 @@
1
1
  {
2
2
  "name": "@gradio/core",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "type": "module",
5
5
  "devDependencies": {
6
- "@gradio/annotatedimage": "^0.9.15",
7
- "@gradio/atoms": "^0.15.1",
8
- "@gradio/accordion": "^0.5.12",
9
- "@gradio/box": "^0.2.16",
10
- "@gradio/button": "^0.4.14",
11
- "@gradio/audio": "^0.17.9",
12
- "@gradio/chatbot": "^0.26.0",
13
- "@gradio/checkbox": "^0.4.18",
14
- "@gradio/checkboxgroup": "^0.6.18",
15
- "@gradio/client": "^1.14.1",
16
- "@gradio/code": "^0.14.0",
17
- "@gradio/colorpicker": "^0.4.18",
6
+ "@gradio/accordion": "^0.5.14",
7
+ "@gradio/atoms": "^0.16.0",
8
+ "@gradio/annotatedimage": "^0.9.17",
9
+ "@gradio/audio": "^0.17.11",
10
+ "@gradio/box": "^0.2.18",
11
+ "@gradio/button": "^0.4.16",
12
+ "@gradio/chatbot": "^0.26.2",
13
+ "@gradio/client": "^1.14.2",
14
+ "@gradio/checkbox": "^0.4.20",
15
+ "@gradio/checkboxgroup": "^0.6.20",
16
+ "@gradio/colorpicker": "^0.4.20",
17
+ "@gradio/code": "^0.14.2",
18
18
  "@gradio/column": "^0.2.0",
19
- "@gradio/dataframe": "^0.17.6",
20
- "@gradio/dataset": "^0.4.14",
21
- "@gradio/datetime": "^0.3.10",
22
- "@gradio/dropdown": "^0.9.18",
23
- "@gradio/downloadbutton": "^0.3.14",
24
- "@gradio/fallback": "^0.4.18",
25
- "@gradio/fileexplorer": "^0.5.25",
26
- "@gradio/form": "^0.2.16",
27
- "@gradio/file": "^0.12.14",
19
+ "@gradio/dataframe": "^0.17.9",
20
+ "@gradio/datetime": "^0.3.12",
21
+ "@gradio/dataset": "^0.4.16",
22
+ "@gradio/downloadbutton": "^0.3.16",
23
+ "@gradio/dropdown": "^0.9.20",
24
+ "@gradio/file": "^0.12.16",
25
+ "@gradio/fileexplorer": "^0.5.27",
26
+ "@gradio/form": "^0.2.18",
27
+ "@gradio/fallback": "^0.4.20",
28
+ "@gradio/gallery": "^0.15.16",
28
29
  "@gradio/group": "^0.2.0",
29
- "@gradio/highlightedtext": "^0.9.1",
30
- "@gradio/html": "^0.6.9",
30
+ "@gradio/highlightedtext": "^0.9.3",
31
+ "@gradio/html": "^0.6.11",
31
32
  "@gradio/icons": "^0.12.0",
32
- "@gradio/image": "^0.22.1",
33
- "@gradio/gallery": "^0.15.14",
34
- "@gradio/imageeditor": "^0.14.0",
35
- "@gradio/json": "^0.5.18",
36
- "@gradio/label": "^0.5.10",
37
- "@gradio/browserstate": "^0.3.1",
38
- "@gradio/markdown": "^0.13.8",
39
- "@gradio/model3d": "^0.14.9",
40
- "@gradio/multimodaltextbox": "^0.10.1",
41
- "@gradio/nativeplot": "^0.5.12",
42
- "@gradio/number": "^0.5.18",
43
- "@gradio/paramviewer": "^0.7.6",
44
- "@gradio/plot": "^0.9.13",
33
+ "@gradio/image": "^0.22.3",
34
+ "@gradio/imageeditor": "^0.14.3",
35
+ "@gradio/json": "^0.5.20",
36
+ "@gradio/label": "^0.5.12",
37
+ "@gradio/imageslider": "^0.2.0",
38
+ "@gradio/browserstate": "^0.3.2",
39
+ "@gradio/model3d": "^0.14.11",
40
+ "@gradio/markdown": "^0.13.10",
41
+ "@gradio/multimodaltextbox": "^0.10.3",
42
+ "@gradio/nativeplot": "^0.5.14",
43
+ "@gradio/number": "^0.5.20",
44
+ "@gradio/paramviewer": "^0.7.8",
45
+ "@gradio/radio": "^0.7.3",
46
+ "@gradio/plot": "^0.9.15",
45
47
  "@gradio/row": "^0.2.1",
46
- "@gradio/radio": "^0.7.1",
47
- "@gradio/sidebar": "^0.1.10",
48
- "@gradio/simpledropdown": "^0.3.18",
49
- "@gradio/simpleimage": "^0.8.25",
50
- "@gradio/simpletextbox": "^0.3.18",
48
+ "@gradio/simpledropdown": "^0.3.20",
49
+ "@gradio/sidebar": "^0.1.12",
50
+ "@gradio/simpleimage": "^0.8.27",
51
+ "@gradio/sketchbox": "^0.6.7",
52
+ "@gradio/simpletextbox": "^0.3.20",
53
+ "@gradio/slider": "^0.6.8",
54
+ "@gradio/statustracker": "^0.10.10",
51
55
  "@gradio/state": "^0.1.2",
52
- "@gradio/sketchbox": "^0.6.5",
53
- "@gradio/slider": "^0.6.6",
54
- "@gradio/statustracker": "^0.10.8",
55
- "@gradio/tabitem": "^0.4.2",
56
- "@gradio/tabs": "^0.4.2",
56
+ "@gradio/tabitem": "^0.4.3",
57
+ "@gradio/tabs": "^0.4.3",
58
+ "@gradio/textbox": "^0.10.10",
57
59
  "@gradio/theme": "^0.4.0",
58
- "@gradio/timer": "^0.4.4",
59
- "@gradio/textbox": "^0.10.8",
60
- "@gradio/upload": "^0.16.1",
61
- "@gradio/uploadbutton": "^0.8.14",
60
+ "@gradio/timer": "^0.4.5",
61
+ "@gradio/upload": "^0.16.3",
62
+ "@gradio/video": "^0.14.11",
62
63
  "@gradio/wasm": "^0.18.1",
63
- "@gradio/video": "^0.14.9",
64
- "@gradio/utils": "^0.10.1"
64
+ "@gradio/uploadbutton": "^0.8.16",
65
+ "@gradio/utils": "^0.10.2"
65
66
  },
66
67
  "msw": {
67
68
  "workerDirectory": "public"
package/src/Blocks.svelte CHANGED
@@ -676,6 +676,8 @@
676
676
  messages = [new_message("Error", data, -1, event), ...messages];
677
677
  } else if (event === "warning") {
678
678
  messages = [new_message("Warning", data, -1, event), ...messages];
679
+ } else if (event === "info") {
680
+ messages = [new_message("Info", data, -1, event), ...messages];
679
681
  } else if (event == "clear_status") {
680
682
  update_status(id, "complete", data);
681
683
  } else if (event == "close_stream") {
@@ -1,7 +1,55 @@
1
- import { format } from "svelte-i18n";
1
+ import { format, _ } from "svelte-i18n";
2
2
  import { get } from "svelte/store";
3
+ import { all_common_keys } from "./i18n";
4
+
3
5
  export { Gradio } from "@gradio/utils";
6
+ export type I18nFormatter = typeof formatter;
4
7
 
5
- export const formatter = get(format);
8
+ /**
9
+ * i18n formatter with fallback to svelte-i18n's format function.
10
+ *
11
+ * @param value - The string to translate or format
12
+ * @returns The translated string
13
+ *
14
+ * This formatter attempts translation in the following order:
15
+ * 1. Direct translation of the input string
16
+ * 2. Checks if input matches any common key names
17
+ * 3. Falls back to svelte-i18n's format function
18
+ */
19
+ export function formatter(value: string | null | undefined): string {
20
+ if (value == null) {
21
+ return "";
22
+ }
23
+ const string_value = String(value);
24
+ const translate = get(_);
25
+ const initial_formatter = get(format);
6
26
 
7
- export type I18nFormatter = typeof formatter;
27
+ let direct_translation = translate(string_value);
28
+
29
+ if (direct_translation !== string_value) {
30
+ return direct_translation;
31
+ }
32
+
33
+ const lower_value = string_value.toLowerCase();
34
+
35
+ for (const common_key of all_common_keys) {
36
+ const key_name = common_key.substring(common_key.indexOf(".") + 1);
37
+
38
+ if (lower_value === key_name) {
39
+ const translation = translate(common_key);
40
+
41
+ if (translation !== common_key) {
42
+ return translation;
43
+ }
44
+ break;
45
+ }
46
+ }
47
+
48
+ // fall back to the svelte-i18n formatter to maintain compatibility
49
+ const formatted = initial_formatter(string_value);
50
+ if (formatted !== string_value) {
51
+ return formatted;
52
+ }
53
+
54
+ return string_value;
55
+ }
package/src/i18n.ts CHANGED
@@ -1,4 +1,10 @@
1
- import { addMessages, init, getLocaleFromNavigator, locale } from "svelte-i18n";
1
+ import {
2
+ addMessages,
3
+ init,
4
+ getLocaleFromNavigator,
5
+ locale,
6
+ _
7
+ } from "svelte-i18n";
2
8
 
3
9
  const langs = import.meta.glob("./lang/*.json", {
4
10
  eager: true
@@ -29,6 +35,8 @@ export const language_choices: [string, string][] = Object.entries(
29
35
  processed_langs
30
36
  ).map(([code, data]) => [data._name || code, code]);
31
37
 
38
+ export let all_common_keys: Set<string> = new Set();
39
+
32
40
  for (const lang in processed_langs) {
33
41
  addMessages(lang, processed_langs[lang]);
34
42
  }
@@ -41,9 +49,11 @@ export async function setupi18n(): Promise<void> {
41
49
  }
42
50
 
43
51
  const browser_locale = getLocaleFromNavigator();
52
+ const normalized_locale = browser_locale?.split("-")[0];
53
+
44
54
  const initial_locale =
45
- browser_locale && available_locales.includes(browser_locale)
46
- ? browser_locale
55
+ normalized_locale && available_locales.includes(normalized_locale)
56
+ ? normalized_locale
47
57
  : "en";
48
58
 
49
59
  await init({
@@ -51,6 +61,20 @@ export async function setupi18n(): Promise<void> {
51
61
  initialLocale: initial_locale
52
62
  });
53
63
 
64
+ for (const lang_code in processed_langs) {
65
+ if (
66
+ processed_langs[lang_code] &&
67
+ typeof processed_langs[lang_code] === "object" &&
68
+ processed_langs[lang_code].common &&
69
+ typeof processed_langs[lang_code].common === "object"
70
+ ) {
71
+ const common_ns = processed_langs[lang_code].common;
72
+ for (const key in common_ns) {
73
+ all_common_keys.add(`common.${key}`);
74
+ }
75
+ }
76
+ }
77
+
54
78
  i18n_initialized = true;
55
79
  }
56
80
 
package/src/lang/en.json CHANGED
@@ -68,7 +68,8 @@
68
68
  "no_devices": "No devices found",
69
69
  "language": "Language",
70
70
  "display_theme": "Display Theme",
71
- "pwa": "Progressive Web App"
71
+ "pwa": "Progressive Web App",
72
+ "run": "Run"
72
73
  },
73
74
  "dataframe": {
74
75
  "incorrect_format": "Incorrect format, only CSV and TSV files are supported",
package/src/lang/es.json CHANGED
@@ -46,6 +46,7 @@
46
46
  "edit": "Editar",
47
47
  "empty": "Vacío",
48
48
  "error": "Error",
49
+ "flag": "Marcar",
49
50
  "hosted_on": "Alojado en",
50
51
  "loading": "Cargando",
51
52
  "logo": "logo",
package/src/lang/fr.json CHANGED
@@ -45,6 +45,7 @@
45
45
  "download": "Télécharger",
46
46
  "edit": "Modifier",
47
47
  "empty": "Vide",
48
+ "flag": "Marquer",
48
49
  "error": "Erreur",
49
50
  "hosted_on": "Hébergé sur",
50
51
  "loading": "Chargement",
@@ -58,7 +59,8 @@
58
59
  "no_devices": "Aucun périphérique trouvé",
59
60
  "language": "Langue",
60
61
  "display_theme": "Thème d'affichage",
61
- "pwa": "Application web progressive"
62
+ "pwa": "Application web progressive",
63
+ "run": "Exécuter"
62
64
  },
63
65
  "dataframe": {
64
66
  "incorrect_format": "Format incorrect, seuls les fichiers CSV et TSV sont pris en charge",
@@ -0,0 +1,17 @@
1
+ import type { Meta, StoryObj } from "@storybook/svelte";
2
+ import I18nMultiLanguageTestComponent from "./I18nMultiLanguageTestComponent.svelte";
3
+
4
+ const meta = {
5
+ title: "Core/I18n Multi-Language Test",
6
+ component: I18nMultiLanguageTestComponent,
7
+ parameters: {
8
+ layout: "centered"
9
+ }
10
+ } satisfies Meta<I18nMultiLanguageTestComponent>;
11
+
12
+ export default meta;
13
+ type Story = StoryObj<typeof meta>;
14
+
15
+ export const Default: Story = {
16
+ args: {}
17
+ };
@@ -0,0 +1,109 @@
1
+ <script lang="ts">
2
+ import { formatter } from "../gradio_helper";
3
+ import I18nTestSetup from "./I18nTestSetup.svelte";
4
+
5
+ const testStrings = ["share", "submit", "clear", "flag", "error"];
6
+ </script>
7
+
8
+ <I18nTestSetup />
9
+
10
+ <div class="i18n-multi-language-container">
11
+ <h2>I18n Formatter Test</h2>
12
+
13
+ <div class="translation-table">
14
+ <div class="table-header">
15
+ <div class="col">String Key</div>
16
+ <div class="col">Common Key</div>
17
+ <div class="col">Formatted Output</div>
18
+ </div>
19
+
20
+ {#each testStrings as str}
21
+ <div class="table-row">
22
+ <div class="col key">{str}</div>
23
+ <div class="col key">common.{str}</div>
24
+ <div class="col result">{formatter(str)}</div>
25
+ </div>
26
+ {/each}
27
+ </div>
28
+
29
+ <div class="instructions">
30
+ <h3>Testing</h3>
31
+ Use the&nbsp;<strong>Locale</strong> selector in the toolbar to change languages
32
+ and refresh
33
+ </div>
34
+ </div>
35
+
36
+ <style>
37
+ .i18n-multi-language-container {
38
+ padding: 2rem;
39
+ border-radius: 0.5rem;
40
+ border: 1px solid #e2e8f0;
41
+ margin: 1rem;
42
+ max-width: 700px;
43
+ font-family:
44
+ system-ui,
45
+ -apple-system,
46
+ sans-serif;
47
+ }
48
+
49
+ h2 {
50
+ margin-top: 0;
51
+ margin-bottom: 1rem;
52
+ font-size: 1.5rem;
53
+ font-weight: 600;
54
+ }
55
+
56
+ h3 {
57
+ font-size: 1.1rem;
58
+ font-weight: 600;
59
+ margin-bottom: 0.75rem;
60
+ }
61
+
62
+ .translation-table {
63
+ border: 1px solid #e2e8f0;
64
+ border-radius: 0.5rem;
65
+ overflow: hidden;
66
+ margin-bottom: 1.5rem;
67
+ }
68
+
69
+ .table-header {
70
+ display: flex;
71
+ background-color: #f8fafc;
72
+ font-weight: 600;
73
+ border-bottom: 1px solid #e2e8f0;
74
+ }
75
+
76
+ .table-row {
77
+ display: flex;
78
+ border-bottom: 1px solid #e2e8f0;
79
+ }
80
+
81
+ .table-row:last-child {
82
+ border-bottom: none;
83
+ }
84
+
85
+ .col {
86
+ padding: 0.75rem 1rem;
87
+ flex: 1;
88
+ }
89
+
90
+ .col:not(:last-child) {
91
+ border-right: 1px solid #e2e8f0;
92
+ }
93
+
94
+ .key {
95
+ font-family: monospace;
96
+ background-color: #f8fafc;
97
+ }
98
+
99
+ .result {
100
+ font-weight: 500;
101
+ }
102
+
103
+ .instructions {
104
+ padding: 1rem;
105
+ background-color: #f0f9ff;
106
+ border-radius: 0.5rem;
107
+ font-size: 0.9rem;
108
+ }
109
+ </style>
@@ -0,0 +1,32 @@
1
+ <script lang="ts">
2
+ import { onMount } from "svelte";
3
+ import { init, _ } from "svelte-i18n";
4
+
5
+ export let active = true;
6
+
7
+ onMount(async () => {
8
+ if (!active) return;
9
+
10
+ await init({
11
+ fallbackLocale: "en",
12
+ initialLocale: "en"
13
+ });
14
+ });
15
+ </script>
16
+
17
+ {#if active}
18
+ <div class="i18n-test-setup">
19
+ <p>i18n test setup active - translations loaded for: en, fr, de, es, ar</p>
20
+ </div>
21
+ {/if}
22
+
23
+ <style>
24
+ .i18n-test-setup {
25
+ font-size: 0.75rem;
26
+ color: #94a3b8;
27
+ margin-bottom: 0.5rem;
28
+ padding: 0.5rem;
29
+ background-color: #f1f5f9;
30
+ border-radius: 0.25rem;
31
+ }
32
+ </style>