Repository: huggingface/huggingface_hub

Issue:
Arbled characters when using snapshot_download and hf_hub_download to download files with cn/jp/kr names
### Describe the bug

When use `snapshot_download` or `hf_hub_download` to download files with chinese (both simpifiled and traditional), japanese or korean names, the tqdm will show arbled characters.

Run the following code

```python
from huggingface_hub import HfApi

hf_client = HfApi()
hf_client.snapshot_download(
    repo_id='narugo/test_rp',
    repo_type='dataset',
    local_dir='test_langs',
    local_dir_use_symlinks=False,
)

print('这是简体中文，输出是正常的')
print('This is simplified chinese, nothing wrong with the environment')
```

The result is

![image](https://github.com/huggingface/huggingface_hub/assets/117186571/7caaba21-8775-482a-ab2b-36e55f8e9718)

The chinese text in `print` statement will be printed normally, while text in `snapshot_download` will not.

The multi-lang repository for test we are using: https://huggingface.co/datasets/narugo/test_rp


### Reproduction

from huggingface_hub import HfApi

hf_client = HfApi()
hf_client.snapshot_download(
    repo_id='narugo/test_rp',
    repo_type='dataset',
    local_dir='test_langs',
    local_dir_use_symlinks=False,
)


print('这是简体中文，输出是正常的')
print('This is simplified chinese, nothing wrong with the environment')


### Logs

_No response_

### System info

```shell
Copy-and-paste the text below in your GitHub issue.

- huggingface_hub version: 0.20.1
- Platform: Linux-5.10.198-187.748.amzn2.x86_64-x86_64-with-glibc2.2.5
- Python version: 3.8.1
- Running in iPython ?: No
- Running in notebook ?: No
- Running in Google Colab ?: No
- Token path ?: /home/user/.cache/huggingface/token
- Has saved token ?: True
- Who am I ?: narugo
- Configured git credential helpers: 
- FastAI: N/A
- Tensorflow: N/A
- Torch: 2.1.0
- Jinja2: 3.1.2
- Graphviz: N/A
- Pydot: N/A
- Pillow: 9.5.0
- hf_transfer: N/A
- gradio: N/A
- tensorboard: N/A
- numpy: 1.24.4
- pydantic: N/A
- aiohttp: 3.8.6
- ENDPOINT: https://huggingface.co
- HF_HUB_CACHE: /home/user/.cache/huggingface/hub
- HF_ASSETS_CACHE: /home/user/.cache/huggingface/assets
- HF_TOKEN_PATH: /home/user/.cache/huggingface/token
- HF_HUB_OFFLINE: False
- HF_HUB_DISABLE_TELEMETRY: False
- HF_HUB_DISABLE_PROGRESS_BARS: None
- HF_HUB_DISABLE_SYMLINKS_WARNING: False
- HF_HUB_DISABLE_EXPERIMENTAL_WARNING: False
- HF_HUB_DISABLE_IMPLICIT_TOKEN: False
- HF_HUB_ENABLE_HF_TRANSFER: False
- HF_HUB_ETAG_TIMEOUT: 10
- HF_HUB_DOWNLOAD_TIMEOUT: 10
```



Discussion:
Hi @narugo1992, thanks for reporting. I've investigated a bit but not sure we can do much about it. The problem comes from the header returned by cloudfront in which the filename is encoded. Here is a standalone script to reproduce it:

```py
import requests
from huggingface_hub import hf_hub_url

url = hf_hub_url(repo_id="narugo/test_rp", filename="日本語/でこんにちは.jpg", repo_type="dataset")
response = requests.head(url=url, allow_redirects=True) # 1 redirect to cloudfront CDN
print(response.headers.get("content-disposition"))
```

which outputs
```
inline; filename*=UTF-8''%E3%81%A7%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF.jpg; filename="ã§ããã«ã¡ã¯.jpg";
```

Filename is "badly" decoded by `requests` and since we're using this value in the tqdm line, I don't think we can do much :confused: 
FIY, for the url of huggingface site resources, maybe extracting the file name from the url and using it for the desc parameter of tqdm is a solution. 

e.g. https://huggingface.co/datasets/narugo/test_rp/resolve/main/%E6%97%A5%E6%9C%AC%E8%AA%9E/%E3%81%A7%E3%81%93 %E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF.jpg, just extract the file name through urlsplit, and obtain the decoded file name through unquote.

```python
from urllib.parse import urlsplit, unquote

from huggingface_hub import hf_hub_url

url = hf_hub_url(repo_id="narugo/test_rp", filename="日本語/でこんにちは.jpg", repo_type="dataset")
print(f'URL is {url}')

filename = unquote(urlsplit(url).path.split('/')[-1])
print(f'Filename is {filename}')

```

update: maybe we can change these lines: https://github.com/huggingface/huggingface_hub/blob/a331e82aad1bc63038194611236db28fa013814c/src/huggingface_hub/file_download.py#L461 , use filename from URL as `displayed_name`.

Resolving patch:
diff --git a/src/huggingface_hub/file_download.py b/src/huggingface_hub/file_download.py
--- a/src/huggingface_hub/file_download.py
+++ b/src/huggingface_hub/file_download.py
@@ -414,18 +414,38 @@ def http_get(
     url: str,
     temp_file: BinaryIO,
     *,
-    proxies=None,
+    proxies: Optional[Dict] = None,
     resume_size: float = 0,
     headers: Optional[Dict[str, str]] = None,
     expected_size: Optional[int] = None,
+    displayed_filename: Optional[str] = None,
     _nb_retries: int = 5,
-):
+) -> None:
     """
     Download a remote file. Do not gobble up errors, and will return errors tailored to the Hugging Face Hub.
 
     If ConnectionError (SSLError) or ReadTimeout happen while streaming data from the server, it is most likely a
     transient error (network outage?). We log a warning message and try to resume the download a few times before
     giving up. The method gives up after 5 attempts if no new data has being received from the server.
+
+    Args:
+        url (`str`):
+            The URL of the file to download.
+        temp_file (`BinaryIO`):
+            The file-like object where to save the file.
+        proxies (`dict`, *optional*):
+            Dictionary mapping protocol to the URL of the proxy passed to `requests.request`.
+        resume_size (`float`, *optional*):
+            The number of bytes already downloaded. If set to 0 (default), the whole file is download. If set to a
+            positive number, the download will resume at the given position.
+        headers (`dict`, *optional*):
+            Dictionary of HTTP Headers to send with the request.
+        expected_size (`int`, *optional*):
+            The expected size of the file to download. If set, the download will raise an error if the size of the
+            received content is different from the expected one.
+        displayed_filename (`str`, *optional*):
+            The filename of the file that is being downloaded. Value is used only to display a nice progress bar. If
+            not set, the filename is guessed from the URL or the `Content-Disposition` header.
     """
     hf_transfer = None
     if HF_HUB_ENABLE_HF_TRANSFER:
@@ -458,21 +478,22 @@ def http_get(
     #       If the file is compressed, the number of bytes in the saved file will be higher than 'total'.
     total = resume_size + int(content_length) if content_length is not None else None
 
-    displayed_name = url
-    content_disposition = r.headers.get("Content-Disposition")
-    if content_disposition is not None:
-        match = HEADER_FILENAME_PATTERN.search(content_disposition)
-        if match is not None:
-            # Means file is on CDN
-            displayed_name = match.groupdict()["filename"]
+    if displayed_filename is None:
+        displayed_filename = url
+        content_disposition = r.headers.get("Content-Disposition")
+        if content_disposition is not None:
+            match = HEADER_FILENAME_PATTERN.search(content_disposition)
+            if match is not None:
+                # Means file is on CDN
+                displayed_filename = match.groupdict()["filename"]
 
     # Truncate filename if too long to display
-    if len(displayed_name) > 40:
-        displayed_name = f"(…){displayed_name[-40:]}"
+    if len(displayed_filename) > 40:
+        displayed_filename = f"(…){displayed_filename[-40:]}"
 
     consistency_error_message = (
         f"Consistency check failed: file should be of size {expected_size} but has size"
-        f" {{actual_size}} ({displayed_name}).\nWe are sorry for the inconvenience. Please retry download and"
+        f" {{actual_size}} ({displayed_filename}).\nWe are sorry for the inconvenience. Please retry download and"
         " pass `force_download=True, resume_download=False` as argument.\nIf the issue persists, please let us"
         " know by opening an issue on https://github.com/huggingface/huggingface_hub."
     )
@@ -483,7 +504,7 @@ def http_get(
         unit_scale=True,
         total=total,
         initial=resume_size,
-        desc=displayed_name,
+        desc=displayed_filename,
         disable=bool(logger.getEffectiveLevel() == logging.NOTSET),
     ) as progress:
         if hf_transfer and total is not None and total > 5 * DOWNLOAD_CHUNK_SIZE:
@@ -1461,6 +1482,7 @@ def _resumable_file_manager() -> Generator[io.BufferedWriter, None, None]:
                 resume_size=resume_size,
                 headers=headers,
                 expected_size=expected_size,
+                displayed_filename=filename,
             )
 
         if local_dir is None:


Test patch: