gradio-app / gradio

Build and share delightful machine learning apps, all in Python. 🌟 Star to support our work!
http://www.gradio.app
Apache License 2.0
30.74k stars 2.29k forks source link

Allow users to define their own i18n (e.g. for button value text) #2465

Open zhengxiaoyao0716 opened 1 year ago

zhengxiaoyao0716 commented 1 year ago

Describe the bug

See Button.svelte#L14, the value of the Button components has always try to translated by svelte-i18n, but I have not found the way to custom the locale dictionaries, that caused all the button value except defined under the /ui/packages/app/src/lang would make a browser warning gave by svelte-i18n.

Is there an existing issue for this?

Reproduction

https://github.com/AUTOMATIC1111/stable-diffusion-webui

Screenshot

image

Logs

browser warning, without python console log.

System Info

gradio==3.4.1

Severity

blocking all usage of gradio

pngwn commented 1 year ago

Is the issue here that you wish to get rid of the console logs or that you wish to set your own language definitions?

zhengxiaoyao0716 commented 1 year ago

Set custom language definitions feels better, but it means maybe we should make all components used sevlte-i18n to show the custom values, not only the Button component.

abidlabs commented 1 year ago

Renamed and categorized as an enhancement

abidlabs commented 1 year ago

Hi @zhengxiaoyao0716 it is now possible to read a user's browser locale and change your function / UI based on that. The way you do that is by passing in a gr.Request object into your function which contains the request headers. It's probably easiest to explain by an example, so let me provide two:

Example 1: changing a function's output based on user language

import gradio as gr

def greet(name, request: gr.Request):
    if request.headers["Accept-Language"].split(",")[0].lower().startswith("en"):
        return "Hello, " + name
    elif request.headers["Accept-Language"].split(",")[0].lower().startswith("es"):
        return "Hola, " + name
    else:
        return "Language not recognized"

gr.Interface(greet, "text", "text").launch()

Which produces (if you have set Spanish in your browser):

image

You can also change button values, like you asked:

Example 2: changing a button value based on user language

import gradio as gr

def greet(request: gr.Request):
    if request.headers["Accept-Language"].split(",")[0].lower().startswith("en"):
        return "Hello"
    elif request.headers["Accept-Language"].split(",")[0].lower().startswith("es"):
        return "Hola"
    else:
        return "Language not recognized"

with gr.Blocks() as demo:
    btn = gr.Button()
    demo.load(greet, None, btn)

demo.launch()

Which produces (if you have set Spanish in your browser):

image

Thanks for raising this issue!

freddyaboulton commented 1 year ago

@abidlabs Very cool workaround. I think we should reopen this issue based on the recent discussions regarding localizing text between Chinese and English for https://huggingface.co/spaces/JohnSmith9982/ChuanhuChatGPT ?

abidlabs commented 1 year ago

Sounds good @freddyaboulton! Basically, we want to provide a simpler API (and one that ideally does not need to make requests every time to the server) for this, right?

pngwn commented 1 year ago

Fundamentally i think the solution should be something like, any 'value' can accept a string or an instance of gr.I18n()/ gr.Translate() which can provide a series of values to be chosen by the i18n tools we use on the frontend.

Something like:

gr.Translate(en = 'Hello', es = 'Hola')

I've lost the dictionary battle before, however in this instance I think it actually should take a dictionary because most i18n standards are built around JSON and it isn't unreasonable for user to generate their translation string elsewhere (maybe even with a different tool/ gui tool) export them and import them into a gradio app.

A user needs to be able to do this:

import translation # some dict that used to be a json file or something

gr.Translate(translation)

gr.Translation could be passed in to Blocks and provide additional translations or we could support translations directly on the component values themselves. We could also support both, with component specific values taking priority where there are collisions.

In order to support this we need to wrap all string with i18n helpers on the frontend. Because the top level version would actually allow a user to provide translations for something that we may not have full translations for but that are not a component value.

abidlabs commented 1 year ago

I agree that a dict structure makes sense here @pngwn! We don't want to hardcode a parameter for every language, so something like:

gr.Translation({"en": "hello", "es": "hola"})

makes sense to me.

Some additional thoughts:

Implementation-wise,

gr.Translation({"en": "hello", "es": "hola"}) could return a plain dictionary with a special key that the frontend internationalizes. This way, we could make it explicitly listed in the config so that no additional server requests are needed to fetch the internationalized values.

freddyaboulton commented 1 year ago

I was thinking this would only apply to labels, info text, button text, etc and not the arbitrary values of the components.

My thought process was that the LLMs and other models that app authors interface with support multiple languages already. So it's not that the input/output of the model needs to be translated, but rather the text displayed in the app to guide a user on how to use the app.

Example from ChuanhuChatGPT supporting english but the app text is in chinese:

image
Sakura-Luna commented 1 year ago

How can I disable i18n for projects that don't use i18n for localization?

freddyaboulton commented 1 year ago

I'm not sure that's possible right now @Sakura-Luna. Tagging @pngwn @dawoodkhan82 and @aliabid94 to confirm.

Sakura-Luna commented 1 year ago

I hope at least this frequent error can be resolved. image

qq-me commented 2 months ago

Hi @zhengxiaoyao0716 it is now possible to read a user's browser locale and change your function / UI based on that. The way you do that is by passing in a gr.Request object into your function which contains the request headers. It's probably easiest to explain by an example, so let me provide two:

Example 1: changing a function's output based on user language

import gradio as gr

def greet(name, request: gr.Request):
    if request.headers["Accept-Language"].split(",")[0].lower().startswith("en"):
        return "Hello, " + name
    elif request.headers["Accept-Language"].split(",")[0].lower().startswith("es"):
        return "Hola, " + name
    else:
        return "Language not recognized"

gr.Interface(greet, "text", "text").launch()

Which produces (if you have set Spanish in your browser):

image

You can also change button values, like you asked:

Example 2: changing a button value based on user language

import gradio as gr

def greet(request: gr.Request):
    if request.headers["Accept-Language"].split(",")[0].lower().startswith("en"):
        return "Hello"
    elif request.headers["Accept-Language"].split(",")[0].lower().startswith("es"):
        return "Hola"
    else:
        return "Language not recognized"

with gr.Blocks() as demo:
    btn = gr.Button()
    demo.load(greet, None, btn)

demo.launch()

Which produces (if you have set Spanish in your browser):

image

Thanks for raising this issue!

how did you set buttons to spanish? It says Limpiar Enviar, but for me buttons stay on english

freddyaboulton commented 2 months ago

I had to change the system language on my computer. But there may be a way to change the language locale in your browser.

pngwn commented 2 months ago

You can set the preferred language in the browser settings iirc.

branaway commented 1 month ago

how to set the global language setting such that the image component uses the correct built-in localized string? Screen Shot 2024-05-30 at 18 17 44