You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
IoT-For-Beginners/translations/vi/6-consumer/lessons/3-spoken-feedback/wio-terminal-text-to-speech.md

27 KiB

Chuyển đổi văn bản thành giọng nói - Wio Terminal

Trong phần này của bài học, bạn sẽ chuyển đổi văn bản thành giọng nói để cung cấp phản hồi bằng lời nói.

Chuyển đổi văn bản thành giọng nói

SDK dịch vụ giọng nói mà bạn đã sử dụng trong bài học trước để chuyển đổi giọng nói thành văn bản cũng có thể được sử dụng để chuyển đổi văn bản thành giọng nói.

Lấy danh sách các giọng nói

Khi yêu cầu giọng nói, bạn cần cung cấp giọng nói để sử dụng vì giọng nói có thể được tạo ra bằng nhiều giọng khác nhau. Mỗi ngôn ngữ hỗ trợ một loạt các giọng nói khác nhau, và bạn có thể lấy danh sách các giọng nói được hỗ trợ cho mỗi ngôn ngữ từ SDK dịch vụ giọng nói. Tuy nhiên, hạn chế của vi điều khiển xuất hiện ở đây - việc gọi để lấy danh sách các giọng nói được hỗ trợ bởi dịch vụ chuyển đổi văn bản thành giọng nói là một tài liệu JSON có kích thước hơn 77KB, quá lớn để được xử lý bởi Wio Terminal. Tại thời điểm viết, danh sách đầy đủ chứa 215 giọng nói, mỗi giọng được định nghĩa bởi một tài liệu JSON như sau:

{
    "Name": "Microsoft Server Speech Text to Speech Voice (en-US, AriaNeural)",
    "DisplayName": "Aria",
    "LocalName": "Aria",
    "ShortName": "en-US-AriaNeural",
    "Gender": "Female",
    "Locale": "en-US",
    "StyleList": [
        "chat",
        "customerservice",
        "narration-professional",
        "newscast-casual",
        "newscast-formal",
        "cheerful",
        "empathetic"
    ],
    "SampleRateHertz": "24000",
    "VoiceType": "Neural",
    "Status": "GA"
}

JSON này dành cho giọng Aria, có nhiều phong cách giọng nói. Tất cả những gì cần thiết khi chuyển đổi văn bản thành giọng nói là tên ngắn, en-US-AriaNeural.

Thay vì tải xuống và giải mã toàn bộ danh sách này trên vi điều khiển của bạn, bạn sẽ cần viết một số mã serverless để lấy danh sách các giọng nói cho ngôn ngữ bạn đang sử dụng, và gọi điều này từ Wio Terminal của bạn. Mã của bạn sau đó có thể chọn một giọng nói phù hợp từ danh sách, chẳng hạn như giọng đầu tiên mà nó tìm thấy.

Nhiệm vụ - tạo một hàm serverless để lấy danh sách các giọng nói

  1. Mở dự án smart-timer-trigger của bạn trong VS Code, và mở terminal đảm bảo môi trường ảo đã được kích hoạt. Nếu không, hãy tắt và tạo lại terminal.

  2. Mở tệp local.settings.json và thêm các thiết lập cho khóa API dịch vụ giọng nói và vị trí:

    "SPEECH_KEY": "<key>",
    "SPEECH_LOCATION": "<location>"
    

    Thay thế <key> bằng khóa API cho tài nguyên dịch vụ giọng nói của bạn. Thay thế <location> bằng vị trí bạn đã sử dụng khi tạo tài nguyên dịch vụ giọng nói.

  3. Thêm một HTTP trigger mới vào ứng dụng này có tên get-voices bằng lệnh sau từ bên trong terminal của VS Code trong thư mục gốc của dự án ứng dụng functions:

    func new --name get-voices --template "HTTP trigger"
    

    Điều này sẽ tạo một HTTP trigger có tên get-voices.

  4. Thay thế nội dung của tệp __init__.py trong thư mục get-voices bằng nội dung sau:

    import json
    import os
    import requests
    
    import azure.functions as func
    
    def main(req: func.HttpRequest) -> func.HttpResponse:
        location = os.environ['SPEECH_LOCATION']
        speech_key = os.environ['SPEECH_KEY']
    
        req_body = req.get_json()
        language = req_body['language']
    
        url = f'https://{location}.tts.speech.microsoft.com/cognitiveservices/voices/list'
    
        headers = {
            'Ocp-Apim-Subscription-Key': speech_key
        }
    
        response = requests.get(url, headers=headers)
        voices_json = json.loads(response.text)
    
        voices = filter(lambda x: x['Locale'].lower() == language.lower(), voices_json)
        voices = map(lambda x: x['ShortName'], voices)
    
        return func.HttpResponse(json.dumps(list(voices)), status_code=200)
    

    Mã này thực hiện một yêu cầu HTTP đến endpoint để lấy danh sách các giọng nói. Danh sách giọng nói này là một khối JSON lớn với các giọng nói cho tất cả các ngôn ngữ, vì vậy các giọng nói cho ngôn ngữ được truyền trong nội dung yêu cầu sẽ được lọc ra, sau đó tên ngắn được trích xuất và trả về dưới dạng danh sách JSON. Tên ngắn là giá trị cần thiết để chuyển đổi văn bản thành giọng nói, vì vậy chỉ giá trị này được trả về.

    💁 Bạn có thể thay đổi bộ lọc nếu cần để chọn chỉ các giọng nói bạn muốn.

    Điều này giảm kích thước dữ liệu từ 77KB (tại thời điểm viết) xuống một tài liệu JSON nhỏ hơn nhiều. Ví dụ, đối với các giọng nói tiếng Mỹ, kích thước này là 408 byte.

  5. Chạy ứng dụng functions của bạn cục bộ. Sau đó, bạn có thể gọi nó bằng một công cụ như curl theo cách tương tự như bạn đã thử nghiệm HTTP trigger text-to-timer. Đảm bảo truyền ngôn ngữ của bạn dưới dạng nội dung JSON:

    {
        "language":"<language>"
    }
    

    Thay thế <language> bằng ngôn ngữ của bạn, chẳng hạn như en-GB, hoặc zh-CN.

💁 Bạn có thể tìm thấy mã này trong thư mục code-spoken-response/functions.

Nhiệm vụ - lấy giọng nói từ Wio Terminal của bạn

  1. Mở dự án smart-timer của bạn trong VS Code nếu nó chưa được mở.

  2. Mở tệp tiêu đề config.h và thêm URL cho ứng dụng functions của bạn:

    const char *GET_VOICES_FUNCTION_URL = "<URL>";
    

    Thay thế <URL> bằng URL cho HTTP trigger get-voices trên ứng dụng functions của bạn. Điều này sẽ giống với giá trị của TEXT_TO_TIMER_FUNCTION_URL, ngoại trừ tên hàm là get-voices thay vì text-to-timer.

  3. Tạo một tệp mới trong thư mục src có tên text_to_speech.h. Tệp này sẽ được sử dụng để định nghĩa một lớp chuyển đổi từ văn bản thành giọng nói.

  4. Thêm các chỉ thị include sau vào đầu tệp text_to_speech.h mới:

    #pragma once
    
    #include <Arduino.h>
    #include <ArduinoJson.h>
    #include <HTTPClient.h>
    #include <Seeed_FS.h>
    #include <SD/Seeed_SD.h>
    #include <WiFiClient.h>
    #include <WiFiClientSecure.h>
    
    #include "config.h"
    #include "speech_to_text.h"
    
  5. Thêm mã sau bên dưới để khai báo lớp TextToSpeech, cùng với một instance có thể được sử dụng trong phần còn lại của ứng dụng:

    class TextToSpeech
    {
    public:
    private:
    };
    
    TextToSpeech textToSpeech;
    
  6. Để gọi ứng dụng functions của bạn, bạn cần khai báo một WiFi client. Thêm đoạn mã sau vào phần private của lớp:

    WiFiClient _client;
    
  7. Trong phần private, thêm một trường cho giọng nói đã chọn:

    String _voice;
    
  8. Trong phần public, thêm một hàm init để lấy giọng nói đầu tiên:

    void init()
    {
    }
    
  9. Để lấy các giọng nói, một tài liệu JSON cần được gửi đến ứng dụng functions với ngôn ngữ. Thêm đoạn mã sau vào hàm init để tạo tài liệu JSON này:

    DynamicJsonDocument doc(1024);
    doc["language"] = LANGUAGE;
    
    String body;
    serializeJson(doc, body);
    
  10. Tiếp theo, tạo một HTTPClient, sau đó sử dụng nó để gọi ứng dụng functions để lấy các giọng nói, gửi tài liệu JSON:

    HTTPClient httpClient;
    httpClient.begin(_client, GET_VOICES_FUNCTION_URL);
    
    int httpResponseCode = httpClient.POST(body);
    
  11. Bên dưới đoạn mã này, thêm mã để kiểm tra mã phản hồi, và nếu là 200 (thành công), thì trích xuất danh sách các giọng nói, lấy giọng đầu tiên từ danh sách:

    if (httpResponseCode == 200)
    {
        String result = httpClient.getString();
        Serial.println(result);
    
        DynamicJsonDocument doc(1024);
        deserializeJson(doc, result.c_str());
    
        JsonArray obj = doc.as<JsonArray>();
        _voice = obj[0].as<String>();
    
        Serial.print("Using voice ");
        Serial.println(_voice);
    }
    else
    {
        Serial.print("Failed to get voices - error ");
        Serial.println(httpResponseCode);
    }
    
  12. Sau đó, kết thúc kết nối HTTP client:

    httpClient.end();
    
  13. Mở tệp main.cpp, và thêm chỉ thị include sau vào đầu để bao gồm tệp tiêu đề mới này:

    #include "text_to_speech.h"
    
  14. Trong hàm setup, bên dưới lệnh gọi đến speechToText.init();, thêm đoạn mã sau để khởi tạo lớp TextToSpeech:

    textToSpeech.init();
    
  15. Biên dịch mã này, tải nó lên Wio Terminal của bạn và kiểm tra thông qua serial monitor. Đảm bảo ứng dụng functions của bạn đang chạy.

    Bạn sẽ thấy danh sách các giọng nói có sẵn được trả về từ ứng dụng functions, cùng với giọng nói đã chọn.

    --- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time
    --- More details at http://bit.ly/pio-monitor-filters
    --- Miniterm on /dev/cu.usbmodem1101  9600,8,N,1 ---
    --- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
    Connecting to WiFi..
    Connected!
    Got access token.
    ["en-US-JennyNeural", "en-US-JennyMultilingualNeural", "en-US-GuyNeural", "en-US-AriaNeural", "en-US-AmberNeural", "en-US-AnaNeural", "en-US-AshleyNeural", "en-US-BrandonNeural", "en-US-ChristopherNeural", "en-US-CoraNeural", "en-US-ElizabethNeural", "en-US-EricNeural", "en-US-JacobNeural", "en-US-MichelleNeural", "en-US-MonicaNeural", "en-US-AriaRUS", "en-US-BenjaminRUS", "en-US-GuyRUS", "en-US-ZiraRUS"]
    Using voice en-US-JennyNeural
    Ready.
    

Chuyển đổi văn bản thành giọng nói

Khi bạn đã có một giọng nói để sử dụng, nó có thể được sử dụng để chuyển đổi văn bản thành giọng nói. Các hạn chế về bộ nhớ tương tự với các giọng nói cũng áp dụng khi chuyển đổi giọng nói thành văn bản, vì vậy bạn sẽ cần ghi giọng nói vào thẻ SD để phát qua ReSpeaker.

💁 Trong các bài học trước của dự án này, bạn đã sử dụng bộ nhớ flash để lưu trữ giọng nói được thu từ micro. Bài học này sử dụng thẻ SD vì nó dễ dàng hơn để phát âm thanh từ đó bằng cách sử dụng thư viện âm thanh Seeed.

Ngoài ra còn có một hạn chế khác cần xem xét, đó là dữ liệu âm thanh có sẵn từ dịch vụ giọng nói và các định dạng mà Wio Terminal hỗ trợ. Không giống như máy tính đầy đủ, các thư viện âm thanh cho vi điều khiển có thể rất hạn chế trong các định dạng âm thanh mà chúng hỗ trợ. Ví dụ, thư viện Seeed Arduino Audio có thể phát âm thanh qua ReSpeaker chỉ hỗ trợ âm thanh ở tần số mẫu 44.1KHz. Dịch vụ giọng nói Azure có thể cung cấp âm thanh ở một số định dạng, nhưng không định dạng nào sử dụng tần số mẫu này, chúng chỉ cung cấp 8KHz, 16KHz, 24KHz và 48KHz. Điều này có nghĩa là âm thanh cần được chuyển đổi lại thành 44.1KHz, điều mà sẽ cần nhiều tài nguyên hơn Wio Terminal có, đặc biệt là bộ nhớ.

Khi cần xử lý dữ liệu như thế này, thường tốt hơn là sử dụng mã serverless, đặc biệt nếu dữ liệu được lấy thông qua một cuộc gọi web. Wio Terminal có thể gọi một hàm serverless, truyền văn bản để chuyển đổi, và hàm serverless có thể vừa gọi dịch vụ giọng nói để chuyển đổi văn bản thành giọng nói, vừa chuyển đổi lại âm thanh thành tần số mẫu cần thiết. Sau đó, nó có thể trả về âm thanh dưới dạng mà Wio Terminal cần để lưu trữ trên thẻ SD và phát qua ReSpeaker.

Nhiệm vụ - tạo một hàm serverless để chuyển đổi văn bản thành giọng nói

  1. Mở dự án smart-timer-trigger của bạn trong VS Code, và mở terminal đảm bảo môi trường ảo đã được kích hoạt. Nếu không, hãy tắt và tạo lại terminal.

  2. Thêm một HTTP trigger mới vào ứng dụng này có tên text-to-speech bằng lệnh sau từ bên trong terminal của VS Code trong thư mục gốc của dự án ứng dụng functions:

    func new --name text-to-speech --template "HTTP trigger"
    

    Điều này sẽ tạo một HTTP trigger có tên text-to-speech.

  3. Gói Pip librosa có các hàm để chuyển đổi lại âm thanh, vì vậy hãy thêm nó vào tệp requirements.txt:

    librosa
    

    Sau khi đã thêm, cài đặt các gói Pip bằng lệnh sau từ terminal của VS Code:

    pip install -r requirements.txt
    

    ⚠️ Nếu bạn đang sử dụng Linux, bao gồm Raspberry Pi OS, bạn có thể cần cài đặt libsndfile bằng lệnh sau:

    sudo apt update
    sudo apt install libsndfile1-dev
    
  4. Để chuyển đổi văn bản thành giọng nói, bạn không thể sử dụng trực tiếp khóa API dịch vụ giọng nói, thay vào đó bạn cần yêu cầu một access token, sử dụng khóa API để xác thực yêu cầu access token. Mở tệp __init__.py từ thư mục text-to-speech và thay thế toàn bộ mã trong đó bằng nội dung sau:

    import io
    import os
    import requests
    
    import librosa
    import soundfile as sf
    import azure.functions as func
    
    location = os.environ['SPEECH_LOCATION']
    speech_key = os.environ['SPEECH_KEY']
    
    def get_access_token():
        headers = {
            'Ocp-Apim-Subscription-Key': speech_key
        }
    
        token_endpoint = f'https://{location}.api.cognitive.microsoft.com/sts/v1.0/issuetoken'
        response = requests.post(token_endpoint, headers=headers)
        return str(response.text)
    

    Điều này định nghĩa các hằng số cho vị trí và khóa dịch vụ giọng nói sẽ được đọc từ các thiết lập. Sau đó, nó định nghĩa hàm get_access_token để lấy access token cho dịch vụ giọng nói.

  5. Bên dưới đoạn mã này, thêm nội dung sau:

    playback_format = 'riff-48khz-16bit-mono-pcm'
    
    def main(req: func.HttpRequest) -> func.HttpResponse:
        req_body = req.get_json()
        language = req_body['language']
        voice = req_body['voice']
        text = req_body['text']
    
        url = f'https://{location}.tts.speech.microsoft.com/cognitiveservices/v1'
    
        headers = {
            'Authorization': 'Bearer ' + get_access_token(),
            'Content-Type': 'application/ssml+xml',
            'X-Microsoft-OutputFormat': playback_format
        }
    
        ssml =  f'<speak version=\'1.0\' xml:lang=\'{language}\'>'
        ssml += f'<voice xml:lang=\'{language}\' name=\'{voice}\'>'
        ssml += text
        ssml += '</voice>'
        ssml += '</speak>'
    
        response = requests.post(url, headers=headers, data=ssml.encode('utf-8'))
    
        raw_audio, sample_rate = librosa.load(io.BytesIO(response.content), sr=48000)
        resampled = librosa.resample(raw_audio, sample_rate, 44100)
    
        output_buffer = io.BytesIO()
        sf.write(output_buffer, resampled, 44100, 'PCM_16', format='wav')
        output_buffer.seek(0)
    
        return func.HttpResponse(output_buffer.read(), status_code=200)
    

    Điều này định nghĩa HTTP trigger chuyển đổi văn bản thành giọng nói. Nó trích xuất văn bản để chuyển đổi, ngôn ngữ và giọng nói từ nội dung JSON được gửi đến yêu cầu, xây dựng một số SSML để yêu cầu giọng nói, sau đó gọi REST API liên quan xác thực bằng access token. Cuộc gọi REST API này trả về âm thanh được mã hóa dưới dạng tệp WAV mono 16-bit, 48KHz, được định nghĩa bởi giá trị playback_format, được gửi đến cuộc gọi REST API.

    Sau đó, âm thanh này được chuyển đổi lại bởi librosa từ tần số mẫu 48KHz thành tần số mẫu 44.1KHz, sau đó âm thanh này được lưu vào một bộ đệm nhị phân và được trả về.

  6. Chạy ứng dụng functions của bạn cục bộ, hoặc triển khai nó lên cloud. Sau đó, bạn có thể gọi nó bằng một công cụ như curl theo cách tương tự như bạn đã thử nghiệm HTTP trigger text-to-timer. Đảm bảo truyền ngôn ngữ, giọng nói và văn bản dưới dạng nội dung JSON:

    {
        "language": "<language>",
        "voice": "<voice>",
        "text": "<text>"
    }
    

    Thay thế <language> bằng ngôn ngữ của bạn, chẳng hạn như en-GB, hoặc zh-CN. Thay thế <voice> bằng giọng nói bạn muốn sử dụng. Thay thế <text> bằng văn bản bạn muốn chuyển đổi thành giọng nói. Bạn có thể lưu đầu ra vào một tệp và phát nó bằng bất kỳ trình phát âm thanh nào có thể phát tệp WAV.

    Ví dụ, để chuyển đổi "Hello" thành giọng nói sử dụng tiếng Anh Mỹ với giọng Jenny Neural, với ứng dụng functions chạy cục bộ, bạn có thể sử dụng lệnh curl sau:

    curl -X GET 'http://localhost:7071/api/text-to-speech' \
         -H 'Content-Type: application/json' \
         -o hello.wav \
         -d '{
           "language":"en-US",
           "voice": "en-US-JennyNeural",
           "text": "Hello"
         }'
    

    Điều này sẽ lưu âm thanh vào hello.wav trong thư mục hiện tại.

💁 Bạn có thể tìm thấy mã này trong thư mục code-spoken-response/functions.

Nhiệm vụ - lấy giọng nói từ Wio Terminal của bạn

  1. Mở dự án smart-timer của bạn trong VS Code nếu nó chưa được mở.

  2. Mở tệp tiêu đề config.h và thêm URL cho ứng dụng functions của bạn:

    const char *TEXT_TO_SPEECH_FUNCTION_URL = "<URL>";
    

    Thay thế <URL> bằng URL cho HTTP trigger text-to-speech trên ứng dụng functions của bạn. Điều này sẽ giống với giá trị của TEXT_TO_TIMER_FUNCTION_URL, ngoại trừ tên hàm là text-to-speech thay vì text-to-timer.

  3. Mở tệp tiêu đề text_to_speech.h, và thêm phương thức sau vào phần public của lớp TextToSpeech:

    void convertTextToSpeech(String text)
    {
    }
    
  4. Trong phương thức convertTextToSpeech, thêm đoạn mã sau để tạo JSON gửi đến ứng dụng functions:

    DynamicJsonDocument doc(1024);
    doc["language"] = LANGUAGE;
    doc["voice"] = _voice;
    doc["text"] = text;
    
    String body;
    serializeJson(doc, body);
    

    Điều này ghi ngôn ngữ, giọng nói và văn bản vào tài liệu JSON, sau đó tuần tự hóa nó thành một chuỗi.

  5. Bên dưới đoạn mã này, thêm đoạn mã sau để gọi ứng dụng functions:

    HTTPClient httpClient;
    httpClient.begin(_client, TEXT_TO_SPEECH_FUNCTION_URL);
    
    int httpResponseCode = httpClient.POST(body);
    

    Điều này tạo một HTTPClient, sau đó thực hiện yêu cầu POST sử dụng tài liệu JSON đến HTTP trigger chuyển đổi văn bản thành giọng nói.

  6. Nếu cuộc gọi thành công, dữ liệu nhị phân thô được trả về từ cuộc gọi ứng dụng functions có thể được truyền trực tiếp vào một tệp trên thẻ SD. Thêm đoạn mã sau để thực hiện điều này:

    if (httpResponseCode == 200)
    {            
        File wav_file = SD.open("SPEECH.WAV", FILE_WRITE);
        httpClient.writeToStream(&wav_file);
        wav_file.close();
    }
    else
    {
        Serial.print("Failed to get speech - error ");
        Serial.println(httpResponseCode);
    }
    

    Đoạn mã này kiểm tra phản hồi, và nếu là 200 (thành công), dữ liệu nhị phân được truyền vào một tệp trong thư mục gốc của thẻ SD có tên SPEECH.WAV.

  7. Ở cuối phương thức này, đóng kết nối HTTP:

    httpClient.end();
    
  8. Văn bản cần được nói bây giờ có thể được chuyển đổi thành âm thanh. Trong tệp main.cpp, thêm dòng sau vào cuối hàm say để chuyển đổi văn bản cần nói thành âm thanh:

    textToSpeech.convertTextToSpeech(text);
    ```

### Nhiệm vụ - phát âm thanh từ Wio Terminal của bạn

**Sp ra mt**

## Triển khai ứng dụng chức năng của bạn lên đám mây

Lý do để chy ng dng chc năng cc b là vì gói Pip `librosa` trên Linux có mt ph thuc vào mt thư vin không được cài đặt mc định, và cn phi được cài đặt trước khi ng dng chc năng có th chy. ng dng chc năng là serverless - không có máy ch nào bn có th t qun lý, vì vy không có cách nào để cài đặt thư vin này trước.

Cách để làm điu này là trin khai ng dng chc năng ca bn bng cách s dng mt Docker container. Container này s được đám mây trin khai bt c khi nào cn khi động mt phiên bn mi ca ng dng chc năng ca bn (chng hn khi nhu cu vượt quá tài nguyên sn có, hoc nếu ng dng chc năng không được s dng trong mt thi gian và b đóng li).

Bn có th tìm thy hướng dn để thiết lp ng dng chc năng và trin khai qua Docker trong [tài liu to chc năng trên Linux bng hình nh tùy chnh trên Microsoft Docs](https://docs.microsoft.com/azure/azure-functions/functions-create-function-linux-custom-image?WT.mc_id=academic-17441-jabenn&tabs=bash%2Cazurecli&pivots=programming-language-python).

Sau khi đã trin khai, bn có th chuyn mã Wio Terminal ca mình để truy cp chc năng này:

1. Thêm chng ch Azure Functions vào `config.h`:

    ```cpp
    const char *FUNCTIONS_CERTIFICATE =
        "-----BEGIN CERTIFICATE-----\r\n"
        "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\r\n"
        "-----END CERTIFICATE-----\r\n";
    ```

1. Thay đổi tt c các ln bao gm `
<WiFiClient.h>` thành `<WiFiClientSecure.h>`.

1. Thay đổi tt c các trường `WiFiClient` thành `WiFiClientSecure`.

1. Trong mi lp có trường `WiFiClientSecure`, thêm mt constructor và đặt chng ch trong constructor đó:

    ```cpp
    _client.setCACert(FUNCTIONS_CERTIFICATE);
    ```

---

**Tuyên b min tr trách nhim**:  
Tài liu này đã được dch bng dch v dch thut AI [Co-op Translator](https://github.com/Azure/co-op-translator). Mặc dù chúng tôi cố gắng đảm bảo độ chính xác, xin lưu ý rằng các bản dịch tự động có thể chứa lỗi hoặc không chính xác. Tài liệu gốc bằng ngôn ngữ bản địa nên được coi là nguồn thông tin chính thức. Đối với các thông tin quan trọng, khuyến nghị sử dụng dịch vụ dịch thuật chuyên nghiệp bởi con người. Chúng tôi không chịu trách nhiệm cho bất kỳ sự hiểu lầm hoặc diễn giải sai nào phát sinh từ việc sử dụng bản dịch này.