# Internacionalização (i18n)

O SillyTavern suporta múltiplos idiomas. Este guia explica como adicionar e gerenciar traduções.

Você provavelmente está aqui porque algum trecho de texto não está traduzido no seu idioma, e isso está te irritando. Primeiro vou mostrar como corrigi algumas traduções faltantes no locale Chinês (Tradicional). Cada uma estava faltando por um motivo diferente, então você terá uma boa ideia de como corrigir suas próprias traduções faltantes.

Na segunda metade, vamos ver

  • como a i18n funciona no SillyTavern,
  • escrevendo traduções e código para usá-las,
  • funções de debug para encontrar traduções faltantes,
  • adicionando um novo idioma,
  • e contribuindo suas alterações.

Se você está desenvolvendo uma extensão ou modificando o código principal, escreva seu HTML e JavaScript com i18n em mente. Desta forma seu trabalho estará pronto para outras pessoas traduzirem para seu idioma.

Ninguém conhece 15 idiomas sozinho. Trabalhamos juntos para tornar o SillyTavern acessível a todos.

Todos no mundo devem ser capazes de usar seu próprio idioma em telefones e computadores.


# Vamos corrigir algumas traduções faltantes!

# Generate Image

O texto "Generate Image" não está traduzido no locale Chinês (Tradicional). Por quê?

generate-image-pre.png
generate-image-pre.png

Clique com o botão direito no elemento e inspecione-o. Você verá o HTML:

<!--rendered HTML-->
<div class="list-group-item flex-container flexGap5 interactable" id="sd_gen" tabindex="0">
    <div data-i18n="[title]Trigger Stable Diffusion" title="觸發 Stable Diffusion"
         class="fa-solid fa-paintbrush extensionsMenuExtensionButton"></div>
    <span>Generate Image</span>
</div>

Onde está seu atributo data-i18n? Está faltando! Vamos adicioná-lo. Encontramos no código-fonte:

<!--public/scripts/extensions/stable-diffusion/button.html-->
<div id="sd_gen" class="list-group-item flex-container flexGap5">
    <div class="fa-solid fa-paintbrush extensionsMenuExtensionButton" title="Trigger Stable Diffusion"
         data-i18n="[title]Trigger Stable Diffusion"></div>
    <span>Generate Image</span>
</div>
<div id="sd_stop_gen" class="list-group-item flex-container flexGap5">
    <div class="fa-solid fa-circle-stop extensionsMenuExtensionButton" title="Abort current image generation task"
         data-i18n="[title]Abort current image generation task"></div>
    <span>Stop Image Generation</span>
</div>

Estamos com sorte, aquela string Generate Image está em muitos dos arquivos de idioma, incluindo em Chinês (Tradicional).

generate-image-lang.png
generate-image-lang.png

{
    "Generate Image": "生成图片"
}

Por que não está aparecendo? Temos que conectar o elemento corretamente:

<!--public/scripts/extensions/stable-diffusion/button.html-->
<div id="sd_gen" class="list-group-item flex-container flexGap5">
    <div class="fa-solid fa-paintbrush extensionsMenuExtensionButton" title="Trigger Stable Diffusion"
         data-i18n="[title]Trigger Stable Diffusion"></div>
    <span data-i18n="Generate Image">Generate Image</span>
</div>
<div id="sd_stop_gen" class="list-group-item flex-container flexGap5">
    <div class="fa-solid fa-circle-stop extensionsMenuExtensionButton" title="Abort current image generation task"
         data-i18n="[title]Abort current image generation task"></div>
    <span>Stop Image Generation</span>
</div>

Agora funciona! Recarregue a página e veja.

generate-image-post.png
generate-image-post.png

Mas já que temos o HTML aberto, o que há com Stop Image Generation logo abaixo? O HTML não parece certo.

Se gerarmos uma imagem e depois abrirmos o menu de varinha enquanto está gerando, vemos texto não traduzido.

stop-generating-image-pre.png
stop-generating-image-pre.png

Primeiro corrija o HTML:

<!--public/scripts/extensions/stable-diffusion/button.html-->
<div id="sd_gen" class="list-group-item flex-container flexGap5">
    <div class="fa-solid fa-paintbrush extensionsMenuExtensionButton" title="Trigger Stable Diffusion"
         data-i18n="[title]Trigger Stable Diffusion"></div>
    <span data-i18n="Generate Image">Generate Image</span>
</div>
<div id="sd_stop_gen" class="list-group-item flex-container flexGap5">
    <div class="fa-solid fa-circle-stop extensionsMenuExtensionButton" title="Abort current image generation task"
         data-i18n="[title]Abort current image generation task"></div>
    <span data-i18n="Stop Image Generation">Stop Image Generation</span>
</div>

Isso não é suficiente para corrigir o problema. Não há traduções para "Stop Image Generation" no arquivo Chinês (Tradicional). Podemos adicioná-la! Aqui está uma tradução possível:

{
    "Stop Image Generation": "停止生成图片"
} 

... que podemos adicionar ao arquivo JSON logo após a tradução "Generate Image".

{
    "Generate Image": "生成图片",
    "Stop Image Generation": "停止生成图片"
} 

Após alguma discussão com o Claude, vamos realmente usar as seguintes traduções:

  • Chinês Tradicional: "Stop Image Generation": "終止圖片生成"
  • Chinês Simplificado: "Stop Image Generation": "中止图像生成"
  • Japonês: "Stop Image Generation": "画像生成を停止"

stop-generating-post-2.png
stop-generating-post-2.png

# Generate Caption

"Generate Caption" não está traduzido no locale Chinês (Tradicional). Vamos corrigir isso!

generate-image-post.png
generate-image-post.png

Onde está? Inspecione o elemento.

<!--rendered HTML-->
<div id="send_picture" class="list-group-item flex-container flexGap5 interactable" tabindex="0">
    <div class="fa-solid fa-image extensionsMenuExtensionButton"></div>
    Generate Caption
</div>

Acontece que este HTML é produzido por JavaScript. Vamos encontrar o código-fonte.

// public/scripts/extensions/caption/index.js
const sendButton = $(`
        <div id="send_picture" class="list-group-item flex-container flexGap5">
            <div class="fa-solid fa-image extensionsMenuExtensionButton"></div>
            Generate Caption
        </div>`);

Primeiro teremos que corrigir o código:

// public/scripts/extensions/caption/index.js
const sendButton = $(`
        <div id="send_picture" class="list-group-item flex-container flexGap5">
            <div class="fa-solid fa-image extensionsMenuExtensionButton"></div>
            <span data-i18n="Generate Caption">Generate Caption</span>
        </div>`);

Também não há traduções para "Generate Caption" no arquivo Chinês (Tradicional). Vamos adicioná-la!

{
    "Generate Caption": "生成圖片說明"
}

Vamos usar as seguintes traduções:

  • Chinês Tradicional: "Generate Caption": "生成圖片說明"
  • Chinês Simplificado: "Generate Caption": "生成图片说明"
  • Japonês: "Generate Caption": "画像説明を生成"

generate-caption-post.png
generate-caption-post.png

# Inspect Prompts

O texto "Inspect Prompts" não está traduzido no locale Chinês (Tradicional). Por quê? Este é um pouco mais complicado. O texto é gerado por JavaScript, e a tradução está faltando.

// Extension-PromptInspector/index.js
const enabledText = 'Stop Inspecting';
const disabledText = 'Inspect Prompts';

Bem, olha só... nenhuma das duas frases está nos arquivos i18n. Vamos adicioná-las.

{
    "Stop Inspecting": "停止檢查",
    "Inspect Prompts": "檢查提示"
}

Agora temos que corrigir o código JavaScript. Ele tem que usar a função t para obter a tradução.

// Extension-PromptInspector/index.js
import {t} from '../../../i18n.js';

const enabledText = t`Stop Inspecting`;
const disabledText = t`Inspect Prompts`;

Recebemos essas sugestões do Claude. Mantenha as strings, ignore o código. Elas têm que ser adicionadas aos arquivos JSON.

// 1. Simplified Chinese (zh-cn):
const enabledText = t`停止检查`;
const disabledText = t`检查提示词`;
// 2. Traditional Chinese (zh-tw):
const enabledText = t`停止檢查`;
const disabledText = t`檢查提示詞`;
// 3. Japanese (ja-jp):
const enabledText = t`検査を停止`;
const disabledText = t`プロンプトを検査`;

Vamos mesclar isso nos arquivos JSON.

{
    "Stop Inspecting": "停止检查",
    "Inspect Prompts": "检查提示词"
}
{
    "Stop Inspecting": "停止檢查",
    "Inspect Prompts": "檢查提示詞"
}
{
    "Stop Inspecting": "検査を停止",
    "Inspect Prompts": "プロンプトを検査"
}

toggle-prompt-inspection-post-tt.png
toggle-prompt-inspection-post-tt.png

Uma pena sobre aquele tooltip. O problema é que o código não usa a função t.

launchButton.title = 'Toggle prompt inspection';

Teremos que corrigir isso no código da extensão.

launchButton.title = t`Toggle prompt inspection`;

Também precisamos adicionar as traduções aos arquivos JSON.

{
    "Toggle prompt inspection": "切换提示检查"
}
{
    "Toggle prompt inspection": "切换提示词检查"
}
{
    "Toggle prompt inspection": "プロンプト検査の切り替え"
}

Prompt inspector é uma extensão separada, então vamos fazer PR das correções de código para aquele repositório: https://github.com/SillyTavern/Extension-PromptInspector/pull/1

As traduções serão adicionadas ao repositório principal do SillyTavern. https://github.com/SillyTavern/SillyTavern/pull/3198

start-inspecting-post.png
start-inspecting-post.png

# Arquivos de idioma

Cada idioma tem um arquivo JSON em public/locales/ nomeado com seu código de idioma (por exemplo, ru-ru.json).

O arquivo contém pares chave-valor onde:

  • Chaves são ou o texto original em inglês ou identificadores únicos
  • Valores são o texto traduzido

Exemplo:

{
    "Save": "Сохранить",
    "Cancel": "Отмена",
    "Could not find proxy with name '${0}'": "Не удалось найти прокси с названием '${0}'"
}

# Como as traduções funcionam

Existem duas maneiras pelas quais as traduções são usadas no aplicativo:

  1. Elementos HTML: Usando atributos data-i18n

    <div data-i18n="some_key">Default Text</div>

    O texto padrão no HTML será substituído pelo texto traduzido, se disponível.

  2. Template Strings: No código JavaScript usando a função t

    t`Some text with ${variable}`

    Essas strings devem ser traduzidas mantendo os placeholders ${0}, ${1}, etc. intactos.

SillyTavern usa elementos HTML com atributos data-i18n para marcar conteúdo traduzível. Existem várias maneiras de usar isso:

# 1. Traduzindo Texto do Elemento

Para conteúdo de texto simples:

<span data-i18n="Role:">Role:</span>
{
    "Role:": "Роль:"
}

Isso substitui o conteúdo de texto do elemento pela tradução de "Role:".

# 2. Traduzindo Atributos

Para traduzir um atributo como um title ou placeholder:

<a class="menu_button fa-chain fa-solid fa-fw"
   title="Insert prompt"
   data-i18n="[title]Insert prompt"></a>
{
    "Insert prompt": "Вставить промпт"
}

O prefixo [title] indica qual atributo traduzir. O resto do valor do atributo é o texto que será usado como chave de busca no arquivo JSON. É comum os programadores usarem o texto em inglês como chave, mas não é obrigatório. A chave pode ser qualquer identificador único.

O texto original em inglês deve estar presente no atributo correspondente (title="Insert prompt"). É usado como fallback se a tradução estiver faltando. Mais notavelmente, não há arquivo de tradução para inglês.

Aqui está um exemplo de uso de um identificador único no_items_text como chave, em vez do texto em inglês:

<!--suppress HtmlUnknownAttribute -->
<div class="openai_logit_bias_list" no_items_text="No items"
     data-i18n="[no_items_text]openai_logit_bias_no_items"></div>
{
    "openai_logit_bias_no_items": "没有相关产品"
}   

# 3. Múltiplas Traduções Por Elemento

Alguns elementos precisam de traduções tanto de conteúdo quanto de atributo, separadas por ponto e vírgula. O padrão mais comum é traduzir tanto o conteúdo de texto do elemento quanto seu atributo title:

<div data-source="openrouter" class="menu_button menu_button_icon openrouter_authorize"
     title="Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai"
     data-i18n="Authorize;[title]Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai">
    Authorize
</div>
{
    "Authorize": "Авторизоваться",
    "Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai": "Получите свой OpenRouter API токен используя OAuth. У вас будет открыта вкладка openrouter.ai"
}

Isso traduz:

  • O conteúdo de texto do elemento usando a chave "Authorize"
  • O atributo title usando a chave "Get your OpenRouter API token using OAuth flow. You will be redirected to openrouter.ai"

Observe que tanto o atributo title quanto o conteúdo de texto do elemento são fornecidos em inglês como fallbacks.

Você também pode traduzir múltiplos atributos:

<!--suppress HtmlUnknownAttribute -->
<textarea id="send_textarea" name="text" class="mdHotkeys"
          data-i18n="[no_connection_text]Not connected to API!;[connected_text]Type a message, or /? for help"
          placeholder="Not connected to API!"
          no_connection_text="Not connected to API!"
          connected_text="Type a message, or /? for help"></textarea>

As traduções correspondentes no seu arquivo de idioma ficariam assim:

{
    "Not connected to API!": "Нет соединения с API!",
    "Type a message, or /? for help": "Введите сообщение или /? для помощи"
}

Quando a página carrega, o sistema:

  1. Encontra todos os elementos com atributos data-i18n
  2. Analisa quaisquer prefixos de atributo como [title] ou [placeholder]
  3. Procura cada chave no arquivo JSON do seu idioma
  4. Substitui o conteúdo ou atributos do elemento pelo texto traduzido

# Texto Dinâmico

Para texto dinâmico no código JavaScript, as traduções usam:

  1. Template literals com a função t:

    toastr.warn(t`Tag ${tagName} not found.`);
  2. Função de tradução direta:

    translate("Some text", "optional_key")

# Placeholders de Variável

Algumas strings contêm placeholders para valores dinâmicos usando ${0}, ${1}, etc:

toastr.error(t`Could not find proxy with name '${presetName}'`);
{
    "Could not find proxy with name '${0}'": "Не удалось найти прокси с названием '${0}'"
}

Mantenha os placeholders iguais para chave e tradução. O sistema substituirá ${0} pelo valor de presetName, etc.

# Encontrando traduções faltantes

Digamos que você não quer apenas corrigir uma tradução faltante irritante, você quer encontrar todas elas.

Essa é uma grande ambição! Mesmo corrigir uma tradução vale a pena. Mas se você quer pegar todas, precisa de uma ferramenta.

# SillyTavern-i18n

https://github.com/SillyTavern/SillyTavern-i18n

Ferramentas para trabalhar com arquivos de localização frontend.

Recursos:

  • Adicionar automaticamente novas chaves para traduzir de arquivos HTML.
  • Remover chaves ausentes de arquivos de localização.
  • Usar tradução automática do Google para preencher automaticamente valores faltantes.
  • Ordenar arquivos JSON por chaves.

# Funções de debug integradas

Estas estão em User Settings > Debug Menu.

# Get missing translations

Detecta dados de localização faltantes no locale atual e despeja os dados no console do navegador. Se o locale atual for inglês, pesquisa todos os outros locales.

O console mostrará uma tabela de traduções faltantes com:

  • key: O texto ou identificador que precisa de tradução
  • language: Seu código de idioma atual
  • value: O texto em inglês para traduzir

# Apply locale

Reaplica o locale atualmente selecionado à página

# Adicionando um novo idioma

Para adicionar suporte a um novo idioma:

  1. Adicione seu idioma a public/locales/lang.json:

    {
      "lang": "xx-xx",
      "display": "Language Name (English Name)"
    }
  2. Crie public/locales/xx-xx.json com suas traduções

# Contribuindo

Quando suas traduções estiverem prontas:

  1. Verifique se seu arquivo JSON é válido
  2. Teste minuciosamente no aplicativo
  3. Envie via pull request do GitHub