i18n: add Brazilian Portuguese (pt-br) UI translation

This commit is contained in:
RicSchonfelder 2026-10-07 18:46:11 -03:00
parent 608d1b077b
commit c8fde702fe
5 changed files with 778 additions and 48 deletions

View File

@ -44,7 +44,7 @@ macro_rules! page {
}
page!(General {
/// Interface language (`en` or `ja`).
/// Interface language (`en`, `ja` or `pt-br`).
language: String = "en".into(),
/// Levels of Undo (1–99).
undo_levels: u32 = 32,
@ -417,7 +417,7 @@ impl Prefs {
pub fn normalize(&mut self) {
self.version = PREFS_VERSION;
let g = &mut self.general;
if !matches!(g.language.as_str(), "en" | "ja") {
if !matches!(g.language.as_str(), "en" | "ja" | "pt-br") {
g.language = "en".into();
}
g.undo_levels = g.undo_levels.clamp(1, 99);
@ -458,8 +458,8 @@ impl Prefs {
/// Set the value at a dotted key. The key must exist and the value must have its type.
pub fn set(&mut self, key: &str, value: Value) -> Result<(), String> {
if key == "general.language" && !matches!(value.as_str(), Some("en" | "ja")) {
return Err("`general.language` expects `en` or `ja`".into());
if key == "general.language" && !matches!(value.as_str(), Some("en" | "ja" | "pt-br")) {
return Err("`general.language` expects `en`, `ja` or `pt-br`".into());
}
let mut v = serde_json::to_value(&*self).map_err(|e| e.to_string())?;
let ptr = format!("/{}", key.replace('.', "/"));
@ -885,7 +885,7 @@ pub fn pages() -> Vec<Page> {
id: "general",
title: "General",
items: vec![
s("general.language", "Language", Kind::Choice(&[("English", "en"), ("日本語", "ja")]), true),
s("general.language", "Language", Kind::Choice(&[("English", "en"), ("日本語", "ja"), ("Português (Brasil)", "pt-br")]), true),
s("general.undoLevels", "Levels of Undo", Kind::Int(1, 99, ""), true),
s("general.pathPointSize", "Path Point and Handle Size", Kind::Int(3, 20, "px"), true),
s("general.recentItems", "Recent Projects Shown", Kind::Int(1, 30, ""), true),

View File

@ -494,6 +494,10 @@ fn interface_language_is_validated_persisted_and_backward_compatible() {
assert_eq!(s.prefs.general.language, "en");
s.execute("prefs.set", json!({"key": "general.language", "value": "ja"})).unwrap();
assert_eq!(s.execute("prefs.get", json!({"key": "general.language"})).unwrap(), json!("ja"));
// pt-br is registered alongside en and ja, and round-trips through the file like any other.
s.execute("prefs.set", json!({"key": "general.language", "value": "pt-br"})).unwrap();
assert_eq!(s.execute("prefs.get", json!({"key": "general.language"})).unwrap(), json!("pt-br"));
s.execute("prefs.set", json!({"key": "general.language", "value": "ja"})).unwrap();
let saved = store.read(PREFS_FILE).unwrap();
for bad in [json!("fr"), json!(""), json!(17), json!(null)] {
assert!(s.execute("prefs.set", json!({"key": "general.language", "value": bad})).is_err());

View File

@ -1,26 +1,49 @@
//! Japanese menu labels for EffectCraft's own engine tree. Commands remain locale-independent.
//! A command plus its source label distinguishes parameterised entries (e.g. blend modes).
//! Empty command keys identify submenus. Dynamic filenames, user names and effect plug-in names
//! remain verbatim; this catalog only translates the fixed menu tree.
//! Menu labels for EffectCraft's own engine tree in each translated language. Commands remain
//! locale-independent. A command plus its source label distinguishes parameterised entries (e.g.
//! blend modes). Empty command keys identify submenus. Dynamic filenames, user names and effect
//! plug-in names remain verbatim; these catalogs only translate the fixed menu tree.
use crate::EffectcraftApp;
use effectcraft_engine::menus::MenuEntry;
/// The active UI language: the stored preference when it is one of the translated languages,
/// otherwise `en` (English is the source of every label and needs no lookup).
pub(crate) fn language(app: &EffectcraftApp) -> &str {
let lang = app.session.prefs.general.language.as_str();
if matches!(lang, "ja" | "pt-br") { lang } else { "en" }
}
/// The UI is in Japanese: the fast path for the menu builders.
pub(crate) fn japanese(app: &EffectcraftApp) -> bool {
app.session.prefs.general.language == "ja"
language(app) == "ja"
}
/// `source` looked up in `catalog` by (command, English label); the source itself when the pair
/// is absent, so computed labels and user names stay verbatim.
fn lookup<'a>(catalog: &[(&str, &str, &'a str)], command: &str, source: &'a str) -> &'a str {
catalog.iter().find(|(id, en, _)| *id == command && *en == source).map(|(_, _, tr)| *tr).unwrap_or(source)
}
pub(crate) fn label<'a>(app: &EffectcraftApp, command: &str, source: &'a str) -> &'a str {
if japanese(app) { JAPANESE.iter().find(|(id, en, _)| *id == command && *en == source).map(|(_, _, ja)| *ja).unwrap_or(source) } else { source }
match language(app) {
"ja" => lookup(JAPANESE, command, source),
"pt-br" => lookup(PORTUGUESE, command, source),
_ => source,
}
}
pub(crate) fn entry(app: &EffectcraftApp, e: &MenuEntry, shown: String) -> String {
if !japanese(app) {
return shown;
}
// Undo/redo names come from the history, so they are formatted per language here rather
// than looked up in the catalog.
let t = match language(app) {
"ja" => ("取り消し", "取り消しできません", "やり直し", "やり直しできません"),
"pt-br" => ("Desfazer", "Não há nada para desfazer", "Refazer", "Não há nada para refazer"),
_ => return shown,
};
let (undo, no_undo, redo, no_redo) = t;
match e.command.as_str() {
"edit.undo" => app.session.history.undo.last().map(|u| format!("取り消し {}", u.0)).unwrap_or_else(|| "取り消しできません".into()),
"edit.redo" => app.session.history.redo.last().map(|u| format!("やり直し {}", u.0)).unwrap_or_else(|| "やり直しできません".into()),
"edit.undo" => app.session.history.undo.last().map(|u| format!("{undo} {}", u.0)).unwrap_or_else(|| no_undo.into()),
"edit.redo" => app.session.history.redo.last().map(|u| format!("{redo} {}", u.0)).unwrap_or_else(|| no_redo.into()),
"window.panel" => {
let prefix = format!("{}: ", e.label);
if let Some(name) = shown.strip_prefix(&prefix) {
@ -37,16 +60,30 @@ pub(crate) fn entry(app: &EffectcraftApp, e: &MenuEntry, shown: String) -> Strin
}
pub(crate) fn submenu(app: &EffectcraftApp, source: &str, shown: String) -> String {
if japanese(app) {
if source == "Assign Shortcut to Workspace" {
if let Some(name) = shown.strip_prefix("Assign Shortcut to “").and_then(|s| s.strip_suffix("” Workspace")) {
return format!("ワークスペース「{name}」にショートカットを割り当て");
match language(app) {
"ja" => {
if source == "Assign Shortcut to Workspace" {
if let Some(name) = shown.strip_prefix("Assign Shortcut to “").and_then(|s| s.strip_suffix("” Workspace")) {
return format!("ワークスペース「{name}」にショートカットを割り当て");
}
} else if source == "Assign Shortcut to 3D View"
&& let Some(name) = shown.strip_prefix("Assign Shortcut to “").and_then(|s| s.strip_suffix('”'))
{
return format!("「{name}」にショートカットを割り当て");
}
} else if source == "Assign Shortcut to 3D View"
&& let Some(name) = shown.strip_prefix("Assign Shortcut to “").and_then(|s| s.strip_suffix('”'))
{
return format!("「{name}」にショートカットを割り当て");
}
"pt-br" => {
if source == "Assign Shortcut to Workspace" {
if let Some(name) = shown.strip_prefix("Assign Shortcut to “").and_then(|s| s.strip_suffix("” Workspace")) {
return format!("Atribuir atalho à área de trabalho “{name}”");
}
} else if source == "Assign Shortcut to 3D View"
&& let Some(name) = shown.strip_prefix("Assign Shortcut to “").and_then(|s| s.strip_suffix('”'))
{
return format!("Atribuir atalho à visualização 3D “{name}”");
}
}
_ => {}
}
label(app, "", &shown).into()
}
@ -671,6 +708,628 @@ const JAPANESE: &[(&str, &str, &str)] = &[
("file.openDemoProject", "Open Demo Project", "デモプロジェクトを開く"),
];
/// Brazilian Portuguese: the same (command, English label) keys as `JAPANESE`, one entry
/// per fixed menu row of the engine's menu tree.
const PORTUGUESE: &[(&str, &str, &str)] = &[
("", "EffectCraft", "EffectCraft"),
("app.about", "About EffectCraft...", "Sobre o EffectCraft..."),
("", "Settings...", "Configurações..."),
("app.settings", "General...", "Geral..."),
("app.settings", "Startup & Repair...", "Inicialização e reparo..."),
("app.settings", "Project...", "Projeto..."),
("app.settings", "Composition...", "Composição..."),
("app.settings", "Previews...", "Pré-visualizações..."),
("app.settings", "Appearance...", "Aparência..."),
("app.settings", "Grids & Guides...", "Grade e guias..."),
("app.settings", "Labels...", "Rótulos..."),
("app.settings", "Type...", "Texto..."),
("app.settings", "Import...", "Importação..."),
("app.settings", "Export...", "Exportação..."),
("app.settings", "Audio...", "Áudio..."),
("app.settings", "Disk...", "Disco..."),
("app.settings", "Memory & CPU...", "Memória e CPU..."),
("app.settings", "Video...", "Vídeo..."),
("app.settings", "3D...", "3D..."),
("app.settings", "Scripting & Expressions...", "Scripts e expressões..."),
("app.hide", "Hide EffectCraft", "Ocultar EffectCraft"),
("app.hideOthers", "Hide Others", "Ocultar outros"),
("app.showAll", "Show All", "Mostrar tudo"),
("app.quit", "Quit EffectCraft", "Sair do EffectCraft"),
("", "File", "Arquivo"),
("", "New", "Novo"),
("file.newProject", "New Project", "Novo projeto"),
("file.newFromTemplate", "New Project from Template...", "Novo projeto a partir de modelo..."),
("project.newFolder", "New Folder", "Nova pasta"),
("file.open", "Open Project...", "Abrir projeto..."),
("", "Open Recent", "Abrir recentes"),
("file.clearRecent", "Clear Recent Projects", "Limpar projetos recentes"),
("file.close", "Close", "Fechar"),
("file.closeProject", "Close Project", "Fechar projeto"),
("file.save", "Save", "Salvar"),
("", "Save As", "Salvar como"),
("file.saveAs", "Save As...", "Salvar como..."),
("file.saveCopy", "Save a Copy...", "Salvar uma cópia..."),
("file.saveCopyAsXml", "Save a Copy As XML...", "Salvar uma cópia como XML..."),
("templates.saveAs", "Save as Template...", "Salvar como modelo..."),
("file.incrementAndSave", "Increment and Save", "Incrementar e salvar"),
("file.revert", "Revert", "Reverter"),
("", "Import", "Importar"),
("file.import", "File...", "Arquivo..."),
("file.importMultiple", "Multiple Files...", "Vários arquivos..."),
("file.importTimeline", "Adobe Premiere Pro Project...", "Projeto do Adobe Premiere Pro..."),
("file.importPlaceholder", "Placeholder...", "Espaço reservado..."),
("file.importSolid", "Solid...", "Sólido..."),
("file.importLottie", "Lottie...", "Lottie..."),
("file.importVanishingPoint", "Vanishing Point (.vpe)...", "Vanishing Point (.vpe)..."),
("essential.importTemplate", "Essential Graphics Template...", "Modelo de gráficos essenciais..."),
("", "Import Recent Footage", "Importar mídias recentes"),
("file.clearRecentFootage", "Clear Recent Footage", "Limpar mídias recentes"),
("", "Export", "Exportar"),
("renderQueue.add", "Add to Render Queue", "Adicionar à fila de renderização"),
("file.exportTimeline", "Adobe Premiere Pro Project...", "Projeto do Adobe Premiere Pro..."),
("file.exportLottie", "Lottie JSON...", "Lottie JSON..."),
("essential.exportTemplate", "Essential Graphics Template...", "Modelo de gráficos essenciais..."),
("app.find", "Find", "Localizar"),
("layer.addItem", "Add Footage to Comp", "Adicionar mídia à composição"),
("file.newCompFromSelection", "New Comp from Selection...", "Nova composição a partir da seleção..."),
("", "Dependencies", "Dependências"),
("file.collectFiles", "Collect Files...", "Coletar arquivos..."),
("file.consolidateFootage", "Consolidate All Footage", "Consolidar todas as mídias"),
("file.removeUnusedFootage", "Remove Unused Footage", "Remover mídias não utilizadas"),
("file.reduceProject", "Reduce Project", "Reduzir projeto"),
("file.findMissing", "Find Missing Effects", "Localizar efeitos ausentes"),
("file.findMissing", "Find Missing Fonts", "Localizar fontes ausentes"),
("file.findMissing", "Find Missing Footage", "Localizar mídias ausentes"),
("file.watchFolder", "Watch Folder...", "Pasta monitorada..."),
("", "Scripts", "Scripts"),
("file.installScript", "Install Script File...", "Instalar arquivo de script..."),
("file.installScriptUIPanel", "Install ScriptUI Panel...", "Instalar painel ScriptUI..."),
("file.runScript", "Run Script File...", "Executar arquivo de script..."),
("", "Create Proxy", "Criar proxy"),
("file.createProxy", "Still...", "Imagem fixa..."),
("file.createProxy", "Movie...", "Filme..."),
("", "Set Proxy", "Definir proxy"),
("file.setProxy", "File...", "Arquivo..."),
("file.setProxyNone", "None", "Nenhum"),
("", "Interpret Footage", "Interpretar mídia"),
("file.interpretFootage", "Main...", "Principal..."),
("file.interpretProxy", "Proxy...", "Proxy..."),
("file.rememberInterpretation", "Remember Interpretation", "Lembrar interpretação"),
("file.applyInterpretation", "Apply Interpretation", "Aplicar interpretação"),
("", "Replace Footage", "Substituir mídia"),
("file.replaceFootage", "File...", "Arquivo..."),
("file.replaceWithPlaceholder", "Placeholder...", "Espaço reservado..."),
("file.replaceWithSolid", "Solid...", "Sólido..."),
("file.replaceWithLayeredComp", "With Layered Comp", "Por composição com camadas"),
("file.reloadFootage", "Reload Footage", "Recarregar mídia"),
("file.revealInFinder", "Reveal in Finder", "Mostrar no Finder"),
("file.projectSettings", "Project Settings...", "Configurações do projeto..."),
("app.quit", "Exit", "Sair"),
("", "Edit", "Editar"),
("edit.undo", "Undo", "Desfazer"),
("edit.redo", "Redo", "Refazer"),
("", "History", "Histórico"),
("app.commandPalette", "Quick Apply...", "Aplicação rápida..."),
("edit.cut", "Cut", "Recortar"),
("edit.copy", "Copy", "Copiar"),
("edit.copyWithPropertyLinks", "Copy with Property Links", "Copiar com vínculos de propriedade"),
("edit.copyWithRelativePropertyLinks", "Copy with Relative Property Links", "Copiar com vínculos de propriedade relativos"),
("edit.copyExpressionOnly", "Copy Expression Only", "Copiar apenas a expressão"),
("edit.paste", "Paste", "Colar"),
("edit.pasteReversedKeyframes", "Paste Reversed Keyframes", "Colar quadros-chave em ordem inversa"),
("edit.pasteTextMatchFormatting", "Paste Text and Match Formatting", "Colar texto e manter a formatação"),
("edit.pasteTextFormattingOnly", "Paste Text Formatting Only", "Colar apenas a formatação do texto"),
("edit.clear", "Clear", "Limpar"),
("edit.duplicate", "Duplicate", "Duplicar"),
("edit.splitLayer", "Split Layer", "Dividir camada"),
("edit.liftWorkArea", "Lift Work Area", "Retirar área de trabalho"),
("edit.extractWorkArea", "Extract Work Area", "Extrair área de trabalho"),
("edit.selectAll", "Select All", "Selecionar tudo"),
("edit.deselectAll", "Deselect All", "Desmarcar tudo"),
("", "Label", "Rótulo"),
("edit.selectLabelGroup", "Select Label Group", "Selecionar grupo de rótulos"),
("edit.label", "None", "Nenhum"),
("edit.label", "Red", "Vermelho"),
("edit.label", "Yellow", "Amarelo"),
("edit.label", "Aqua", "Aqua"),
("edit.label", "Pink", "Rosa"),
("edit.label", "Lavender", "Lavanda"),
("edit.label", "Peach", "Pêssego"),
("edit.label", "Sea Foam", "Espuma do mar"),
("edit.label", "Blue", "Azul"),
("edit.label", "Green", "Verde"),
("edit.label", "Purple", "Roxo"),
("edit.label", "Orange", "Laranja"),
("edit.label", "Brown", "Marrom"),
("edit.label", "Fuchsia", "Fúcsia"),
("edit.label", "Cyan", "Ciano"),
("edit.label", "Sandstone", "Arenito"),
("edit.label", "Dark Green", "Verde escuro"),
("app.settings", "Edit Label Colors...", "Editar cores dos rótulos..."),
("", "Select Keyframe Label Group", "Selecionar grupo de rótulos de quadros-chave"),
("keys.selectLabelGroup", "On Selected Layers", "Nas camadas selecionadas"),
("keys.selectLabelGroup", "On All Layers", "Em todas as camadas"),
("keys.selectLabelGroup", "Visible Keyframes on Selected Layers", "Quadros-chave visíveis nas camadas selecionadas"),
("keys.selectLabelGroup", "Visible Keyframes on All Layers", "Quadros-chave visíveis em todas as camadas"),
("", "Purge", "Limpar cache"),
("edit.purge", "All Cache...", "Todo o cache..."),
("edit.purge", "All Memory & Disk Cache...", "Toda a memória e o cache do disco..."),
("edit.purge", "All Memory", "Toda a memória"),
("edit.purge", "All Disk Cache...", "Todo o cache do disco..."),
("edit.purge", "All 3D Cache...", "Todo o cache 3D..."),
("edit.purgeUndo", "Undo", "Desfazer"),
("edit.purge", "Image Cache Memory", "Memória de cache de imagens"),
("edit.purge", "Snapshot", "Instantâneo"),
("edit.editOriginal", "Edit Original...", "Editar original..."),
("", "Templates", "Modelos"),
("app.templates", "Render Settings...", "Configurações de renderização..."),
("app.templates", "Output Module...", "Módulo de saída..."),
("app.keyboardShortcuts", "Keyboard Shortcuts", "Atalhos de teclado"),
("", "Preferences", "Preferências"),
("", "Composition", "Composição"),
("comp.new", "New Composition...", "Nova composição..."),
("comp.settings", "Composition Settings...", "Configurações da composição..."),
("comp.setPosterTime", "Set Poster Time", "Definir tempo do pôster"),
("comp.trimToWorkArea", "Trim Comp to Work Area", "Ajustar composição à área de trabalho"),
("comp.cropToRegionOfInterest", "Crop Comp to Region of Interest", "Recortar composição para a região de interesse"),
("comp.cropToLayerBounds", "Crop Comp to Selected Layer(s) Bounds", "Recortar composição aos limites das camadas selecionada(s)"),
("render.addOutputModule", "Add Output Module", "Adicionar módulo de saída"),
("", "Preview", "Pré-visualização"),
("playback.toggle", "Play Current Preview", "Reproduzir pré-visualização atual"),
("playback.cacheWhenIdle", "Cache Frames When Idle", "Armazenar quadros quando ocioso"),
("playback.audio", "Audio", "Áudio"),
("", "Save Frame As", "Salvar quadro como"),
("comp.saveFrameAs", "File...", "Arquivo..."),
("comp.saveFrameAsPsd", "Photoshop Layers...", "Camadas do Photoshop..."),
("comp.saveFrameAsExr", "ProEXR...", "ProEXR..."),
("render.preRender", "Pre-render...", "Pré-renderização..."),
("render.saveCurrentPreview", "Save Current Preview...", "Salvar pré-visualização atual..."),
("comp.openInEssentialGraphics", "Open in Essential Graphics", "Abrir em Gráficos essenciais"),
("", "Responsive Design — Time", "Design responsivo — Tempo"),
("comp.responsiveTime", "Create Intro", "Criar introdução"),
("comp.responsiveTime", "Create Outro", "Criar encerramento"),
("comp.responsiveTime", "Create Protected Region from Work Area", "Criar região protegida a partir da área de trabalho"),
("comp.flowchart", "Composition Flowchart", "Fluxograma da composição"),
("comp.miniFlowchart", "Composition Mini-Flowchart", "Minifluxograma da composição"),
("", "VR", "VR"),
("comp.vr.createEnvironment", "Create VR Environment...", "Criar ambiente VR..."),
("comp.vr.extractCubemap", "Extract Cubemap...", "Extrair cubemapa..."),
("", "Layer", "Camada"),
("layer.newText", "Text", "Texto"),
("layer.newSolid", "Solid...", "Sólido..."),
("layer.newLight", "Light...", "Luz..."),
("layer.newCamera", "Camera...", "Câmera..."),
("layer.newNull", "Null Object", "Objeto nulo"),
("layer.newShape", "Shape Layer", "Camada de forma"),
("layer.newAdjustment", "Adjustment Layer", "Camada de ajuste"),
("layer.newContentAwareFill", "Content-Aware Fill Layer...", "Camada de preenchimento sensível ao conteúdo..."),
("layer.new3dPrimitive", "Cube", "Cubo"),
("layer.new3dPrimitive", "Sphere", "Esfera"),
("layer.new3dPrimitive", "Plane", "Plano"),
("layer.new3dPrimitive", "Torus", "Toro"),
("layer.new3dPrimitive", "Cone", "Cone"),
("layer.new3dPrimitive", "Cylinder", "Cilindro"),
("layer.settings", "Layer Settings...", "Configurações da camada..."),
("layer.openLayer", "Open Layer", "Abrir camada"),
("layer.openSource", "Open Layer Source", "Abrir a fonte da camada"),
("layer.revealInFinder", "Reveal in Finder", "Mostrar no Finder"),
("", "Mask", "Máscara"),
("layer.addMask", "New Mask", "Nova máscara"),
("layer.mask.shape", "Mask Shape...", "Forma da máscara..."),
("layer.mask.set", "Mask Feather...", "Pena da máscara..."),
("layer.mask.set", "Mask Opacity...", "Opacidade da máscara..."),
("layer.mask.set", "Mask Expansion...", "Expansão da máscara..."),
("layer.mask.reset", "Reset Mask", "Redefinir máscara"),
("layer.mask.remove", "Remove Mask", "Remover máscara"),
("layer.mask.removeAll", "Remove All Masks", "Remover todas as máscaras"),
("track.mask", "Track Mask", "Rastrear máscara"),
("", "Mode", "Modo"),
("layer.mask.mode", "None", "Nenhum"),
("layer.mask.mode", "Add", "Adicionar"),
("layer.mask.mode", "Subtract", "Subtrair"),
("layer.mask.mode", "Intersect", "Interseção"),
("layer.mask.mode", "Lighten", "Clarear"),
("layer.mask.mode", "Darken", "Escurecer"),
("layer.mask.mode", "Difference", "Diferença"),
("layer.mask.invert", "Inverted", "Invertida"),
("layer.mask.lock", "Locked", "Bloqueada"),
("", "Motion Blur", "Borrão de movimento"),
("layer.mask.motionBlur", "Same As Layer", "Igual à camada"),
("layer.mask.motionBlur", "On", "Ativado"),
("layer.mask.motionBlur", "Off", "Desativado"),
("", "Feather Falloff", "Decaimento da pena"),
("layer.mask.featherFalloff", "Smooth", "Suavizar"),
("layer.mask.featherFalloff", "Linear", "Linear"),
("layer.mask.unlockAll", "Unlock All Masks", "Desbloquear todas as máscaras"),
("layer.mask.lockOthers", "Lock Other Masks", "Bloquear outras máscaras"),
("layer.mask.hideLocked", "Hide Locked Masks", "Ocultar máscaras bloqueadas"),
("", "Mask and Shape Path", "Caminho da máscara e da forma"),
("path.rotoBezier", "RotoBezier", "RotoBezier"),
("mask.setClosed", "Closed", "Fechado"),
("path.convertToBezier", "Convert To Bezier Path", "Converter em caminho Bézier"),
("path.setFirstVertex", "Set First Vertex", "Definir primeiro vértice"),
("path.freeTransform", "Free Transform Points", "Pontos de transformação livre"),
("", "Quality", "Qualidade"),
("layer.quality", "Best", "Melhor"),
("layer.quality", "Draft", "Rascunho"),
("layer.quality", "Wireframe", "Wireframe"),
("layer.sampling", "Bilinear", "Bilinear"),
("layer.sampling", "Bicubic", "Bicúbico"),
("", "Switches", "Interruptores"),
("layer.hideOtherVideo", "Hide Other Video", "Ocultar os outros vídeos"),
("layer.showAllVideo", "Show All Video", "Mostrar todos os vídeos"),
("layer.unlockAll", "Unlock All Layers", "Desbloquear todas as camadas"),
("layer.expressions", "Enable Expressions", "Ativar expressões"),
("layer.expressions", "Disable Expressions", "Desativar expressões"),
("layer.setSwitch", "Shy", "Escondida"),
("layer.setSwitch", "Lock", "Bloquear"),
("layer.setSwitch", "Audio", "Áudio"),
("layer.setSwitch", "Video", "Vídeo"),
("layer.setSwitch", "Solo", "Solo"),
("layer.setSwitch", "Effect", "Efeito"),
("layer.setSwitch", "Collapse", "Colapsar"),
("layer.setSwitch", "Motion Blur", "Borrão de movimento"),
("layer.setSwitch", "Adjustment Layer", "Camada de ajuste"),
("", "Transform", "Transformar"),
("layer.transform", "Reset", "Redefinir"),
("layer.setTransform", "Anchor Point...", "Ponto de ancoragem..."),
("layer.setTransform", "Position...", "Posição..."),
("layer.setTransform", "Scale...", "Escala..."),
("layer.setTransform", "Orientation...", "Orientação..."),
("layer.setTransform", "Rotation...", "Rotação..."),
("layer.setTransform", "Opacity...", "Opacidade..."),
("layer.transform", "Flip Horizontal", "Espelhar horizontalmente"),
("layer.transform", "Flip Vertical", "Espelhar verticalmente"),
("layer.transform", "Center In View", "Centralizar na visualização"),
("layer.centerAnchor", "Center Anchor Point in Layer Content", "Centralizar ponto de ancoragem no conteúdo da camada"),
("layer.transform", "Fit to Comp", "Ajustar à composição"),
("layer.transform", "Fit to Comp Width", "Ajustar à largura da composição"),
("layer.transform", "Fit to Comp Height", "Ajustar à altura da composição"),
("layer.autoOrient", "Auto-Orient...", "Auto-orientar..."),
("", "Time", "Tempo"),
("layer.enableTimeRemap", "Enable Time Remapping", "Ativar remapeamento de tempo"),
("layer.timeReverse", "Time-Reverse Layer", "Inverter tempo da camada"),
("layer.timeStretch", "Time Stretch...", "Redefinir duração..."),
("layer.freezeFrame", "Freeze Frame", "Congelar quadro"),
("layer.freezeOnLastFrame", "Freeze On Last Frame", "Congelar no último quadro"),
("layer.alignVideoToData", "Align Video to Data", "Alinhar vídeo aos dados"),
("", "Frame Blending", "Blenda de quadros"),
("layer.frameBlending", "Off", "Desativado"),
("layer.frameBlending", "Frame Mix", "Mistura de quadros"),
("layer.frameBlending", "Pixel Motion", "Movimento de pixels"),
("layer.setSwitch", "3D Layer", "Camada 3D"),
("layer.setSwitch", "Guide Layer", "Camada de guia"),
("layer.environment", "Environment Layer", "Camada de ambiente"),
("", "Markers", "Marcadores"),
("layer.addMarker", "Add Marker", "Adicionar marcador"),
("layer.updateMarkersFromSource", "Update Markers From Source", "Atualizar marcadores da origem"),
("layer.markersLock", "Lock Markers", "Bloquear marcadores"),
("layer.deleteAllMarkers", "Delete All Markers", "Excluir todos os marcadores"),
("layer.setSwitch", "Preserve Transparency", "Preservar transparência"),
("", "Blending Mode", "Modo de mesclagem"),
("layer.setBlendMode", "Normal", "Normal"),
("layer.setBlendMode", "Dissolve", "Dissolver"),
("layer.setBlendMode", "Dancing Dissolve", "Dissolver dinâmico"),
("layer.setBlendMode", "Darken", "Escurecer"),
("layer.setBlendMode", "Multiply", "Multiplicar"),
("layer.setBlendMode", "Color Burn", "Superexposição de cores"),
("layer.setBlendMode", "Classic Color Burn", "Superexposição de cores clássica"),
("layer.setBlendMode", "Linear Burn", "Superexposição linear"),
("layer.setBlendMode", "Darker Color", "Cor mais escura"),
("layer.setBlendMode", "Add", "Adição"),
("layer.setBlendMode", "Lighten", "Clarear"),
("layer.setBlendMode", "Screen", "Divisão"),
("layer.setBlendMode", "Color Dodge", "Subexposição de cores"),
("layer.setBlendMode", "Classic Color Dodge", "Subexposição de cores clássica"),
("layer.setBlendMode", "Linear Dodge", "Subexposição linear"),
("layer.setBlendMode", "Lighter Color", "Cor mais clara"),
("layer.setBlendMode", "Overlay", "Sobrepor"),
("layer.setBlendMode", "Soft Light", "Luz suave"),
("layer.setBlendMode", "Hard Light", "Luz forte"),
("layer.setBlendMode", "Linear Light", "Luz linear"),
("layer.setBlendMode", "Vivid Light", "Luz viva"),
("layer.setBlendMode", "Pin Light", "Luz pontual"),
("layer.setBlendMode", "Hard Mix", "Mistura dura"),
("layer.setBlendMode", "Difference", "Diferença"),
("layer.setBlendMode", "Classic Difference", "Diferença clássica"),
("layer.setBlendMode", "Exclusion", "Exclusão"),
("layer.setBlendMode", "Subtract", "Subtrair"),
("layer.setBlendMode", "Divide", "Dividir"),
("layer.setBlendMode", "Hue", "Matiz"),
("layer.setBlendMode", "Saturation", "Saturação"),
("layer.setBlendMode", "Color", "Cor"),
("layer.setBlendMode", "Luminosity", "Luminosidade"),
("layer.setBlendMode", "Stencil Alpha", "Estêncil alfa"),
("layer.setBlendMode", "Stencil Luma", "Estêncil luminância"),
("layer.setBlendMode", "Silhouette Alpha", "Silhueta alfa"),
("layer.setBlendMode", "Silhouette Luma", "Silhueta luminância"),
("layer.setBlendMode", "Alpha Add", "Adição alfa"),
("layer.setBlendMode", "Luminescent Premul", "Pré-multiplicação luminescente"),
("layer.setBlendMode", "Next Blending Mode", "Modo de mesclagem seguinte"),
("layer.setBlendMode", "Previous Blending Mode", "Modo de mesclagem anterior"),
("", "Track Matte", "Track matte"),
("layer.trackMatte", "No Track Matte", "Sem track matte"),
("layer.trackMatte", "Alpha Matte", "Matte alfa"),
("layer.trackMatte", "Alpha Inverted Matte", "Matte alfa invertida"),
("layer.trackMatte", "Luma Matte", "Matte de luminância"),
("layer.trackMatte", "Luma Inverted Matte", "Matte de luminância invertida"),
("layer.trackMatte", "Matte with Layer Above", "Matte com a camada acima"),
("layer.trackMatte", "Matte with Layer Below", "Matte com a camada abaixo"),
("", "Layer Styles", "Estilos de camada"),
("layer.style.options", "Layer Style Options...", "Opções de estilo de camada..."),
("layer.style.convertToEditable", "Convert to Editable Styles", "Converter em estilos editáveis"),
("layer.style.showAll", "Show All", "Mostrar todos"),
("layer.style.removeAll", "Remove All", "Remover todos"),
("layer.style.dropShadow", "Drop Shadow", "Sombra projetada"),
("layer.style.innerShadow", "Inner Shadow", "Sombra interna"),
("layer.style.outerGlow", "Outer Glow", "Brilho externo"),
("layer.style.innerGlow", "Inner Glow", "Brilho interno"),
("layer.style.bevelEmboss", "Bevel and Emboss", "Chanfro e relevo"),
("layer.style.satin", "Satin", "Satin"),
("layer.style.colorOverlay", "Color Overlay", "Sobreposição de cores"),
("layer.style.gradientOverlay", "Gradient Overlay", "Sobreposição de gradiente"),
("layer.style.stroke", "Stroke", "Traçado"),
("path.groupShapes", "Group Shapes", "Agrupar formas"),
("path.ungroupShapes", "Ungroup Shapes", "Desagrupar formas"),
("", "Arrange", "Organizar"),
("layer.arrange", "Bring Layer to Front", "Trazer camada para o topo"),
("layer.arrange", "Bring Layer Forward", "Trazer camada para a frente"),
("layer.arrange", "Send Layer Backward", "Enviar camada para trás"),
("layer.arrange", "Send Layer to Back", "Enviar camada para o fundo"),
("", "Reveal", "Revelar"),
("layer.revealSource", "Reveal Layer Source in Project", "Revelar origem da camada no projeto"),
("comp.flowchart", "Reveal Layer in Project Flowchart", "Revelar camada no fluxograma do projeto"),
("comp.revealInProject", "Reveal Composition in Project", "Revelar composição no projeto"),
("layer.revealExpressionErrors", "Reveal Expression Errors", "Revelar erros de expressão"),
("", "Create", "Criar"),
("layer.create", "Convert to Editable Text", "Converter em texto editável"),
("layer.create", "Create Shapes from Text", "Criar formas a partir do texto"),
("layer.create", "Create Masks from Text", "Criar máscaras a partir do texto"),
("layer.create", "Create Shapes from Vector Layer", "Criar formas a partir da camada vetorial"),
("layer.create", "Create Keyframes from Data", "Criar quadros-chave a partir de dados"),
("layer.create", "Null Controllers for Positional Points", "Controles null para pontos posicionais"),
("layer.create", "Null Controllers for Path Points", "Controles null para pontos do caminho"),
("layer.create", "Nulls Following Path Points", "Nulls seguindo pontos do caminho"),
("layer.create", "Null Tracing Along Path", "Null traçando ao longo do caminho"),
("layer.create", "Create 3D Layer Instance", "Criar instância de camada 3D"),
("", "Camera", "Câmera"),
("camera.fromView", "Create Camera from 3D View", "Criar câmera a partir da visualização 3D"),
("camera.stereoRig", "Create Stereo 3D Rig", "Criar estrutura 3D estéreo"),
("camera.orbitNull", "Create Orbit Null", "Criar null de órbita"),
("camera.fromModel", "Create Cameras from 3D Model", "Criar câmeras a partir de modelo 3D"),
("camera.linkFocusToPoi", "Link Focus Distance to Point of Interest", "Vincular distância de foco ao ponto de interesse"),
("camera.linkFocusToLayer", "Link Focus Distance to Layer", "Vincular distância de foco à camada"),
("camera.setFocusToLayer", "Set Focus Distance to Layer", "Definir distância de foco para a camada"),
("layer.cameraSettings", "Camera Settings...", "Configurações da câmera..."),
("view.reset3DView", "Reset 3D View", "Redefinir visualização 3D"),
("", "Light", "Luz"),
("light.fromModel", "Create Lights from 3D Model", "Criar luzes a partir de modelo 3D"),
("light.controlWithCamera", "Control Light with Camera", "Controlar luz com a câmera"),
("light.environmentBackground", "Create Environment Light Background Layer", "Criar camada de fundo de luz ambiente"),
("", "Material", "Material"),
("material.revealSource", "Reveal Material Source in Project", "Revelar origem do material no projeto"),
("material.reset", "Reset Material", "Redefinir material"),
("material.duplicateAssign", "Duplicate and Assign Material", "Duplicar e atribuir material"),
("layer.autoTrace", "Auto-trace...", "Traçado automático..."),
("layer.precompose", "Pre-compose...", "Pré-compor..."),
("layer.sceneEditDetection", "Scene Edit Detection...", "Detecção de edição de cena..."),
("", "Effect", "Efeito"),
("window.panel", "Effect Controls", "Controles de efeito"),
("effect.applyLast", "Last Effect", "Último efeito"),
("effect.removeAll", "Remove All", "Remover todos"),
("effect.manage", "Manage Effects...", "Gerenciar efeitos..."),
("effect.plugins.load", "Load Effect Plug-in...", "Carregar plug-in de efeito..."),
("", "Animation", "Animação"),
("anim.savePreset", "Save Animation Preset...", "Salvar predefinição de animação..."),
("anim.applyPreset", "Apply Animation Preset...", "Aplicar predefinição de animação..."),
("", "Recent Animation Presets", "Predefinições de animação recentes"),
("anim.clearRecentPresets", "Clear Recent Presets", "Limpar predefinições recentes"),
("anim.browsePresets", "Browse Presets...", "Procurar predefinições..."),
("", "Text Animation Presets", "Predefinições de animação de texto"),
("layer.applyTextPreset", "Typewriter", "Máquina de escrever"),
("layer.applyTextPreset", "Fade Up Characters", "Aparecer caractere a caractere"),
("layer.applyTextPreset", "Bounce In Words", "Entrar quicando nas palavras"),
("layer.applyTextPreset", "Tracking In", "Entrar com espaçamento"),
("layer.applyTextPreset", "Scramble", "Embaralhar"),
("layer.applyTextPreset", "Blur In", "Entrar desfocado"),
("layer.applyTextPreset", "Jitter", "Tremor"),
("layer.applyTextPreset", "Drop In Lines", "Entrar com queda de linhas"),
("anim.addKeyframe", "Add Keyframe", "Adicionar quadro-chave"),
("keys.toggleHold", "Toggle Hold Keyframe", "Alternar quadro-chave de retenção"),
("keys.interpolation", "Keyframe Interpolation...", "Interpolação de quadro-chave..."),
("keys.velocity", "Keyframe Velocity...", "Velocidade do quadro-chave..."),
("", "Keyframe Assistant", "Assistente de quadro-chave"),
("keys.audioToKeyframes", "Convert Audio to Keyframes", "Converter áudio em quadros-chave"),
("prop.convertExpressionToKeyframes", "Convert Expression to Keyframes", "Converter expressão em quadros-chave"),
("keys.easyEase", "Easy Ease", "Easy Ease"),
("keys.easyEaseIn", "Easy Ease In", "Easy Ease In"),
("keys.easyEaseOut", "Easy Ease Out", "Easy Ease Out"),
("keys.exponentialScale", "Exponential Scale", "Escala exponencial"),
("keys.rpfCameraImport", "RPF Camera Import", "Importação de câmera RPF"),
("layer.sequence", "Sequence Layers...", "Sequenciar camadas..."),
("keys.timeReverse", "Time-Reverse Keyframes", "Inverter ordem dos quadros-chave"),
("", "Animate Text", "Animar texto"),
("layer.enablePerChar3D", "Enable Per-character 3D", "Ativar 3D por caractere"),
("layer.addTextAnimator", "Anchor Point", "Ponto de ancoragem"),
("layer.addTextAnimator", "Position", "Posição"),
("layer.addTextAnimator", "Scale", "Escala"),
("layer.addTextAnimator", "Skew", "Distorção"),
("layer.addTextAnimator", "Rotation", "Rotação"),
("layer.addTextAnimator", "Opacity", "Opacidade"),
("layer.addTextAnimator", "All Transform Properties", "Todas as propriedades de transformação"),
("", "Fill Color", "Cor de preenchimento"),
("layer.addTextAnimator", "RGB", "RGB"),
("layer.addTextAnimator", "Hue", "Matiz"),
("layer.addTextAnimator", "Saturation", "Saturação"),
("layer.addTextAnimator", "Brightness", "Brilho"),
("", "Stroke Color", "Cor do traçado"),
("layer.addTextAnimator", "Stroke Width", "Largura do traçado"),
("layer.addTextAnimator", "Tracking", "Tracking"),
("layer.addTextAnimator", "Line Anchor", "Âncora da linha"),
("layer.addTextAnimator", "Line Spacing", "Espaçamento entre linhas"),
("layer.addTextAnimator", "Character Offset", "Deslocamento de caractere"),
("layer.addTextAnimator", "Character Value", "Valor do caractere"),
("layer.addTextAnimator", "Blur", "Desfoque"),
("text.animatorFontAxes", "Variable Font Axes", "Eixos de fonte variável"),
("", "Add Text Selector", "Adicionar seletor de texto"),
("text.addSelector", "Range", "Intervalo"),
("text.addSelector", "Wiggly", "Oscilante"),
("text.addSelector", "Expression", "Expressão"),
("text.removeAllAnimators", "Remove All Text Animators", "Remover todos os animadores de texto"),
("prop.setExpression", "Add Expression", "Adicionar expressão"),
("essential.addProperty", "Add Property to Essential Graphics", "Adicionar propriedade ao Gráficos essenciais"),
("prop.separateDimensions", "Separate Dimensions", "Dimensões separadas"),
("track.camera", "Track Camera", "Rastrear câmera"),
("track.warpStabilizer", "Warp Stabilizer VFX", "Warp Stabilizer VFX"),
("track.motion", "Track Motion", "Rastrear movimento"),
("track.stabilize", "Stabilize Motion", "Estabilizar movimento"),
("track.property", "Track this Property", "Rastrear esta propriedade"),
("anim.reveal", "Reveal Properties with Keyframes", "Revelar propriedades com quadros-chave"),
("anim.reveal", "Reveal Properties with Animation", "Revelar propriedades com animação"),
("anim.reveal", "Reveal All Modified Properties", "Revelar todas as propriedades modificadas"),
("", "View", "Exibir"),
("view.newViewer", "New Viewer", "Nova visualização"),
("view.splitLockedViewer", "Split with New Locked Viewer", "Dividir com nova visualização bloqueada"),
("view.zoomIn", "Zoom In", "Aproximar"),
("view.zoomOut", "Zoom Out", "Afastar"),
("", "Resolution", "Resolução"),
("view.res.full", "Full", "Total"),
("view.res.half", "Half", "Meio"),
("view.res.third", "Third", "Um terço"),
("view.res.quarter", "Quarter", "Um quarto"),
("view.res.custom", "Custom...", "Personalizado..."),
("view.displayColorManagement", "Use Display Color Management", "Usar gerenciamento de cores do monitor"),
("", "Simulate Output", "Simular saída"),
("view.simulateOutput", "No Output Simulation", "Sem simulação de saída"),
("view.simulateOutput", "HDTV (Rec. 709)", "HDTV (Rec. 709)"),
("view.simulateOutput", "SDTV NTSC", "SDTV NTSC"),
("view.simulateOutput", "SDTV PAL", "SDTV PAL"),
("view.simulateOutput", "Legacy Macintosh RGB (Gamma 1.8)", "RGB legado do Macintosh (Gamma 1.8)"),
("view.simulateOutput", "Internet Standard RGB (sRGB)", "RGB padrão da internet (sRGB)"),
("view.simulateOutput", "UHDTV (Rec. 2020)", "UHDTV (Rec. 2020)"),
("view.simulateOutput", "Display P3", "Display P3"),
("view.simulateOutput", "Linear (1.0 Gamma)", "Linear (Gamma 1.0)"),
("view.customRgb", "My Custom RGB...", "Meu RGB personalizado..."),
("view.simulateOutput", "Custom...", "Personalizado..."),
("view.rulers", "Show Rulers", "Mostrar réguas"),
("", "Panel Background Color", "Cor de fundo do painel"),
("view.panelBackground", "Black", "Preto"),
("view.panelBackground", "Dark Gray", "Cinza escuro"),
("view.panelBackground", "Medium Gray (Default)", "Cinza médio (padrão)"),
("view.panelBackground", "Light Gray", "Cinza claro"),
("view.panelBackground", "White", "Branco"),
("view.panelBackground", "Custom", "Personalizado"),
("view.panelBackground", "Select Custom Background Color...", "Selecionar cor de fundo personalizada..."),
("view.guides", "Show Guides", "Mostrar guias"),
("view.snapToGuides", "Snap to Guides", "Encaixar nas guias"),
("view.lockGuides", "Lock Guides", "Bloquear guias"),
("view.addGuide", "Add Guide...", "Adicionar guia..."),
("view.clearGuides", "Clear Guides", "Limpar guias"),
("view.importGuides", "Import Guides...", "Importar guias..."),
("view.exportGuides", "Export Guides...", "Exportar guias..."),
("view.grid", "Show Grid", "Mostrar grade"),
("view.snapToGrid", "Snap to Grid", "Encaixar na grade"),
("view.snapping", "Snapping", "Encaixe"),
("view.options", "View Options...", "Opções de visualização..."),
("view.layerControls", "Show Layer Controls", "Mostrar controles da camada"),
("", "Switch View Layout", "Alternar layout de visualização"),
("view.layout", "1 View", "1 visualização"),
("view.layout", "2 Views", "2 visualizações"),
("view.layout", "4 Views", "4 visualizações"),
("view.shareViewOptions", "Share View Options", "Compartilhar opções de visualização"),
("", "Switch 3D View", "Alternar visualização 3D"),
("view.3d.activeCamera", "Active Camera", "Câmera ativa"),
("view.3d.default", "Default", "Padrão"),
("view.3d.front", "Front", "Frente"),
("view.3d.left", "Left", "Esquerda"),
("view.3d.top", "Top", "Topo"),
("view.3d.back", "Back", "Atrás"),
("view.3d.right", "Right", "Direita"),
("view.3d.bottom", "Bottom", "Base"),
("view.3d.custom1", "Custom View 1", "Visualização personalizada 1"),
("view.3d.custom2", "Custom View 2", "Visualização personalizada 2"),
("view.3d.custom3", "Custom View 3", "Visualização personalizada 3"),
("", "Assign Shortcut to 3D View", "Atribuir atalho à visualização 3D"),
("view.3d.last", "Switch to Last 3D View", "Alternar para a última visualização 3D"),
("view.lookAtSelected", "Look at Selected Layers", "Olhar para as camadas selecionadas"),
("view.lookAtAll", "Look at All Layers", "Olhar para todas as camadas"),
("time.set", "Go to Time...", "Ir para o tempo..."),
("view.fullScreen", "Enter Full Screen", "Entrar em tela cheia"),
("", "Window", "Janela"),
("", "Workspace", "Área de trabalho"),
("window.workspace", "Default", "Inicial"),
("window.workspace", "Review", "Revisão"),
("window.workspace", "Learn", "Aprender"),
("window.workspace", "Small Screen", "Tela pequena"),
("window.workspace", "Standard", "Padrão"),
("window.workspace", "All Panels", "Todos os painéis"),
("window.workspace", "Animation", "Animação"),
("window.workspace", "Color", "Cores"),
("window.workspace", "Effects", "Efeitos"),
("window.workspace", "Essential Graphics", "Gráficos essenciais"),
("window.workspace", "Minimal", "Mínimo"),
("window.workspace", "Motion Tracking", "Rastreamento de movimento"),
("window.workspace", "Paint", "Pintura"),
("window.workspace", "Text", "Texto"),
("window.workspace", "Undocked Panels", "Painéis desacoplados"),
("window.resetWorkspace", "Reset to Saved Layout", "Redefinir para o layout salvo"),
("window.saveWorkspace", "Save Changes to this Workspace", "Salvar alterações nesta área de trabalho"),
("window.saveWorkspaceAs", "Save as New Workspace...", "Salvar como nova área de trabalho..."),
("window.editWorkspaces", "Edit Workspaces...", "Editar áreas de trabalho..."),
("", "Assign Shortcut to Workspace", "Atribuir atalho à área de trabalho"),
("window.panel", "Align", "Alinhar"),
("window.panel", "Audio", "Áudio"),
("window.panel", "Brushes", "Pincéis"),
("window.panel", "Character", "Caractere"),
("window.panel", "Content-Aware Fill", "Preenchimento sensível ao conteúdo"),
("window.panel", "Effects & Presets", "Efeitos e predefinições"),
("window.panel", "Essential Graphics", "Gráficos essenciais"),
("window.panel", "Info", "Informações"),
("help.inAppTutorials", "Learn", "Aprender"),
("window.panel", "Lumetri Scopes", "Escopos Lumetri"),
("window.panel", "Mask Interpolation", "Interpolação de máscara"),
("window.panel", "Media Browser", "Navegador de mídia"),
("window.panel", "Metadata", "Metadados"),
("window.panel", "Motion Sketch", "Esboço de movimento"),
("window.panel", "Paint", "Pintura"),
("window.panel", "Paragraph", "Parágrafo"),
("window.panel", "Preview", "Pré-visualização"),
("window.panel", "Progress", "Progresso"),
("window.panel", "Properties", "Propriedades"),
("window.panel", "Script Console", "Console de script"),
("window.panel", "Smoother", "Suavizador"),
("window.panel", "Tools", "Ferramentas"),
("window.panel", "Tracker", "Rastreador"),
("window.panel", "Wiggler", "Oscilador"),
("window.panel", "Composition", "Composição"),
("window.panel", "Flowchart", "Fluxograma"),
("window.panel", "Footage", "Mídia"),
("window.panel", "Layer", "Camada"),
("window.panel", "Project", "Projeto"),
("window.panel", "Render Queue", "Fila de renderização"),
("window.panel", "Timeline", "Linha do tempo"),
("window.panel", "Create Nulls From Paths", "Criar nulls a partir de caminhos"),
("window.panel", "VR Comp Editor", "Editor de composição VR"),
("", "Help", "Ajuda"),
("help.docs", "EffectCraft Help...", "Ajuda do EffectCraft..."),
("help.docs", "Scripting Help...", "Ajuda de scripting..."),
("help.docs", "Expression Reference...", "Referência de expressões..."),
("help.docs", "Effect Reference...", "Referência de efeitos..."),
("anim.browsePresets", "Animation Presets...", "Predefinições de animação..."),
("app.keyboardShortcuts", "Keyboard Shortcuts...", "Atalhos de teclado..."),
("help.inAppTutorials", "In-App Tutorials...", "Tutoriais no aplicativo..."),
("help.onlineTutorials", "Online Tutorials...", "Tutoriais online..."),
("help.systemReport", "System Compatibility Report...", "Relatório de compatibilidade do sistema..."),
("help.enableLogging", "Enable Logging", "Ativar registro em log"),
("help.revealLogFile", "Reveal Logging File", "Revelar arquivo de log"),
("help.discord", "Join the ArtCraft Discord...", "Entrar no Discord do ArtCraft..."),
("help.reportIssue", "Provide Feedback...", "Enviar comentários..."),
("help.website", "ArtCraft Website", "Site do ArtCraft"),
("help.appPage", "EffectCraft Home Page", "Página inicial do EffectCraft"),
("help.github", "EffectCraft on GitHub", "EffectCraft no GitHub"),
("file.openDemoProject", "Open Demo Project", "Abrir projeto de demonstração"),
];
#[cfg(test)]
mod tests {
use super::*;
@ -699,25 +1358,75 @@ mod tests {
for mac in [false, true] {
keys(&parse(TREE, mac).unwrap(), &mut actual);
}
let mut unique = BTreeSet::new();
for (command, en, ja) in JAPANESE {
let key = (command.to_string(), en.to_string());
assert!(actual.contains(&key), "stale translation: {key:?}");
assert!(unique.insert(key), "duplicate translation: {command} / {en}");
assert!(!ja.is_empty());
}
// All fixed TREE entries are covered; @effects is a generated plug-in catalog,
// whose product/effect names are intentionally left as registered.
for line in TREE.lines() {
let line = line.trim().trim_start_matches("[mac] ").trim_start_matches("[!mac] ");
if line.is_empty() || line == "---" || line.starts_with('@') {
continue;
let mut japanese_keys = BTreeSet::new();
let mut portuguese_keys = BTreeSet::new();
for (catalog, unique) in [(JAPANESE, &mut japanese_keys), (PORTUGUESE, &mut portuguese_keys)] {
for (command, en, translated) in catalog {
let key = (command.to_string(), en.to_string());
assert!(actual.contains(&key), "stale translation: {key:?}");
assert!(unique.insert(key), "duplicate translation: {command} / {en}");
assert!(!translated.is_empty());
}
// All fixed TREE entries are covered; @effects is a generated plug-in catalog,
// whose product/effect names are intentionally left as registered.
for line in TREE.lines() {
let line = line.trim().trim_start_matches("[mac] ").trim_start_matches("[!mac] ");
if line.is_empty() || line == "---" || line.starts_with('@') {
continue;
}
let mut parts = line.split(" | ");
let en = parts.next().unwrap();
let command = parts.next().and_then(|p| p.split_whitespace().next()).unwrap_or("");
assert!(unique.contains(&(command.into(), en.into())), "untranslated fixed entry: {line}");
}
let mut parts = line.split(" | ");
let en = parts.next().unwrap();
let command = parts.next().and_then(|p| p.split_whitespace().next()).unwrap_or("");
assert!(unique.contains(&(command.into(), en.into())), "untranslated fixed entry: {line}");
}
// Every translated language covers exactly the same entries.
assert_eq!(japanese_keys, portuguese_keys, "the languages must translate the same entries");
}
#[test]
fn every_portuguese_entry_keeps_the_english_shape() {
for (command, en, pt) in PORTUGUESE {
assert_eq!(en.contains("..."), pt.contains("..."), "{command} / {en}: {pt}");
let slots = |s: &str| s.chars().filter(|c| *c == '{' || *c == '}').count();
assert_eq!(slots(en), slots(pt), "{command} / {en}: {pt}");
let brackets = |s: &str| s.chars().filter(|c| *c == '(' || *c == ')').count();
assert_eq!(brackets(en), brackets(pt), "{command} / {en}: {pt}");
assert!(!pt.contains(" "), "{command} / {en}: {pt}");
}
}
#[test]
fn pt_br_translates_labels_computed_titles_and_keeps_names_verbatim() {
let mut app = EffectcraftApp::new(effectcraft_engine::Session::default());
app.session.execute("prefs.set", json!({"key":"general.language", "value":"pt-br"})).unwrap();
assert_eq!(language(&app), "pt-br");
assert!(!japanese(&app));
// Fixed menu rows come from the catalog.
assert_eq!(label(&app, "", "Composition"), "Composição");
assert_eq!(label(&app, "", "Effect"), "Efeito");
assert_eq!(label(&app, "comp.new", "New Composition..."), "Nova composição...");
assert_eq!(label(&app, "layer.newText", "Text"), "Texto");
// Untranslated keys and dynamic labels stay verbatim.
assert_eq!(label(&app, "unknown.command", "Text"), "Text");
assert_eq!(
entry(&app, &MenuEntry { label: "Undo".into(), command: "edit.undo".into(), params: json!({}), shortcut: None }, "Undo Comp1".into()),
"Não há nada para desfazer"
);
assert_eq!(
entry(
&app,
&MenuEntry { label: "Layer".into(), command: "window.panel".into(), params: json!({"panel":"layer"}), shortcut: None },
"Layer: File".into()
),
"Camada: File"
);
// The submenu titles that name the current workspace / view.
assert_eq!(
submenu(&app, "Assign Shortcut to Workspace", "Assign Shortcut to “Default” Workspace".into()),
"Atribuir atalho à área de trabalho “Default”"
);
assert_eq!(submenu(&app, "Assign Shortcut to 3D View", "Assign Shortcut to “Top”".into()), "Atribuir atalho à visualização 3D “Top”");
}
#[test]
@ -745,7 +1454,21 @@ mod tests {
assert!(ja.items().iter().any(|i| i.command == "layer.newText" && i.label == "テキスト"));
assert!(ja.items().iter().any(|i| i.command == "keys.easyEase" && i.label == "イージーイーズ"));
app.session.prefs.push_recent("/tmp/File.ecproj");
assert!(crate::native_menu::build(&app).items().iter().any(|i| i.command == "file.openRecent" && i.label == "File.ecproj"));
let ja_recent = crate::native_menu::build(&app);
assert!(ja_recent.items().iter().any(|i| i.command == "file.openRecent" && i.label == "File.ecproj"));
// Portuguese (Brazil): same commands, parameters and shortcuts, translated labels.
app.session.execute("prefs.set", json!({"key":"general.language", "value":"pt-br"})).unwrap();
let pt = crate::native_menu::build(&app);
assert_ne!(crate::native_menu::state_key(&app), state);
// `ja_recent` was captured after the recent-file push, so it matches the pt tree (same commands).
assert_eq!(bindings(&ja_recent), bindings(&pt));
assert_eq!(label(&app, "", "Composition"), "Composição");
assert!(pt.items().iter().any(|i| i.command == "comp.new" && i.label == "Nova composição..."));
assert!(pt.items().iter().any(|i| i.command == "layer.newText" && i.label == "Texto"));
// Easy Ease keeps its English name in every language.
assert!(pt.items().iter().any(|i| i.command == "keys.easyEase" && i.label == "Easy Ease"));
// A dynamic filename is never translated.
assert!(pt.items().iter().any(|i| i.command == "file.openRecent" && i.label == "File.ecproj"));
app.session.execute("prefs.set", json!({"key":"general.language", "value":"en"})).unwrap();
assert_eq!(label(&app, "", "Composition"), "Composition");
}

View File

@ -182,11 +182,14 @@ pub fn build(app: &EffectcraftApp) -> NativeMenu {
if let Some(role) = role {
// Services sits just above Hide, as in every Mac app.
if role == Role::Hide && !services_added {
out.push(NativeNode::Predefined {
role: Role::Services,
label: if crate::i18n::japanese(app) { "サービス" } else { "Services" }.into(),
command: String::new(),
});
let services = if crate::i18n::japanese(app) {
"サービス"
} else if crate::i18n::language(app) == "pt-br" {
"Serviços"
} else {
"Services"
};
out.push(NativeNode::Predefined { role: Role::Services, label: services.into(), command: String::new() });
out.push(NativeNode::Separator);
services_added = true;
}

View File

@ -29,7 +29,7 @@ Commands (CLI, MCP, control channel):
### Settings that change behaviour
- `general.language`: interface language (`en` / `ja`), Settings ▸ General ▸ Language; menu labels change immediately. Native Japanese UI uses installed system fonts; the web host must supply a Japanese font.
- `general.language`: interface language (`en` / `ja` / `pt-br`), Settings ▸ General ▸ Language; menu labels change immediately. Native Japanese UI uses installed system fonts; the web host must supply a Japanese font.
- `general.undoLevels`: Levels of Undo
- `general.pathPointSize`: Path Point and Handle Size
- `general.recentItems`: Recent Projects Shown