trello-cli-unofficial 0.7.3 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "trello-cli-unofficial",
3
3
  "type": "module",
4
- "version": "0.7.3",
4
+ "version": "0.7.4",
5
5
  "private": false,
6
6
  "description": "Unofficial Trello CLI using Power-Up authentication, built with Bun for maximum performance",
7
7
  "author": "Matheus Caiser <matheus.kaiser@gmail.com> (https://www.mrdeveloper.com.br/)",
@@ -41,8 +41,7 @@
41
41
  "install.sh",
42
42
  "main.ts",
43
43
  "scripts",
44
- "src",
45
- "test_trigger.txt"
44
+ "src"
46
45
  ],
47
46
  "engines": {
48
47
  "bun": ">=1.0.0"
@@ -75,6 +74,7 @@
75
74
  "commander": "^14.0.2",
76
75
  "dotenv": "^17.2.3",
77
76
  "fs-extra": "^11.3.2",
77
+ "i18next": "^25.6.1",
78
78
  "inquirer": "^12.10.0"
79
79
  },
80
80
  "devDependencies": {
@@ -0,0 +1,150 @@
1
+ # Internacionalização (i18n)
2
+
3
+ Este projeto usa **i18next** para suportar múltiplos idiomas.
4
+
5
+ ## Idiomas Suportados
6
+
7
+ - 🇧🇷 **Português (pt-BR)** - Padrão para sistemas em Português
8
+ - 🇺🇸 **English (en)** - Padrão para outros sistemas
9
+
10
+ ## Detecção Automática
11
+
12
+ O idioma é detectado automaticamente baseado na variável de ambiente `LANG` do sistema.
13
+
14
+ ## Como Usar
15
+
16
+ ### Importando a função de tradução
17
+
18
+ ```typescript
19
+ import { t } from "@/i18n";
20
+ ```
21
+
22
+ ### Tradução simples
23
+
24
+ ```typescript
25
+ console.log(t("auth.notAuthenticated"));
26
+ // 🇧🇷: "🔐 Você não está autenticado!"
27
+ // 🇺🇸: "🔐 You are not authenticated!"
28
+ ```
29
+
30
+ ### Tradução com interpolação
31
+
32
+ ```typescript
33
+ console.log(t("board.notFound", { name: "My Board" }));
34
+ // 🇧🇷: "❌ Board não encontrado: My Board"
35
+ // 🇺🇸: "❌ Board not found: My Board"
36
+ ```
37
+
38
+ ### Mudando o idioma manualmente
39
+
40
+ ```typescript
41
+ import { changeLanguage } from "@/i18n";
42
+
43
+ changeLanguage("en"); // Muda para Inglês
44
+ changeLanguage("pt-BR"); // Muda para Português
45
+ ```
46
+
47
+ ### Obtendo o idioma atual
48
+
49
+ ```typescript
50
+ import { getCurrentLanguage } from "@/i18n";
51
+
52
+ const currentLang = getCurrentLanguage();
53
+ console.log(currentLang); // 'pt-BR' ou 'en'
54
+ ```
55
+
56
+ ## Estrutura dos Arquivos
57
+
58
+ ```
59
+ src/i18n/
60
+ ├── index.ts # Configuração e funções helper
61
+ └── locales/
62
+ ├── pt-BR.json # Traduções em Português
63
+ └── en.json # Traduções em Inglês
64
+ ```
65
+
66
+ ## Adicionando Novas Traduções
67
+
68
+ 1. Adicione a chave em `src/i18n/locales/pt-BR.json`:
69
+
70
+ ```json
71
+ {
72
+ "myFeature": {
73
+ "message": "Minha mensagem em português"
74
+ }
75
+ }
76
+ ```
77
+
78
+ 2. Adicione a mesma chave em `src/i18n/locales/en.json`:
79
+
80
+ ```json
81
+ {
82
+ "myFeature": {
83
+ "message": "My message in English"
84
+ }
85
+ }
86
+ ```
87
+
88
+ 3. Use no código:
89
+
90
+ ```typescript
91
+ import { t } from "@/i18n";
92
+
93
+ console.log(t("myFeature.message"));
94
+ ```
95
+
96
+ ## Exemplo Completo
97
+
98
+ ```typescript
99
+ import { t } from "@/i18n";
100
+ import inquirer from "inquirer";
101
+
102
+ export class AuthController {
103
+ async setupToken(): Promise<void> {
104
+ const { token } = await inquirer.prompt([
105
+ {
106
+ type: "input",
107
+ name: "token",
108
+ message: t("auth.enterToken"),
109
+ validate: (input) => input.startsWith("ATTA") || t("auth.tokenInvalid"),
110
+ },
111
+ ]);
112
+
113
+ console.log(t("auth.tokenSaved"));
114
+ }
115
+ }
116
+ ```
117
+
118
+ ## Testando Diferentes Idiomas
119
+
120
+ ### Linux/macOS
121
+
122
+ ```bash
123
+ # Testar em Português
124
+ LANG=pt_BR.UTF-8 bun run main.ts
125
+
126
+ # Testar em Inglês
127
+ LANG=en_US.UTF-8 bun run main.ts
128
+ ```
129
+
130
+ ### Windows (PowerShell)
131
+
132
+ ```powershell
133
+ # Testar em Português
134
+ $env:LANG = "pt_BR.UTF-8"; bun run main.ts
135
+
136
+ # Testar em Inglês
137
+ $env:LANG = "en_US.UTF-8"; bun run main.ts
138
+ ```
139
+
140
+ ## Boas Práticas
141
+
142
+ 1. ✅ **Use chaves descritivas**: `auth.notAuthenticated` em vez de `msg1`
143
+ 2. ✅ **Organize por feature**: `auth.*`, `card.*`, `board.*`
144
+ 3. ✅ **Mantenha consistência**: Use os mesmos emojis em ambos os idiomas
145
+ 4. ✅ **Interpole valores dinâmicos**: Use `{{variavel}}` para valores que mudam
146
+ 5. ✅ **Fallback para inglês**: Sempre mantenha o inglês completo como fallback
147
+
148
+ ## TypeScript Support
149
+
150
+ O projeto está configurado com tipos para i18next. O TypeScript vai autocompletar as chaves de tradução disponíveis!
@@ -0,0 +1,61 @@
1
+ import i18next from 'i18next';
2
+ import en from './locales/en.json';
3
+ import ptBR from './locales/pt-BR.json';
4
+
5
+ /**
6
+ * Detecta o idioma do sistema
7
+ * Retorna 'pt-BR' ou 'en' baseado na variável de ambiente LANG
8
+ */
9
+ function detectLanguage(): string {
10
+ const lang = process.env.LANG || process.env.LANGUAGE || 'en_US';
11
+
12
+ // Mapeia common locales para nossos idiomas suportados
13
+ if (lang.startsWith('pt')) {
14
+ return 'pt-BR';
15
+ }
16
+
17
+ return 'en';
18
+ }
19
+
20
+ // Inicializa i18next
21
+ i18next.init({
22
+ lng: detectLanguage(),
23
+ fallbackLng: 'en',
24
+ resources: {
25
+ 'pt-BR': {
26
+ translation: ptBR,
27
+ },
28
+ 'en': {
29
+ translation: en,
30
+ },
31
+ },
32
+ interpolation: {
33
+ escapeValue: false, // React já faz escape, não precisamos aqui
34
+ },
35
+ });
36
+
37
+ /**
38
+ * Função helper para tradução
39
+ * @param key - Chave da tradução (ex: 'auth.notAuthenticated')
40
+ * @param options - Opções de interpolação (ex: { name: 'João' })
41
+ */
42
+ export function t(key: string, options?: Record<string, any>): string {
43
+ return i18next.t(key, options);
44
+ }
45
+
46
+ /**
47
+ * Muda o idioma da aplicação
48
+ * @param language - Código do idioma ('pt-BR' ou 'en')
49
+ */
50
+ export function changeLanguage(language: 'pt-BR' | 'en'): void {
51
+ i18next.changeLanguage(language);
52
+ }
53
+
54
+ /**
55
+ * Retorna o idioma atual
56
+ */
57
+ export function getCurrentLanguage(): string {
58
+ return i18next.language;
59
+ }
60
+
61
+ export default i18next;
@@ -0,0 +1,129 @@
1
+ {
2
+ "common": {
3
+ "yes": "Yes",
4
+ "no": "No",
5
+ "cancel": "Cancel",
6
+ "back": "Back",
7
+ "exit": "Exit",
8
+ "success": "✅ Success!",
9
+ "error": "❌ Error!",
10
+ "loading": "⏳ Loading..."
11
+ },
12
+ "auth": {
13
+ "notAuthenticated": "🔐 You are not authenticated!",
14
+ "enterToken": "Please enter your Trello token:",
15
+ "tokenInvalid": "❌ Invalid token! Token must start with 'ATTA' and be at least 11 characters long.",
16
+ "tokenSaved": "✅ Token saved successfully!",
17
+ "authenticated": "✅ You are already authenticated!"
18
+ },
19
+ "menu": {
20
+ "title": "� Main Menu - Trello CLI Unofficial",
21
+ "boards": "📋 View my boards",
22
+ "explore": "📝 Explore board",
23
+ "create": "➕ Create card",
24
+ "config": "⚙️ Settings",
25
+ "exit": "🚪 Exit",
26
+ "goodbye": "👋 Goodbye!",
27
+ "exploreInDevelopment": "� Explore board - Under development",
28
+ "configTitle": "⚙️ Settings",
29
+ "configToken": "🔑 Configure token",
30
+ "configView": "👀 View current configuration",
31
+ "configReset": "🔄 Reset configuration",
32
+ "configBack": "⬅️ Back",
33
+ "currentConfig": "� Current configuration:",
34
+ "tokenConfigured": "Token configured:",
35
+ "configFile": "Config file: ~/.trello-cli-unofficial/config.json",
36
+ "confirmReset": "Are you sure you want to reset all configuration?",
37
+ "configResetted": "✅ Configuration resetted!",
38
+ "selectBoard": "Select a board:",
39
+ "selectList": "Select a list:",
40
+ "selectCard": "Select a card:"
41
+ },
42
+ "board": {
43
+ "loading": "⏳ Loading boards...",
44
+ "empty": "📋 No boards found.",
45
+ "notFound": "❌ Board \"{{name}}\" not found",
46
+ "lists": "📝 Lists of Board: {{name}}",
47
+ "yourBoards": "📋 Your Trello Boards:"
48
+ },
49
+ "list": {
50
+ "loading": "⏳ Loading lists...",
51
+ "empty": "📝 No lists found in this board.",
52
+ "notFound": "❌ List \"{{listName}}\" not found in board \"{{boardName}}\"",
53
+ "cards": "🃏 Cards of List: {{name}}",
54
+ "boardLists": "📋 Lists from board \"{{boardName}}\":"
55
+ },
56
+ "card": {
57
+ "loading": "⏳ Loading cards...",
58
+ "empty": "🃏 No cards found in this list.",
59
+ "notFound": "❌ Card with ID \"{{cardId}}\" not found",
60
+ "created": "✅ Card created successfully!",
61
+ "updated": "✅ Card updated successfully!",
62
+ "deleted": "✅ Card deleted successfully!",
63
+ "moved": "✅ Card moved successfully!",
64
+ "enterName": "Card name:",
65
+ "enterDescription": "Description (optional):",
66
+ "confirmDelete": "Are you sure you want to delete \"{{name}}\"?",
67
+ "listCards": "📋 Cards from list \"{{listName}}\" in board \"{{boardName}}\":",
68
+ "emptyList": "📭 This list is empty.",
69
+ "selectBoard": "Select board:",
70
+ "selectList": "Select list:",
71
+ "selectCard": "Select a card:",
72
+ "whatToDo": "What would you like to do?",
73
+ "newName": "New name:",
74
+ "newDescription": "New description:",
75
+ "moveToWhichList": "Move to which list?",
76
+ "cardName": "📝 Name: {{name}}",
77
+ "cardUrl": "🔗 URL: {{url}}",
78
+ "cardId": "🆔 ID: {{id}}",
79
+ "movedTo": "➡️ To: {{listName}}",
80
+ "actions": {
81
+ "view": "👁️ View Details",
82
+ "create": "➕ Create Card",
83
+ "edit": "✏️ Edit Card",
84
+ "move": "🔄 Move Card",
85
+ "delete": "🗑️ Delete Card",
86
+ "back": "⬅️ Back"
87
+ }
88
+ },
89
+ "errors": {
90
+ "general": "❌ An error occurred: {{message}}",
91
+ "generic": "❌ Error:",
92
+ "network": "❌ Network error. Check your connection.",
93
+ "invalidInput": "❌ Invalid input: {{field}}",
94
+ "required": "❌ Required field: {{field}}"
95
+ },
96
+ "commands": {
97
+ "boards": {
98
+ "description": "List all available boards",
99
+ "success": "📋 Boards loaded successfully!"
100
+ },
101
+ "lists": {
102
+ "description": "List the lists of a specific board",
103
+ "boardRequired": "❌ Board name is required",
104
+ "success": "📝 Lists loaded successfully!"
105
+ },
106
+ "cards": {
107
+ "description": "List the cards of a specific list",
108
+ "boardRequired": "❌ Board name is required",
109
+ "listRequired": "❌ List name is required",
110
+ "success": "🃏 Cards loaded successfully!"
111
+ },
112
+ "createCard": {
113
+ "description": "Create a new card in a list",
114
+ "nameRequired": "❌ Card name is required",
115
+ "success": "✅ Card '{{name}}' created successfully!"
116
+ },
117
+ "moveCard": {
118
+ "description": "Move a card to another list",
119
+ "cardIdRequired": "❌ Card ID is required",
120
+ "targetListRequired": "❌ Target list ID is required",
121
+ "success": "✅ Card moved successfully!"
122
+ },
123
+ "deleteCard": {
124
+ "description": "Delete a card",
125
+ "cardIdRequired": "❌ Card ID is required",
126
+ "success": "✅ Card deleted successfully!"
127
+ }
128
+ }
129
+ }
@@ -0,0 +1,129 @@
1
+ {
2
+ "common": {
3
+ "yes": "Sim",
4
+ "no": "Não",
5
+ "cancel": "Cancelar",
6
+ "back": "Voltar",
7
+ "exit": "Sair",
8
+ "success": "✅ Sucesso!",
9
+ "error": "❌ Erro!",
10
+ "loading": "⏳ Carregando..."
11
+ },
12
+ "auth": {
13
+ "notAuthenticated": "🔐 Você não está autenticado!",
14
+ "enterToken": "Por favor, insira seu token do Trello:",
15
+ "tokenInvalid": "❌ Token inválido! O token deve começar com 'ATTA' e ter pelo menos 11 caracteres.",
16
+ "tokenSaved": "✅ Token salvo com sucesso!",
17
+ "authenticated": "✅ Você já está autenticado!"
18
+ },
19
+ "menu": {
20
+ "title": "� Menu Principal - Trello CLI Unofficial",
21
+ "boards": "📋 Ver meus quadros",
22
+ "explore": "📝 Explorar quadro",
23
+ "create": "➕ Criar cartão",
24
+ "config": "⚙️ Configurações",
25
+ "exit": "🚪 Sair",
26
+ "goodbye": "👋 Até logo!",
27
+ "exploreInDevelopment": "🚧 Explorar quadro - Em desenvolvimento",
28
+ "configTitle": "⚙️ Configurações",
29
+ "configToken": "� Configurar token",
30
+ "configView": "👀 Ver configuração atual",
31
+ "configReset": "🔄 Resetar configuração",
32
+ "configBack": "⬅️ Voltar",
33
+ "currentConfig": "� Configuração atual:",
34
+ "tokenConfigured": "Token configurado:",
35
+ "configFile": "Arquivo de config: ~/.trello-cli-unofficial/config.json",
36
+ "confirmReset": "Tem certeza que deseja resetar toda a configuração?",
37
+ "configResetted": "✅ Configuração resetada!",
38
+ "selectBoard": "Selecione um board:",
39
+ "selectList": "Selecione uma lista:",
40
+ "selectCard": "Selecione um card:"
41
+ },
42
+ "board": {
43
+ "loading": "⏳ Carregando boards...",
44
+ "empty": "📋 Nenhum board encontrado.",
45
+ "notFound": "❌ Quadro \"{{name}}\" não encontrado",
46
+ "lists": "📝 Listas do Board: {{name}}",
47
+ "yourBoards": "📋 Seus Quadros do Trello:"
48
+ },
49
+ "list": {
50
+ "loading": "⏳ Carregando listas...",
51
+ "empty": "📝 Nenhuma lista encontrada neste board.",
52
+ "notFound": "❌ Lista \"{{listName}}\" não encontrada no quadro \"{{boardName}}\"",
53
+ "cards": "🃏 Cards da Lista: {{name}}",
54
+ "boardLists": "📋 Listas do quadro \"{{boardName}}\":"
55
+ },
56
+ "card": {
57
+ "loading": "⏳ Carregando cards...",
58
+ "empty": "🃏 Nenhum card encontrado nesta lista.",
59
+ "notFound": "❌ Cartão com ID \"{{cardId}}\" não encontrado",
60
+ "created": "✅ Cartão criado com sucesso!",
61
+ "updated": "✅ Cartão atualizado com sucesso!",
62
+ "deleted": "✅ Cartão deletado com sucesso!",
63
+ "moved": "✅ Cartão movido com sucesso!",
64
+ "enterName": "Nome do cartão:",
65
+ "enterDescription": "Descrição (opcional):",
66
+ "confirmDelete": "Tem certeza que deseja deletar \"{{name}}\"?",
67
+ "listCards": "📋 Cartões da lista \"{{listName}}\" no quadro \"{{boardName}}\":",
68
+ "emptyList": "📭 Esta lista está vazia.",
69
+ "selectBoard": "Selecione o quadro:",
70
+ "selectList": "Selecione a lista:",
71
+ "selectCard": "Selecione um cartão:",
72
+ "whatToDo": "O que deseja fazer?",
73
+ "newName": "Novo nome:",
74
+ "newDescription": "Nova descrição:",
75
+ "moveToWhichList": "Mover para qual lista?",
76
+ "cardName": "📝 Nome: {{name}}",
77
+ "cardUrl": "🔗 URL: {{url}}",
78
+ "cardId": "🆔 ID: {{id}}",
79
+ "movedTo": "➡️ Para: {{listName}}",
80
+ "actions": {
81
+ "view": "👁️ Ver Detalhes",
82
+ "create": "➕ Criar Card",
83
+ "edit": "✏️ Editar Card",
84
+ "move": "🔄 Mover Card",
85
+ "delete": "🗑️ Deletar Card",
86
+ "back": "⬅️ Voltar"
87
+ }
88
+ },
89
+ "errors": {
90
+ "general": "❌ Ocorreu um erro: {{message}}",
91
+ "generic": "❌ Erro:",
92
+ "network": "❌ Erro de rede. Verifique sua conexão.",
93
+ "invalidInput": "❌ Entrada inválida: {{field}}",
94
+ "required": "❌ Campo obrigatório: {{field}}"
95
+ },
96
+ "commands": {
97
+ "boards": {
98
+ "description": "Lista todos os boards disponíveis",
99
+ "success": "📋 Boards carregados com sucesso!"
100
+ },
101
+ "lists": {
102
+ "description": "Lista as listas de um board específico",
103
+ "boardRequired": "❌ Nome do board é obrigatório",
104
+ "success": "📝 Listas carregadas com sucesso!"
105
+ },
106
+ "cards": {
107
+ "description": "Lista os cards de uma lista específica",
108
+ "boardRequired": "❌ Nome do board é obrigatório",
109
+ "listRequired": "❌ Nome da lista é obrigatório",
110
+ "success": "🃏 Cards carregados com sucesso!"
111
+ },
112
+ "createCard": {
113
+ "description": "Cria um novo card em uma lista",
114
+ "nameRequired": "❌ Nome do card é obrigatório",
115
+ "success": "✅ Card '{{name}}' criado com sucesso!"
116
+ },
117
+ "moveCard": {
118
+ "description": "Move um card para outra lista",
119
+ "cardIdRequired": "❌ ID do card é obrigatório",
120
+ "targetListRequired": "❌ ID da lista de destino é obrigatório",
121
+ "success": "✅ Card movido com sucesso!"
122
+ },
123
+ "deleteCard": {
124
+ "description": "Deleta um card",
125
+ "cardIdRequired": "❌ ID do card é obrigatório",
126
+ "success": "✅ Card deletado com sucesso!"
127
+ }
128
+ }
129
+ }
@@ -1,6 +1,7 @@
1
1
  import type { ConfigRepository } from '@domain/repositories';
2
2
  import { AuthenticateUserUseCase } from '@application/use-cases';
3
3
  import inquirer from 'inquirer';
4
+ import { t } from '@/i18n';
4
5
 
5
6
  export class AuthController {
6
7
  private authenticateUseCase: AuthenticateUserUseCase;
@@ -12,7 +13,7 @@ export class AuthController {
12
13
  async ensureAuthenticated(): Promise<void> {
13
14
  const result = await this.authenticateUseCase.execute();
14
15
  if (!result.success) {
15
- console.log(result.message);
16
+ console.log(t('auth.notAuthenticated'));
16
17
  await this.setupToken();
17
18
  }
18
19
  }
@@ -22,14 +23,13 @@ export class AuthController {
22
23
  {
23
24
  type: 'input',
24
25
  name: 'token',
25
- message: 'Digite seu token do Trello (ATTA...):',
26
- validate: input =>
27
- input.startsWith('ATTA') || 'Token deve começar com ATTA',
26
+ message: t('auth.enterToken'),
27
+ validate: input => input.startsWith('ATTA') || t('auth.tokenInvalid'),
28
28
  },
29
29
  ]);
30
30
 
31
31
  const result = await this.authenticateUseCase.execute(token);
32
- console.log(result.message);
32
+ console.log(result.success ? t('auth.tokenSaved') : result.message);
33
33
  }
34
34
 
35
35
  async getConfig() {
@@ -1,5 +1,7 @@
1
1
  import type { TrelloRepository } from '@domain/repositories';
2
+
2
3
  import { GetBoardsUseCase, GetCardsUseCase, GetListsUseCase } from '@application/use-cases';
4
+ import { t } from '@/i18n';
3
5
 
4
6
  export class BoardController {
5
7
  private getBoardsUseCase: GetBoardsUseCase;
@@ -15,7 +17,7 @@ export class BoardController {
15
17
  async showBoards(): Promise<void> {
16
18
  const boards = await this.getBoardsUseCase.execute();
17
19
 
18
- console.log('📋 Seus Quadros do Trello:');
20
+ console.log(t('board.yourBoards'));
19
21
  boards.forEach((board, index) => {
20
22
  console.log(`${index + 1}. ${board.name}`);
21
23
  console.log(` 🔗 ${board.url}`);
@@ -28,12 +30,12 @@ export class BoardController {
28
30
  const board = boards.find(b => b.name === boardName);
29
31
 
30
32
  if (!board) {
31
- throw new Error(`Quadro "${boardName}" não encontrado`);
33
+ throw new Error(t('board.notFound', { name: boardName }));
32
34
  }
33
35
 
34
36
  const lists = await this.getListsUseCase.execute(board.id);
35
37
 
36
- console.log(`📋 Listas do quadro "${boardName}":`);
38
+ console.log(t('list.boardLists', { boardName }));
37
39
  lists.forEach((list, index) => {
38
40
  console.log(`${index + 1}. ${list.name}`);
39
41
  console.log(` 🆔 ${list.id}\n`);
@@ -45,23 +47,21 @@ export class BoardController {
45
47
  const board = boards.find(b => b.name === boardName);
46
48
 
47
49
  if (!board) {
48
- throw new Error(`Quadro "${boardName}" não encontrado`);
50
+ throw new Error(t('board.notFound', { name: boardName }));
49
51
  }
50
52
 
51
53
  const lists = await this.getListsUseCase.execute(board.id);
52
54
  const list = lists.find(l => l.name === listName);
53
55
 
54
56
  if (!list) {
55
- throw new Error(
56
- `Lista "${listName}" não encontrada no quadro "${boardName}"`,
57
- );
57
+ throw new Error(t('list.notFound', { listName, boardName }));
58
58
  }
59
59
 
60
60
  const cards = await this.getCardsUseCase.execute(list.id);
61
61
 
62
- console.log(`📋 Cartões da lista "${listName}" no quadro "${boardName}":`);
62
+ console.log(t('card.listCards', { listName, boardName }));
63
63
  if (cards.length === 0) {
64
- console.log('📭 Esta lista está vazia.');
64
+ console.log(t('card.emptyList'));
65
65
  return;
66
66
  }
67
67