Coverage for src / moai_adk / cli / commands / language.py: 0.00%

161 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-20 20:52 +0900

1"""Language management commands for MoAI-ADK. 

2 

3Provides commands for language configuration, template processing, 

4and multilingual content generation. 

5""" 

6 

7import json 

8from pathlib import Path 

9 

10import click 

11from rich.console import Console 

12from rich.table import Table 

13 

14from ...core.claude_integration import ClaudeCLIIntegration 

15from ...core.language_config import ( 

16 LANGUAGE_CONFIG, 

17 get_all_supported_codes, 

18 get_native_name, 

19 get_optimal_model, 

20) 

21from ...core.template_engine import TemplateEngine 

22 

23console = Console() 

24 

25 

26@click.group() 

27def language(): 

28 """Language management and multilingual support.""" 

29 pass 

30 

31 

32@language.command() 

33@click.option("--json-output", is_flag=True, help="Output as JSON") 

34def list(json_output): 

35 """List all supported languages.""" 

36 if json_output: 

37 console.print(json.dumps(LANGUAGE_CONFIG, indent=2, ensure_ascii=False)) 

38 return 

39 

40 table = Table(title="Supported Languages") 

41 table.add_column("Code", style="cyan", no_wrap=True) 

42 table.add_column("English Name", style="green") 

43 table.add_column("Native Name", style="yellow") 

44 table.add_column("Family", style="blue") 

45 

46 for code, info in LANGUAGE_CONFIG.items(): 

47 table.add_row(code, info["name"], info["native_name"], info["family"]) 

48 

49 console.print(table) 

50 

51 

52@language.command() 

53@click.argument("language_code") 

54@click.option("--detail", is_flag=True, help="Show detailed information") 

55def info(language_code, detail): 

56 """Show information about a specific language.""" 

57 lang_info = LANGUAGE_CONFIG.get(language_code.lower()) 

58 

59 if not lang_info: 

60 console.print(f"[red]Language code '{language_code}' not found.[/red]") 

61 console.print(f"Available codes: {', '.join(get_all_supported_codes())}") 

62 return 

63 

64 console.print("[bold]Language Information:[/bold]") 

65 console.print(f"Code: {language_code}") 

66 console.print(f"English Name: {lang_info['name']}") 

67 console.print(f"Native Name: {lang_info['native_name']}") 

68 console.print(f"Family: {lang_info['family']}") 

69 

70 if detail: 

71 optimal_model = get_optimal_model(language_code) 

72 console.print(f"Optimal Claude Model: {optimal_model}") 

73 

74 

75@language.command() 

76@click.argument("template_path", type=click.Path(exists=True)) 

77@click.argument("variables_file", type=click.Path(exists=True)) 

78@click.option("--output", "-o", type=click.Path(), help="Output file path") 

79@click.option("--language", "-l", help="Target language code") 

80def render_template(template_path, variables_file, output, language): 

81 """Render template with variables and language support.""" 

82 try: 

83 # Load variables 

84 with open(variables_file, "r", encoding="utf-8") as f: 

85 variables = json.load(f) 

86 

87 # Add language info if specified 

88 if language: 

89 variables["CONVERSATION_LANGUAGE"] = language 

90 variables["CONVERSATION_LANGUAGE_NAME"] = get_native_name(language) 

91 

92 # Render template 

93 template_engine = TemplateEngine() 

94 template_path_obj = Path(template_path) 

95 output_path_obj = Path(output) if output else None 

96 

97 rendered = template_engine.render_file( 

98 template_path_obj, variables, output_path_obj 

99 ) 

100 

101 if not output: 

102 console.print("[bold]Rendered Template:[/bold]") 

103 console.print(rendered) 

104 else: 

105 console.print(f"[green]Template rendered to: {output}[/green]") 

106 

107 except Exception as e: 

108 console.print(f"[red]Error rendering template: {e}[/red]") 

109 

110 

111@language.command() 

112@click.argument("base_description") 

113@click.option("--target-languages", "-t", help="Comma-separated target language codes") 

114@click.option("--output", "-o", type=click.Path(), help="Output JSON file") 

115def translate_descriptions(base_description, target_languages, output): 

116 """Generate multilingual descriptions using Claude CLI.""" 

117 try: 

118 if target_languages: 

119 languages = [lang.strip() for lang in target_languages.split(",")] 

120 else: 

121 languages = ["en", "ko", "ja", "es", "fr", "de"] 

122 

123 claude_integration = ClaudeCLIIntegration() 

124 descriptions = claude_integration.generate_multilingual_descriptions( 

125 {"base": base_description}, languages 

126 ) 

127 

128 if output: 

129 with open(output, "w", encoding="utf-8") as f: 

130 json.dump(descriptions, f, indent=2, ensure_ascii=False) 

131 console.print(f"[green]Descriptions saved to: {output}[/green]") 

132 else: 

133 console.print("[bold]Multilingual Descriptions:[/bold]") 

134 console.print(json.dumps(descriptions, indent=2, ensure_ascii=False)) 

135 

136 except Exception as e: 

137 console.print(f"[red]Error generating descriptions: {e}[/red]") 

138 

139 

140@language.command() 

141@click.argument("prompt_template") 

142@click.option( 

143 "--variables", "-v", type=click.Path(exists=True), help="Variables JSON file" 

144) 

145@click.option("--language", "-l", help="Target language code") 

146@click.option( 

147 "--output-format", default="json", help="Output format (text, json, stream-json)" 

148) 

149@click.option("--dry-run", is_flag=True, help="Show command without executing") 

150def execute(prompt_template, variables, language, output_format, dry_run): 

151 """Execute Claude CLI with template variables and language support.""" 

152 try: 

153 # Load or create variables 

154 template_vars = {} 

155 if variables: 

156 with open(variables, "r", encoding="utf-8") as f: 

157 template_vars = json.load(f) 

158 

159 if language: 

160 template_vars["CONVERSATION_LANGUAGE"] = language 

161 template_vars["CONVERSATION_LANGUAGE_NAME"] = get_native_name(language) 

162 

163 # Process template 

164 template_engine = TemplateEngine() 

165 processed_prompt = template_engine.render_string(prompt_template, template_vars) 

166 

167 if dry_run: 

168 console.print("[bold]Dry Run - Command that would be executed:[/bold]") 

169 console.print( 

170 f"claude --print --output-format {output_format} '{processed_prompt}'" 

171 ) 

172 console.print("[bold]Variables used:[/bold]") 

173 console.print(json.dumps(template_vars, indent=2, ensure_ascii=False)) 

174 return 

175 

176 # Execute with Claude integration 

177 claude_integration = ClaudeCLIIntegration() 

178 result = claude_integration.process_template_command( 

179 prompt_template, template_vars, print_mode=True, output_format=output_format 

180 ) 

181 

182 if result["success"]: 

183 console.print("[green]✓ Command executed successfully[/green]") 

184 if output_format == "json" and result["stdout"]: 

185 try: 

186 output_data = json.loads(result["stdout"]) 

187 console.print(json.dumps(output_data, indent=2, ensure_ascii=False)) 

188 except json.JSONDecodeError: 

189 console.print(result["stdout"]) 

190 else: 

191 console.print(result["stdout"]) 

192 else: 

193 console.print("[red]✗ Command execution failed[/red]") 

194 if "error" in result: 

195 console.print(f"Error: {result['error']}") 

196 if result["stderr"]: 

197 console.print(f"Stderr: {result['stderr']}") 

198 

199 except Exception as e: 

200 console.print(f"[red]Error executing command: {e}[/red]") 

201 

202 

203@language.command() 

204@click.argument("config_file", type=click.Path(exists=True)) 

205@click.option( 

206 "--validate-languages", is_flag=True, help="Validate language codes in config" 

207) 

208def validate_config(config_file, validate_languages): 

209 """Validate language configuration in MoAI-ADK config file.""" 

210 try: 

211 with open(config_file, "r", encoding="utf-8") as f: 

212 config = json.load(f) 

213 

214 console.print(f"[bold]Validating config: {config_file}[/bold]") 

215 

216 # Basic structure validation 

217 if "language" not in config: 

218 console.print("[yellow]⚠ No 'language' section found in config[/yellow]") 

219 else: 

220 lang_config = config["language"] 

221 if not isinstance(lang_config, dict): 

222 console.print("[red]✗ 'language' section must be an object[/red]") 

223 else: 

224 console.print("[green]✓ Language section structure is valid[/green]") 

225 

226 # Check conversation_language 

227 conv_lang = lang_config.get("conversation_language") 

228 if conv_lang: 

229 if conv_lang in get_all_supported_codes(): 

230 console.print( 

231 f"[green]✓ conversation_language '{conv_lang}' is supported[/green]" 

232 ) 

233 else: 

234 console.print( 

235 f"[red]✗ conversation_language '{conv_lang}' is not supported[/red]" 

236 ) 

237 else: 

238 console.print( 

239 "[yellow]⚠ No conversation_language specified[/yellow]" 

240 ) 

241 

242 # Check conversation_language_name 

243 conv_lang_name = lang_config.get("conversation_language_name") 

244 if conv_lang_name and conv_lang: 

245 expected_name = get_native_name(conv_lang) 

246 if conv_lang_name == expected_name: 

247 console.print( 

248 "[green]✓ conversation_language_name matches[/green]" 

249 ) 

250 else: 

251 console.print( 

252 f"[yellow]⚠ conversation_language_name '{conv_lang_name}' doesn't match expected '{expected_name}'[/yellow]" 

253 ) 

254 

255 if validate_languages: 

256 # Scan entire config for language codes 

257 config_str = json.dumps(config) 

258 found_codes = [] 

259 for code in get_all_supported_codes(): 

260 if code in config_str: 

261 found_codes.append(code) 

262 

263 if found_codes: 

264 console.print( 

265 f"[blue]Found language codes in config: {', '.join(found_codes)}[/blue]" 

266 ) 

267 

268 except Exception as e: 

269 console.print(f"[red]Error validating config: {e}[/red]")