use std::sync::OnceLock;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Idioma {
English,
Portugues,
}
static IDIOMA_GLOBAL: OnceLock<Idioma> = OnceLock::new();
pub fn idioma_atual() -> Idioma {
*IDIOMA_GLOBAL.get().unwrap_or(&Idioma::English)
}
pub fn definir_idioma(idioma: Idioma) {
let _ = IDIOMA_GLOBAL.set(idioma);
}
pub fn resolver_idioma(cli_lang: Option<&str>) -> Idioma {
if let Some(lang) = cli_lang {
return parse_lang_str(lang);
}
if let Ok(env_lang) = std::env::var("CONTEXT7_LANG") {
return parse_lang_str(&env_lang);
}
if let Some(locale) = sys_locale::get_locale() {
if locale.to_lowercase().starts_with("pt") {
return Idioma::Portugues;
}
}
Idioma::English
}
fn parse_lang_str(s: &str) -> Idioma {
match s.to_lowercase().as_str() {
"pt" | "pt-br" | "pt_br" | "portugues" | "português" => Idioma::Portugues,
_ => Idioma::English,
}
}
#[derive(Debug, Clone, Copy)]
pub enum Mensagem {
ChaveAdicionada,
NenhumaChaveArmazenada,
UsarKeysAdd,
ContadorChaves,
NenhumaChaveParaRemover,
IndiceInvalido,
ChaveRemovidaSucesso,
OperacaoCancelada,
TodasChavesRemovidas,
SistemaXdgNaoSuportado,
ChavesImportadasSucesso,
NenhumaChaveContext7NoArquivo,
ConfirmarRemoverTodas,
RespostaConfirmacao,
NenhumaBibliotecaEncontrada,
BibliotecasEncontradas,
ConfiancaScore,
NenhumaDocumentacaoEncontrada,
TituloDocumentacao,
TituloFontes,
SemConteudoDisponivel,
BuscandoBiblioteca,
ErroDeRede,
ErroDeRedeDocs,
FalhaDesserializar,
RateLimitAtingido,
ErrodoServidor,
ChaveApiInvalida,
Tentativa,
AguardandoRetry,
ErroCaminhoXdg,
FalhaLerConfig,
FalhaTomlInvalido,
FalhaEscreverConfig,
FalhaCriarDiretorio,
NenhumaChaveConfigurada,
FalhaSerializarToml,
ChavesCarregadasEnvVar,
ChavesCarregadasXdg,
FalhaLerXdgContinuando,
IniciandoComChaves,
ChavesCarregadasCompileTime,
FalhaSerializarJson,
FalhaSerializarDocs,
FalhaBuscarBiblioteca,
FalhaLerMetadados,
FalhaDefinirPermissoes,
FalhaBuscarDocumentacao,
FalhaCriarClienteHttp,
SemDocumentacaoDisponivel,
BibliotecaNaoEncontradaApi,
}
impl Mensagem {
pub fn texto(self, idioma: Idioma) -> &'static str {
match idioma {
Idioma::English => en(self),
Idioma::Portugues => pt(self),
}
}
}
pub fn t(msg: Mensagem) -> &'static str {
match idioma_atual() {
Idioma::English => en(msg),
Idioma::Portugues => pt(msg),
}
}
fn en(msg: Mensagem) -> &'static str {
match msg {
Mensagem::ChaveAdicionada => "Key added successfully at:",
Mensagem::NenhumaChaveArmazenada => "No key stored.",
Mensagem::UsarKeysAdd => "Use `context7 keys add <KEY>` to add a key.",
Mensagem::ContadorChaves => "key(s) stored:",
Mensagem::NenhumaChaveParaRemover => "No key stored to remove.",
Mensagem::IndiceInvalido => "Invalid index. Use a number between 1 and",
Mensagem::ChaveRemovidaSucesso => "Key removed successfully.",
Mensagem::OperacaoCancelada => "Operation cancelled.",
Mensagem::TodasChavesRemovidas => "All keys removed.",
Mensagem::SistemaXdgNaoSuportado => "System does not support XDG directories.",
Mensagem::ChavesImportadasSucesso => "key(s) imported successfully.",
Mensagem::NenhumaChaveContext7NoArquivo => "No CONTEXT7_API= key found in:",
Mensagem::ConfirmarRemoverTodas => "Are you sure you want to remove ALL keys? [y/N] ",
Mensagem::RespostaConfirmacao => "y|yes",
Mensagem::NenhumaBibliotecaEncontrada => "No library found.",
Mensagem::BibliotecasEncontradas => "Libraries found:",
Mensagem::ConfiancaScore => "trust",
Mensagem::NenhumaDocumentacaoEncontrada => "No documentation found.",
Mensagem::TituloDocumentacao => "Documentation:",
Mensagem::TituloFontes => "Sources:",
Mensagem::SemConteudoDisponivel => "No content available.",
Mensagem::BuscandoBiblioteca => "Searching library:",
Mensagem::ErroDeRede => "Network error searching library:",
Mensagem::ErroDeRedeDocs => "Network error fetching documentation:",
Mensagem::FalhaDesserializar => "Failed to deserialise JSON response:",
Mensagem::RateLimitAtingido => "Rate limit reached (429), waiting for retry…",
Mensagem::ErrodoServidor => "Server error, retrying…",
Mensagem::ChaveApiInvalida => "Invalid API key (401/403), trying next…",
Mensagem::Tentativa => "Attempt",
Mensagem::AguardandoRetry => "Waiting before retrying…",
Mensagem::ErroCaminhoXdg => "System does not support XDG — cannot save configuration",
Mensagem::FalhaLerConfig => "Failed to read XDG config at:",
Mensagem::FalhaTomlInvalido => "Invalid TOML at:",
Mensagem::FalhaEscreverConfig => "Failed to write config at:",
Mensagem::FalhaCriarDiretorio => "Failed to create directory:",
Mensagem::NenhumaChaveConfigurada => {
"No API key configured. Set CONTEXT7_API_KEYS or use `context7 keys add <KEY>`."
}
Mensagem::FalhaSerializarToml => "Failed to serialise configuration to TOML",
Mensagem::ChavesCarregadasEnvVar => {
"Keys loaded from CONTEXT7_API_KEYS environment variable"
}
Mensagem::ChavesCarregadasXdg => "Keys loaded from XDG configuration",
Mensagem::FalhaLerXdgContinuando => "Failed to read XDG configuration (continuing):",
Mensagem::IniciandoComChaves => "Starting context7 with",
Mensagem::ChavesCarregadasCompileTime => "Keys loaded from compile-time CONTEXT7_API_KEYS",
Mensagem::FalhaSerializarJson => "Failed to serialise results to JSON",
Mensagem::FalhaSerializarDocs => "Failed to serialise documentation to JSON",
Mensagem::FalhaBuscarBiblioteca => "Failed to search library",
Mensagem::FalhaLerMetadados => "Failed to read metadata of:",
Mensagem::FalhaDefinirPermissoes => "Failed to set permissions on:",
Mensagem::FalhaBuscarDocumentacao => "Failed to fetch documentation for:",
Mensagem::FalhaCriarClienteHttp => "Failed to create HTTP client",
Mensagem::SemDocumentacaoDisponivel => "No documentation available",
Mensagem::BibliotecaNaoEncontradaApi => {
"Library not found. Verify the ID via `context7 library <name>`."
}
}
}
fn pt(msg: Mensagem) -> &'static str {
match msg {
Mensagem::ChaveAdicionada => "Chave adicionada com sucesso em:",
Mensagem::NenhumaChaveArmazenada => "Nenhuma chave armazenada.",
Mensagem::UsarKeysAdd => "Use `context7 keys add <CHAVE>` para adicionar uma chave.",
Mensagem::ContadorChaves => "chave(s) armazenada(s):",
Mensagem::NenhumaChaveParaRemover => "Nenhuma chave armazenada para remover.",
Mensagem::IndiceInvalido => "Índice inválido. Use um número entre 1 e",
Mensagem::ChaveRemovidaSucesso => "Chave removida com sucesso.",
Mensagem::OperacaoCancelada => "Operação cancelada.",
Mensagem::TodasChavesRemovidas => "Todas as chaves foram removidas.",
Mensagem::SistemaXdgNaoSuportado => "Sistema não suporta diretórios XDG.",
Mensagem::ChavesImportadasSucesso => "chave(s) importada(s) com sucesso.",
Mensagem::NenhumaChaveContext7NoArquivo => "Nenhuma chave CONTEXT7_API= encontrada em:",
Mensagem::ConfirmarRemoverTodas => "Tem certeza que deseja remover TODAS as chaves? [s/N] ",
Mensagem::RespostaConfirmacao => "s|sim",
Mensagem::NenhumaBibliotecaEncontrada => "Nenhuma biblioteca encontrada.",
Mensagem::BibliotecasEncontradas => "Bibliotecas encontradas:",
Mensagem::ConfiancaScore => "confiança",
Mensagem::NenhumaDocumentacaoEncontrada => "Nenhuma documentação encontrada.",
Mensagem::TituloDocumentacao => "Documentação:",
Mensagem::TituloFontes => "Fontes:",
Mensagem::SemConteudoDisponivel => "Sem conteúdo disponível.",
Mensagem::BuscandoBiblioteca => "Buscando biblioteca:",
Mensagem::ErroDeRede => "Erro de rede ao buscar biblioteca:",
Mensagem::ErroDeRedeDocs => "Erro de rede ao buscar documentação:",
Mensagem::FalhaDesserializar => "Falha ao desserializar resposta JSON:",
Mensagem::RateLimitAtingido => "Rate limit atingido (429), aguardando retry…",
Mensagem::ErrodoServidor => "Erro do servidor, tentando novamente…",
Mensagem::ChaveApiInvalida => "Chave de API inválida (401/403), tentando próxima…",
Mensagem::Tentativa => "Tentativa",
Mensagem::AguardandoRetry => "Aguardando antes de tentar novamente…",
Mensagem::ErroCaminhoXdg => {
"Sistema não suporta diretórios XDG — impossível salvar configuração"
}
Mensagem::FalhaLerConfig => "Falha ao ler configuração XDG em:",
Mensagem::FalhaTomlInvalido => "TOML inválido em:",
Mensagem::FalhaEscreverConfig => "Falha ao escrever config em:",
Mensagem::FalhaCriarDiretorio => "Falha ao criar diretório:",
Mensagem::NenhumaChaveConfigurada => {
"Nenhuma chave de API encontrada. Configure CONTEXT7_API_KEYS ou use `context7 keys add <CHAVE>`."
}
Mensagem::FalhaSerializarToml => "Falha ao serializar configuração para TOML",
Mensagem::ChavesCarregadasEnvVar => {
"Chaves carregadas via variável de ambiente CONTEXT7_API_KEYS"
}
Mensagem::ChavesCarregadasXdg => "Chaves carregadas via configuração XDG",
Mensagem::FalhaLerXdgContinuando => "Falha ao ler configuração XDG (continuando):",
Mensagem::IniciandoComChaves => "Iniciando context7 com",
Mensagem::ChavesCarregadasCompileTime => {
"Chaves carregadas via compile-time CONTEXT7_API_KEYS"
}
Mensagem::FalhaSerializarJson => "Falha ao serializar resultados para JSON",
Mensagem::FalhaSerializarDocs => "Falha ao serializar documentação para JSON",
Mensagem::FalhaBuscarBiblioteca => "Falha ao buscar biblioteca",
Mensagem::FalhaLerMetadados => "Falha ao ler metadados de:",
Mensagem::FalhaDefinirPermissoes => "Falha ao definir permissões em:",
Mensagem::FalhaBuscarDocumentacao => "Falha ao buscar documentação para:",
Mensagem::FalhaCriarClienteHttp => "Falha ao criar cliente HTTP",
Mensagem::SemDocumentacaoDisponivel => "Nenhuma documentação disponível",
Mensagem::BibliotecaNaoEncontradaApi => {
"Biblioteca não encontrada. Verifique o ID via `context7 library <nome>`."
}
}
}
#[cfg(test)]
mod testes {
use super::*;
#[test]
fn testa_idioma_atual_padrao_e_english() {
let _ = idioma_atual();
}
#[test]
fn testa_resolver_idioma_cli_flag_pt() {
assert_eq!(resolver_idioma(Some("pt")), Idioma::Portugues);
assert_eq!(resolver_idioma(Some("pt-BR")), Idioma::Portugues);
assert_eq!(resolver_idioma(Some("PT_BR")), Idioma::Portugues);
}
#[test]
fn testa_resolver_idioma_cli_flag_en() {
assert_eq!(resolver_idioma(Some("en")), Idioma::English);
assert_eq!(resolver_idioma(Some("en-US")), Idioma::English);
}
#[test]
fn testa_resolver_idioma_sem_flag_nem_env_retorna_english_ou_pt() {
let idioma = resolver_idioma(None);
assert!(idioma == Idioma::English || idioma == Idioma::Portugues);
}
#[test]
fn testa_t_mensagem_nenhuma_chave_en() {
let msg_en = en(Mensagem::NenhumaChaveArmazenada);
assert!(!msg_en.is_empty());
assert!(
msg_en.to_lowercase().contains("no") || msg_en.to_lowercase().contains("key"),
"EN deve conter 'no' ou 'key', obteve: {}",
msg_en
);
}
#[test]
fn testa_t_mensagem_nenhuma_chave_pt() {
let msg_pt = pt(Mensagem::NenhumaChaveArmazenada);
assert!(!msg_pt.is_empty());
assert!(
msg_pt.to_lowercase().contains("nenhuma") || msg_pt.to_lowercase().contains("chave"),
"PT deve conter 'nenhuma' ou 'chave', obteve: {}",
msg_pt
);
}
#[test]
fn testa_confirmacao_resposta_en_contem_y() {
let confirmacao = en(Mensagem::RespostaConfirmacao);
assert!(
confirmacao.contains('y'),
"EN: resposta confirmação deve conter 'y'"
);
}
#[test]
fn testa_confirmacao_resposta_pt_contem_s() {
let confirmacao = pt(Mensagem::RespostaConfirmacao);
assert!(
confirmacao.contains('s'),
"PT: resposta confirmação deve conter 's'"
);
}
#[test]
fn testa_todas_variantes_en_nao_sao_vazias() {
let variantes = [
Mensagem::ChaveAdicionada,
Mensagem::NenhumaChaveArmazenada,
Mensagem::UsarKeysAdd,
Mensagem::ContadorChaves,
Mensagem::NenhumaChaveParaRemover,
Mensagem::IndiceInvalido,
Mensagem::ChaveRemovidaSucesso,
Mensagem::OperacaoCancelada,
Mensagem::TodasChavesRemovidas,
Mensagem::SistemaXdgNaoSuportado,
Mensagem::ChavesImportadasSucesso,
Mensagem::NenhumaChaveContext7NoArquivo,
Mensagem::ConfirmarRemoverTodas,
Mensagem::RespostaConfirmacao,
Mensagem::NenhumaBibliotecaEncontrada,
Mensagem::BibliotecasEncontradas,
Mensagem::ConfiancaScore,
Mensagem::NenhumaDocumentacaoEncontrada,
Mensagem::TituloDocumentacao,
Mensagem::TituloFontes,
Mensagem::SemConteudoDisponivel,
Mensagem::BuscandoBiblioteca,
Mensagem::ErroDeRede,
Mensagem::ErroDeRedeDocs,
Mensagem::FalhaDesserializar,
Mensagem::RateLimitAtingido,
Mensagem::ErrodoServidor,
Mensagem::ChaveApiInvalida,
Mensagem::Tentativa,
Mensagem::AguardandoRetry,
Mensagem::ErroCaminhoXdg,
Mensagem::FalhaLerConfig,
Mensagem::FalhaTomlInvalido,
Mensagem::FalhaEscreverConfig,
Mensagem::FalhaCriarDiretorio,
Mensagem::NenhumaChaveConfigurada,
Mensagem::FalhaSerializarToml,
Mensagem::ChavesCarregadasEnvVar,
Mensagem::ChavesCarregadasXdg,
Mensagem::FalhaLerXdgContinuando,
Mensagem::IniciandoComChaves,
Mensagem::ChavesCarregadasCompileTime,
Mensagem::FalhaSerializarJson,
Mensagem::FalhaSerializarDocs,
Mensagem::FalhaBuscarBiblioteca,
Mensagem::FalhaLerMetadados,
Mensagem::FalhaDefinirPermissoes,
Mensagem::FalhaBuscarDocumentacao,
Mensagem::FalhaCriarClienteHttp,
Mensagem::SemDocumentacaoDisponivel,
Mensagem::BibliotecaNaoEncontradaApi,
];
for v in &variantes {
let msg_en = en(*v);
let msg_pt = pt(*v);
assert!(
!msg_en.is_empty(),
"EN mensagem vazia para variante {:?}",
v
);
assert!(
!msg_pt.is_empty(),
"PT mensagem vazia para variante {:?}",
v
);
}
}
}