From 92d3b1bd5061f579f5ea85150b731960eb6fdad4 Mon Sep 17 00:00:00 2001 From: felipepimentab Date: Wed, 2 Sep 2026 10:39:12 -0300 Subject: [PATCH 1/2] feat: add Brazilian Portuguese localization --- .../pt-BR.lproj/WidgetConfiguration.strings | 101 ++++++ .../pt-BR.lproj/Localizable.strings | 308 ++++++++++++++++++ .../pt-BR.lproj/Localizable.stringsdict | 150 +++++++++ app/dime.xcodeproj/project.pbxproj | 31 ++ 4 files changed, 590 insertions(+) create mode 100644 app/ExpenditureWidget/pt-BR.lproj/WidgetConfiguration.strings create mode 100644 app/Localizations/pt-BR.lproj/Localizable.strings create mode 100644 app/Localizations/pt-BR.lproj/Localizable.stringsdict diff --git a/app/ExpenditureWidget/pt-BR.lproj/WidgetConfiguration.strings b/app/ExpenditureWidget/pt-BR.lproj/WidgetConfiguration.strings new file mode 100644 index 0000000..8a75572 --- /dev/null +++ b/app/ExpenditureWidget/pt-BR.lproj/WidgetConfiguration.strings @@ -0,0 +1,101 @@ +"0S0EUc" = "Ano"; + +"1RQFcH-DGV4UF" = "Só para confirmar, você queria ‘Ano’?"; + +"1RQFcH-P3jTfV" = "Só para confirmar, você queria ‘Dia’?"; + +"1RQFcH-czRaIN" = "Só para confirmar, você queria ‘Mês’?"; + +"1RQFcH-kmTpVR" = "Só para confirmar, você queria ‘Semana’?"; + +"1hyBus" = "Configuração do widget de orçamento"; + +"6cmEVw" = "Saldo total"; + +"7qqVwM" = "Configuração do widget de transações recentes"; + +"AUFESp" = "Configuração do widget de análises"; + +"Bhhyri" = "Semana"; + +"DGV4UF" = "Ano"; + +"Eh1Ri6-DGV4UF" = "Há ${count} opções correspondentes a ‘Ano’."; + +"Eh1Ri6-P3jTfV" = "Há ${count} opções correspondentes a ‘Dia’."; + +"Eh1Ri6-czRaIN" = "Há ${count} opções correspondentes a ‘Mês’."; + +"Eh1Ri6-kmTpVR" = "Há ${count} opções correspondentes a ‘Semana’."; + +"H5x4UP-6cmEVw" = "Há ${count} opções correspondentes a ‘Saldo total’."; + +"H5x4UP-YcEMd2" = "Há ${count} opções correspondentes a ‘Total de despesas’."; + +"H5x4UP-mM5dSQ" = "Há ${count} opções correspondentes a ‘Receita total’."; + +"Ho0bYB" = "Período das análises"; + +"JAwE5i-ZNKfLE" = "Há ${count} opções correspondentes a ‘Despesas’."; + +"JAwE5i-qZQiJe" = "Há ${count} opções correspondentes a ‘Receita’."; + +"OIASDL-ZNKfLE" = "Só para confirmar, você queria ‘Despesas’?"; + +"OIASDL-qZQiJe" = "Só para confirmar, você queria ‘Receita’?"; + +"P3jTfV" = "Dia"; + +"QBbDzN" = "Receita"; + +"QEV7yi" = "Widget de orçamento"; + +"XTKaD2" = "Mês"; + +"XlOqjc" = "Período"; + +"YQ1Wnb" = "Período"; + +"YcEMd2" = "Total de despesas"; + +"ZNKfLE" = "Despesas"; + +"Za14jD" = "Orçamento"; + +"czRaIN" = "Mês"; + +"e1simm-DGV4UF" = "Há ${count} opções correspondentes a ‘Ano’."; + +"e1simm-P3jTfV" = "Há ${count} opções correspondentes a ‘Dia’."; + +"e1simm-czRaIN" = "Há ${count} opções correspondentes a ‘Mês’."; + +"e1simm-kmTpVR" = "Há ${count} opções correspondentes a ‘Semana’."; + +"eeVlqE" = "Tipo de análise"; + +"gBvLZr" = "Período"; + +"iDgY1G-DGV4UF" = "Só para confirmar, você queria ‘Ano’?"; + +"iDgY1G-P3jTfV" = "Só para confirmar, você queria ‘Dia’?"; + +"iDgY1G-czRaIN" = "Só para confirmar, você queria ‘Mês’?"; + +"iDgY1G-kmTpVR" = "Só para confirmar, você queria ‘Semana’?"; + +"kmTpVR" = "Semana"; + +"mM5dSQ" = "Receita total"; + +"ocCluD-6cmEVw" = "Só para confirmar, você queria ‘Saldo total’?"; + +"ocCluD-YcEMd2" = "Só para confirmar, você queria ‘Total de despesas’?"; + +"ocCluD-mM5dSQ" = "Só para confirmar, você queria ‘Receita total’?"; + +"qZQiJe" = "Receita"; + +"wfQ8VF" = "Tipo de análise"; + +"zlEWQ3" = "Tipo de análise"; diff --git a/app/Localizations/pt-BR.lproj/Localizable.strings b/app/Localizations/pt-BR.lproj/Localizable.strings new file mode 100644 index 0000000..5ec2554 --- /dev/null +++ b/app/Localizations/pt-BR.lproj/Localizable.strings @@ -0,0 +1,308 @@ +/* + Localizable.strings + dime + + Brazilian Portuguese localization +*/ + +// welcome sheet + +"dime_name" = "Dime"; +"welcome_header_1" = "Acompanhe suas finanças"; +"welcome_subtitle_1" = "Adicione novos lançamentos de forma simples e direta"; +"welcome_header_2" = "Analise suas despesas"; +"welcome_subtitle_2" = "Entenda seus gastos ao longo de diferentes períodos"; +"welcome_header_3" = "Mantenha-se dentro do orçamento"; +"welcome_subtitle_3" = "Defina metas por categoria e período para limitar seus gastos"; +"Get Started" = "Começar"; + +// categories + +"Categories" = "Categorias"; +"no_income_categories" = "Nenhuma categoria de receita encontrada. Toque no botão 'Nova' para adicionar uma"; +"no_expense_categories" = "Nenhuma categoria de despesa encontrada. Toque no botão 'Nova' para adicionar uma"; +"Suggestions Hidden" = "Sugestões ocultas"; +"Successfully Edited" = "Editado com sucesso"; +"INCOME CATEGORIES" = "CATEGORIAS DE RECEITA"; +"EXPENSE CATEGORIES" = "CATEGORIAS DE DESPESA"; +"SUGGESTED" = "SUGESTÕES"; +"Income Category" = "Categoria de receita"; +"Expense Category" = "Categoria de despesa"; +"New" = "Nova"; +"Expense" = "Despesa"; +"Income" = "Receita"; +"Delete" = "Excluir"; +"Cancel" = "Cancelar"; +"Delete '%@'?" = "Excluir '%@'?"; +"This action cannot be undone, and all %@ transactions would be deleted." = "Esta ação não pode ser desfeita, e todas as transações de %@ serão excluídas."; +"Category Name" = "Nome da categoria"; +"Missing Name" = "Nome ausente"; +"Missing Emoji" = "Emoji ausente"; +"Incomplete Entry" = "Lançamento incompleto"; +"Missing Information" = "Informações ausentes"; +"This action cannot be undone." = "Esta ação não pode ser desfeita."; +"Duplicate Found" = "Item duplicado"; +"Please Edit Accordingly" = "Edite conforme necessário"; +"Please Change Emoji" = "Altere o emoji"; +"Please Change Name" = "Altere o nome"; +"Food" = "Alimentação"; +"Transport" = "Transporte"; +"Rent" = "Aluguel"; +"Subscriptions" = "Assinaturas"; +"Groceries" = "Supermercado"; +"Family" = "Família"; +"Utilities" = "Contas"; +"Fashion" = "Moda"; +"Healthcare" = "Saúde"; +"Pets" = "Animais de estimação"; +"Sneakers" = "Tênis"; +"Gifts" = "Presentes"; +"Paycheck" = "Salário"; +"Allowance" = "Mesada"; +"Part-Time" = "Trabalho de meio período"; +"Investments" = "Investimentos"; +"Tips" = "Gorjetas"; +"Successfully Added" = "Adicionado com sucesso"; + +// new transaction page +"Today, %@" = "Hoje, %@"; +"Add Note" = "Adicionar observação"; +"Category" = "Categoria"; +"none" = "nenhuma"; +"daily" = "diariamente"; +"weekly" = "semanalmente"; +"monthly" = "mensalmente"; +"custom" = "personalizado"; +"Custom Interval" = "Intervalo personalizado"; +"Repeats every" = "Repete a cada"; +"days" = "dias"; +"weeks" = "semanas"; +"months" = "meses"; +"Edit" = "Editar"; +"Delete Expense?" = "Excluir despesa?"; +"Missing Amount" = "Valor ausente"; +"Missing Category" = "Categoria ausente"; +"transaction-view-income-picker" = "Receita"; + +// log view +"Your Log is Empty" = "Seu histórico está vazio"; +"Press the plus button to add your first entry" = "Toque no botão de mais para adicionar seu primeiro lançamento"; +"Search entry by note" = "Buscar lançamento pela observação"; +"all entries" = "todos os lançamentos"; +"by type" = "por tipo"; +"by day" = "por dia"; +"by week" = "por semana"; +"by month" = "por mês"; +"by category" = "por categoria"; +"recurring" = "recorrentes"; +"filter-tag-type" = "por tipo"; +"filter-tag-day" = "por dia"; +"filter-tag-category" = "por categoria"; +"filter-tag-week" = "por semana"; +"filter-tag-month" = "por mês"; +"filter-tag-recurring" = "recorrentes"; +"filter-picker-income" = "Receita"; +"filter-tag-upcoming" = "futuros"; +"Total Spent" = "Total gasto"; +"Total Income" = "Receita total"; +"Net Total" = "Saldo total"; +"Net total" = "Saldo total"; +"Spent" = "Gasto"; +"Earned" = "Recebido"; +"today" = "hoje"; +"this week" = "esta semana"; +"this month" = "este mês"; +"this year" = "este ano"; +"No entries found." = "Nenhum lançamento encontrado."; +"Try a different search query!" = "Tente fazer outra busca!"; +"yesterday" = "ontem"; + + +// insights +"Insights" = "Análises"; +"Analyse Your Expenditure" = "Analise suas despesas"; +"As transactions start piling up" = "Conforme suas transações forem se acumulando"; +"Expenses" = "Despesas"; +"SPENT/DAY" = "GASTO/DIA"; +"INCOME/DAY" = "RECEITA/DIA"; +"SPENT/MTH" = "GASTO/MÊS"; +"INCOME/MTH" = "RECEITA/MÊS"; +"AVG/DAY" = "MÉDIA/DIA"; +"AVG/MTH" = "MÉDIA/MÊS"; +"That's all, buddy." = "Isso é tudo."; +"Into the unknown." = "Rumo ao desconhecido."; +"week" = "semana"; +"month" = "mês"; +"year" = "ano"; +"Jan" = "Jan"; +"Apr" = "Abr"; +"Jul" = "Jul"; +"Oct" = "Out"; + +// budgets +"Budgets" = "Orçamentos"; +"Budget Your Finances" = "Planeje suas finanças"; +"Link budgets to categories and set appropriate expenditure goals" = "Vincule orçamentos a categorias e defina metas de gastos adequadas"; +"No Budgets Found" = "Nenhum orçamento encontrado"; +"Add your first budget today!" = "Adicione seu primeiro orçamento hoje!"; +"category budget" = "orçamento por categoria"; +"overall budget" = "orçamento geral"; +"new" = "novo"; +"New Budget" = "Novo orçamento"; +"Overall Budget" = "Orçamento geral"; +"Select a category" = "Selecione uma categoria"; +"Begin by linking this budget to an existing category." = "Comece vinculando este orçamento a uma categoria existente."; +"Choose a time frame" = "Escolha um período"; +"The budget will periodically refresh according to your preference." = "O orçamento será renovado periodicamente de acordo com sua preferência."; +"Continue" = "Continuar"; +"Pick a start date" = "Escolha uma data de início"; +"Daily" = "Diário"; +"Weekly" = "Semanal"; +"Monthly" = "Mensal"; +"Yearly" = "Anual"; +"Which day of the %@ do you want your budget to start from?" = "Em qual dia de %@ você quer iniciar seu orçamento?"; +"weekNewBudget" = "cada semana"; +"monthNewBudget" = "cada mês"; +"yearNewBudget" = "cada ano"; +"Sunday" = "Domingo"; +"Monday" = "Segunda-feira"; +"Tuesday" = "Terça-feira"; +"Wednesday" = "Quarta-feira"; +"Thursday" = "Quinta-feira"; +"Friday" = "Sexta-feira"; +"Saturday" = "Sábado"; +"Start of month" = "Início do mês"; +"%@ of month" = "Dia %@ do mês"; +"Set budget amount" = "Defina o valor do orçamento"; +"Feel free to edit this in the future." = "Você poderá editar isso depois."; +"%@ SPENT" = "%@ GASTO"; +"left %@" = "restante: %@"; +"over %@" = "excedido: %@"; +"left today" = "restante hoje"; +"left on %@" = "restante em %@"; +"over today" = "excedido hoje"; +"over on %@" = "excedido em %@"; +"left this week" = "restante esta semana"; +"over this week" = "excedido esta semana"; +"left each day" = "restante por dia"; +"left this month" = "restante este mês"; +"over this month" = "excedido este mês"; +"left this year" = "restante este ano"; +"over this year" = "excedido este ano"; +"%lldd left" = "%lldd restantes"; +"%lldh left" = "%lldh restantes"; +"%@ • %@ spent" = "%@ • %@ gasto"; +"Back" = "Voltar"; +"Delete the '%@' budget?" = "Excluir o orçamento '%@'?"; +"OVERALL SPENT: %@" = "TOTAL GASTO: %@"; +"Delete your overall budget?" = "Excluir seu orçamento geral?"; + +// Settings +"Settings" = "Ajustes"; +"GENERAL" = "GERAL"; +"DATA" = "DADOS"; +"OTHERS" = "OUTROS"; +"Appearance" = "Aparência"; +"Start of Week" = "Início da semana"; +"Notifications" = "Notificações"; +"Currency" = "Moeda"; +"Number Entry" = "Entrada de números"; +"Authentication" = "Autenticação"; +"Income Tracking" = "Acompanhamento de receitas"; +"Display Cents" = "Exibir centavos"; +"iCloud Sync" = "Sincronização com o iCloud"; +"Export Data" = "Exportar dados"; +"Erase Data" = "Apagar dados"; +"Animated Charts" = "Gráficos animados"; +"Feature Lab" = "Laboratório de recursos"; +"Tip Jar" = "Contribua"; +"Report Bug" = "Relatar problema"; +"Rate on App Store" = "Avaliar na App Store"; +"Share with Friends" = "Compartilhar com amigos"; +"Follow Dime on Twitter" = "Seguir o Dime no Twitter"; +"Follow Rafael on Instagram" = "Seguir o Rafael no Instagram"; +"Light" = "Claro"; +"Dark" = "Escuro"; +"System" = "Sistema"; +"Mornings" = "Manhãs"; +"Evenings" = "Noites"; +"Custom" = "Personalizado"; +"Off" = "Desativado"; +"On" = "Ativado"; +"Type 1" = "Tipo 1"; +"Type 2" = "Tipo 2"; +"Close and reload app for change to take effect." = "Feche e abra novamente o app para aplicar a alteração."; +"Enable Notifications" = "Ativar notificações"; +"Every morning (8:00 AM)" = "Todas as manhãs (08:00)"; +"Every evening (8:00 PM)" = "Todas as noites (20:00)"; +"Custom Time" = "Horário personalizado"; +"Currencies" = "Moedas"; +"Number Entry Method" = "Método de entrada de números"; +"\"Pre-dotted\"" = "\"Centavos automáticos\""; +"If you're too lazy to add a decimal point, I gotchu covered." = "Se você não quiser digitar a vírgula decimal, a gente cuida disso."; +"\"Cent-less\"" = "\"Sem centavos\""; +"If your transactions usually amount to whole numbers - this one is for you." = "Se suas transações geralmente têm valores inteiros, esta opção é para você."; +"Enable Sync" = "Ativar sincronização"; +"Permanently Delete Everythin'" = "Apagar tudo permanentemente"; +"This action would delete all existing transactions, categories, and budgets, and cannot be undone." = "Esta ação excluirá todas as transações, categorias e orçamentos existentes e não poderá ser desfeita."; +"Danger Zone" = "Zona de perigo"; +"This action genuinely cannot be undone. Long press to confirm." = "Esta ação realmente não pode ser desfeita. Mantenha pressionado para confirmar."; +"Delete Literally Everything" = "Excluir literalmente tudo"; +"Back Out" = "Voltar atrás"; +"Celebrate Expenses" = "Comemorar despesas"; +"Show Line Graph" = "Exibir gráfico de linhas"; +"Budget Rows" = "Linhas de orçamento"; +"Experimental features - proceed with caution." = "Recursos experimentais — use com cuidado."; +"Replace Time Label" = "Substituir horário"; +"Swaps the time label of each transaction with its category name. However, if you do not manually input a note for each transaction - in which case the note is the category name by default - duplicate text will appear." = "Substitui o horário de cada transação pelo nome da categoria. Porém, se você não inserir manualmente uma observação em cada transação — caso em que o nome da categoria é usado por padrão — aparecerá um texto duplicado."; +"Keep the streak going!" = "Mantenha a sequência!"; +"Remember to input your expenses today." = "Lembre-se de registrar suas despesas hoje."; +"Loading" = "Carregando"; +"Unable to load tip options, please try again later 🥲" = "Não foi possível carregar as opções de contribuição. Tente novamente mais tarde 🥲"; +"Hey! Dime was built by a solo student developer, and is intended to be completely free-of-charge, with no paywalls or ads. If you enjoy using Dime and want to support development, please consider a small tip." = "Olá! O Dime foi criado por um único desenvolvedor estudante e foi pensado para ser totalmente gratuito, sem conteúdo pago nem anúncios. Se você gosta de usar o Dime e quer apoiar o desenvolvimento, considere fazer uma pequena contribuição."; +"Tip failed to go through, please try again!" = "Não foi possível concluir a contribuição. Tente novamente!"; +"Thanks a million, %@ Rafael" = "Muito obrigado, %@ Rafael"; +"Have a great day ahead!" = "Tenha um ótimo dia!"; +"Version %@ (%@)" = "Versão %@ (%@)"; +"Made with ❤️ by %@ from 🇸🇬" = "Feito com ❤️ por %@, de 🇸🇬"; +"☕ Coffee-Sized Tip" = "☕ Contribuição do tamanho de um café"; +"🌮 Taco-Sized Tip" = "🌮 Contribuição do tamanho de um taco"; +"🍕 Pizza-Sized Tip" = "🍕 Contribuição do tamanho de uma pizza"; + +// Widgets + +// Labels +"Recent Transactions" = "Transações recentes"; +"View your latest expenses." = "Veja suas despesas mais recentes."; +"Analyse your expenditure breakdowns over various time periods." = "Analise a distribuição das suas despesas em diferentes períodos."; +"Monitor how you are sticking to your overall budgets." = "Acompanhe seu progresso no orçamento geral."; +"Budget" = "Orçamento"; +"Monitor how you are sticking to your budgets." = "Acompanhe seu progresso nos orçamentos."; +"New Expense" = "Nova despesa"; +"A convenient button to log new purchases." = "Um botão prático para registrar novas compras."; + +"%lld%% spent" = "%lld%% gasto"; +"NO RECENT EXPENSES" = "NENHUMA DESPESA RECENTE"; +"RECENT EXPENSES" = "DESPESAS RECENTES"; +"in %@" = "em %@"; +"net" = "saldo"; +"earned" = "recebido"; +"spent" = "gasto"; +"Time Period" = "Período"; +"NO TRANSACTIONS\n%@" = "NENHUMA TRANSAÇÃO\n%@"; +"THIS WEEK" = "ESTA SEMANA"; +"THIS MONTH" = "ESTE MÊS"; +"THIS YEAR" = "ESTE ANO"; +"EARNED " = "RECEBIDO "; +"SPENT " = "GASTO "; +"ADD OVERALL BUDGET" = "ADICIONAR ORÇAMENTO GERAL"; +"Percent Spent" = "Percentual gasto"; +"Create your overall budget in the app" = "Crie seu orçamento geral no app"; +"ADD BUDGET" = "ADICIONAR ORÇAMENTO"; +"over" = "excedido"; +"SPENT: %@" = "GASTO: %@"; +"Select budget in widget options" = "Selecione um orçamento nas opções do widget"; +"Budget no longer exists - please select new budget from widget options." = "Este orçamento não existe mais. Selecione outro nas opções do widget."; +"left" = "restante"; +"SELECT BUDGET" = "SELECIONAR ORÇAMENTO"; +"SELECT BUDGET IN WIDGET OPTIONS" = "SELECIONE UM ORÇAMENTO NAS OPÇÕES DO WIDGET"; diff --git a/app/Localizations/pt-BR.lproj/Localizable.stringsdict b/app/Localizations/pt-BR.lproj/Localizable.stringsdict new file mode 100644 index 0000000..61d25da --- /dev/null +++ b/app/Localizations/pt-BR.lproj/Localizable.stringsdict @@ -0,0 +1,150 @@ + + + + + %lld days left + + NSStringLocalizedFormatKey + %#@VARIABLE@ + VARIABLE + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + lld + zero + Vazio + one + 1 dia restante + other + %lld dias restantes + + + %lld hours left + + NSStringLocalizedFormatKey + %#@VARIABLE@ + VARIABLE + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + lld + zero + Vazio + one + 1 hora restante + other + %lld horas restantes + + + %lld days + + NSStringLocalizedFormatKey + %#@VARIABLE@ + VARIABLE + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + lld + zero + dias + one + dia + other + dias + + + %lld weeks + + NSStringLocalizedFormatKey + %#@VARIABLE@ + VARIABLE + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + lld + zero + semanas + one + semana + other + semanas + + + %lld months + + NSStringLocalizedFormatKey + %#@VARIABLE@ + VARIABLE + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + lld + zero + meses + one + mês + other + meses + + + %lld weeks ago + + NSStringLocalizedFormatKey + %#@VARIABLE@ + VARIABLE + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + lld + zero + 0 semanas atrás + one + 1 semana atrás + other + %lld semanas atrás + + + %lld months ago + + NSStringLocalizedFormatKey + %#@VARIABLE@ + VARIABLE + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + lld + zero + 0 meses atrás + one + 1 mês atrás + other + %lld meses atrás + + + %lld years ago + + NSStringLocalizedFormatKey + %#@VARIABLE@ + VARIABLE + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + lld + zero + 0 anos atrás + one + 1 ano atrás + other + %lld anos atrás + + + + diff --git a/app/dime.xcodeproj/project.pbxproj b/app/dime.xcodeproj/project.pbxproj index 4598761..ce12306 100644 --- a/app/dime.xcodeproj/project.pbxproj +++ b/app/dime.xcodeproj/project.pbxproj @@ -156,6 +156,10 @@ AECE7F9E2AED1A6800B57267 /* SuggestedTransactions.swift in Sources */ = {isa = PBXBuildFile; fileRef = AECE7F9D2AED1A6800B57267 /* SuggestedTransactions.swift */; }; D63B204A2D8728C300CAFB20 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D63B20462D8728C300CAFB20 /* Localizable.strings */; }; D63B204B2D8728C300CAFB20 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = D63B20482D8728C300CAFB20 /* Localizable.stringsdict */; }; + D63B20562D8728C300CAFB20 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D63B20462D8728C300CAFB20 /* Localizable.strings */; }; + D63B20572D8728C300CAFB20 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = D63B20482D8728C300CAFB20 /* Localizable.stringsdict */; }; + D63B20582D8728C300CAFB20 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = D63B20482D8728C300CAFB20 /* Localizable.stringsdict */; }; + D63B20592D8728C300CAFB20 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = D63B20482D8728C300CAFB20 /* Localizable.stringsdict */; }; D6A4DE202D61744400F7F751 /* SwiftUIIntrospect in Frameworks */ = {isa = PBXBuildFile; productRef = D6A4DE1F2D61744400F7F751 /* SwiftUIIntrospect */; }; D6A4DE242D61744400F7F751 /* SwiftUIIntrospect-Static in Frameworks */ = {isa = PBXBuildFile; productRef = D6A4DE232D61744400F7F751 /* SwiftUIIntrospect-Static */; }; /* End PBXBuildFile section */ @@ -214,6 +218,10 @@ 530C89D52A793B6C00B5028D /* ShortcutsEntities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsEntities.swift; sourceTree = ""; }; 530C9DFC28ACE14A0038535F /* BudgetWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BudgetWidget.swift; sourceTree = ""; }; 5311A22E29F04EF300A5BE20 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/WidgetConfiguration.intentdefinition; sourceTree = ""; }; + D63B20522D8728C300CAFB20 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/WidgetConfiguration.strings; sourceTree = ""; }; + D63B20532D8728C300CAFB20 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/WidgetConfiguration.strings; sourceTree = ""; }; + D63B20542D8728C300CAFB20 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/WidgetConfiguration.strings; sourceTree = ""; }; + D63B20552D8728C300CAFB20 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/WidgetConfiguration.strings"; sourceTree = ""; }; 5313EC5B28ACE941000EAB0C /* BudgetIntent.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = BudgetIntent.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 5313EC5C28ACE941000EAB0C /* Intents.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Intents.framework; path = System/Library/Frameworks/Intents.framework; sourceTree = SDKROOT; }; 5313EC5F28ACE941000EAB0C /* IntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentHandler.swift; sourceTree = ""; }; @@ -333,6 +341,12 @@ AECE7F9D2AED1A6800B57267 /* SuggestedTransactions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestedTransactions.swift; sourceTree = ""; }; D63B20472D8728C300CAFB20 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Localizations/en.lproj/Localizable.strings; sourceTree = ""; }; D63B20492D8728C300CAFB20 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = Localizations/en.lproj/Localizable.stringsdict; sourceTree = ""; }; + D63B204C2D8728C300CAFB20 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = Localizations/fr.lproj/Localizable.strings; sourceTree = ""; }; + D63B204D2D8728C300CAFB20 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fr; path = Localizations/fr.lproj/Localizable.stringsdict; sourceTree = ""; }; + D63B204E2D8728C300CAFB20 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = Localizations/ru.lproj/Localizable.strings; sourceTree = ""; }; + D63B204F2D8728C300CAFB20 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ru; path = Localizations/ru.lproj/Localizable.stringsdict; sourceTree = ""; }; + D63B20502D8728C300CAFB20 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "Localizations/pt-BR.lproj/Localizable.strings"; sourceTree = ""; }; + D63B20512D8728C300CAFB20 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-BR"; path = "Localizations/pt-BR.lproj/Localizable.stringsdict"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -785,6 +799,9 @@ hasScannedForEncodings = 0; knownRegions = ( en, + fr, + ru, + "pt-BR", Base, ); mainGroup = 5327D27A287C697400F76ADF; @@ -814,6 +831,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + D63B20582D8728C300CAFB20 /* Localizable.stringsdict in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -822,6 +840,7 @@ buildActionMask = 2147483647; files = ( 5313EC6E28ACE941000EAB0C /* MainInterface.storyboard in Resources */, + D63B20592D8728C300CAFB20 /* Localizable.stringsdict in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -843,6 +862,8 @@ buildActionMask = 2147483647; files = ( 533182D328A0D61D00DEBFA3 /* Assets.xcassets in Resources */, + D63B20562D8728C300CAFB20 /* Localizable.strings in Resources */, + D63B20572D8728C300CAFB20 /* Localizable.stringsdict in Resources */, 53B1D193288ED87000E28062 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1049,6 +1070,10 @@ isa = PBXVariantGroup; children = ( 5311A22E29F04EF300A5BE20 /* Base */, + D63B20522D8728C300CAFB20 /* en */, + D63B20532D8728C300CAFB20 /* fr */, + D63B20542D8728C300CAFB20 /* ru */, + D63B20552D8728C300CAFB20 /* pt-BR */, ); name = WidgetConfiguration.intentdefinition; sourceTree = ""; @@ -1065,6 +1090,9 @@ isa = PBXVariantGroup; children = ( D63B20472D8728C300CAFB20 /* en */, + D63B204C2D8728C300CAFB20 /* fr */, + D63B204E2D8728C300CAFB20 /* ru */, + D63B20502D8728C300CAFB20 /* pt-BR */, ); name = Localizable.strings; sourceTree = ""; @@ -1073,6 +1101,9 @@ isa = PBXVariantGroup; children = ( D63B20492D8728C300CAFB20 /* en */, + D63B204D2D8728C300CAFB20 /* fr */, + D63B204F2D8728C300CAFB20 /* ru */, + D63B20512D8728C300CAFB20 /* pt-BR */, ); name = Localizable.stringsdict; sourceTree = ""; From 16600be539888293438113897d1d5f7e9f0e3a29 Mon Sep 17 00:00:00 2001 From: Vinicius Pimenta Date: Thu, 3 Sep 2026 15:38:04 -0300 Subject: [PATCH 2/2] Complete Brazilian Portuguese localization --- app/BudgetIntent/en.lproj/InfoPlist.strings | 2 + .../pt-BR.lproj/InfoPlist.strings | 2 + app/BudgetIntentUI/en.lproj/InfoPlist.strings | 2 + .../pt-BR.lproj/InfoPlist.strings | 2 + app/ExpenditureWidget/BudgetWidget.swift | 2 +- app/ExpenditureWidget/LockBudgetWidget.swift | 2 +- app/ExpenditureWidget/MainBudgetWidget.swift | 2 +- .../en.lproj/InfoPlist.strings | 2 + .../pt-BR.lproj/InfoPlist.strings | 2 + .../en.lproj/AppShortcuts.strings | 3 + app/Localizations/en.lproj/InfoPlist.strings | 6 + .../pt-BR.lproj/AppShortcuts.strings | 3 + .../pt-BR.lproj/InfoPlist.strings | 6 + .../pt-BR.lproj/Localizable.strings | 225 ++++++++++++++++++ .../pt-BR.lproj/Localizable.stringsdict | 18 +- app/dime.xcodeproj/project.pbxproj | 74 ++++++ .../xcshareddata/xcschemes/dime.xcscheme | 1 - .../Components/Buttons/AlertButtons.swift | 2 +- .../Actions/BudgetInsightsIntent.swift | 2 +- .../Shortcuts/Actions/GetInsightsIntent.swift | 16 +- app/dime/Views/BudgetView.swift | 10 +- app/dime/Views/CategoryView.swift | 38 +-- app/dime/Views/CustomTabBar.swift | 17 +- app/dime/Views/HomeView.swift | 4 +- app/dime/Views/ImportDataView.swift | 63 ++--- app/dime/Views/InsightsView.swift | 6 +- app/dime/Views/LogView.swift | 18 +- app/dime/Views/NewBudgetView.swift | 34 ++- .../SettingsAppIconView.swift | 8 +- .../SettingsFeatureLabView.swift | 2 +- app/dime/Views/Settings/SettingsView.swift | 16 +- app/dime/Views/TemplateTransactionView.swift | 18 +- app/dime/Views/TransactionView.swift | 18 +- app/dime/Views/UpdateSheet.swift | 20 +- app/dime/Views/WelcomeSheetView.swift | 10 +- 35 files changed, 510 insertions(+), 146 deletions(-) create mode 100644 app/BudgetIntent/en.lproj/InfoPlist.strings create mode 100644 app/BudgetIntent/pt-BR.lproj/InfoPlist.strings create mode 100644 app/BudgetIntentUI/en.lproj/InfoPlist.strings create mode 100644 app/BudgetIntentUI/pt-BR.lproj/InfoPlist.strings create mode 100644 app/ExpenditureWidget/en.lproj/InfoPlist.strings create mode 100644 app/ExpenditureWidget/pt-BR.lproj/InfoPlist.strings create mode 100644 app/Localizations/en.lproj/AppShortcuts.strings create mode 100644 app/Localizations/en.lproj/InfoPlist.strings create mode 100644 app/Localizations/pt-BR.lproj/AppShortcuts.strings create mode 100644 app/Localizations/pt-BR.lproj/InfoPlist.strings diff --git a/app/BudgetIntent/en.lproj/InfoPlist.strings b/app/BudgetIntent/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..6db7565 --- /dev/null +++ b/app/BudgetIntent/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +"CFBundleDisplayName" = "BudgetIntent"; +"CFBundleName" = "BudgetIntent"; diff --git a/app/BudgetIntent/pt-BR.lproj/InfoPlist.strings b/app/BudgetIntent/pt-BR.lproj/InfoPlist.strings new file mode 100644 index 0000000..2493437 --- /dev/null +++ b/app/BudgetIntent/pt-BR.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +"CFBundleDisplayName" = "Orçamentos do Dime"; +"CFBundleName" = "Orçamentos do Dime"; diff --git a/app/BudgetIntentUI/en.lproj/InfoPlist.strings b/app/BudgetIntentUI/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..4472a08 --- /dev/null +++ b/app/BudgetIntentUI/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +"CFBundleDisplayName" = "BudgetIntentUI"; +"CFBundleName" = "BudgetIntentUI"; diff --git a/app/BudgetIntentUI/pt-BR.lproj/InfoPlist.strings b/app/BudgetIntentUI/pt-BR.lproj/InfoPlist.strings new file mode 100644 index 0000000..2493437 --- /dev/null +++ b/app/BudgetIntentUI/pt-BR.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +"CFBundleDisplayName" = "Orçamentos do Dime"; +"CFBundleName" = "Orçamentos do Dime"; diff --git a/app/ExpenditureWidget/BudgetWidget.swift b/app/ExpenditureWidget/BudgetWidget.swift index 2084924..f3761bc 100644 --- a/app/ExpenditureWidget/BudgetWidget.swift +++ b/app/ExpenditureWidget/BudgetWidget.swift @@ -125,7 +125,7 @@ struct BudgetWidgetEntryView: View { case 4: return String(localized: "this year") default: - return "this week" + return String(localized: "this week") } } diff --git a/app/ExpenditureWidget/LockBudgetWidget.swift b/app/ExpenditureWidget/LockBudgetWidget.swift index 3555cd4..78b9847 100644 --- a/app/ExpenditureWidget/LockBudgetWidget.swift +++ b/app/ExpenditureWidget/LockBudgetWidget.swift @@ -136,7 +136,7 @@ struct LockBudgetWidgetEntryView: View { case 4: return String(localized: "this year") default: - return "this week" + return String(localized: "this week") } } diff --git a/app/ExpenditureWidget/MainBudgetWidget.swift b/app/ExpenditureWidget/MainBudgetWidget.swift index ad62635..ed1621a 100644 --- a/app/ExpenditureWidget/MainBudgetWidget.swift +++ b/app/ExpenditureWidget/MainBudgetWidget.swift @@ -92,7 +92,7 @@ struct MainBudgetWidgetEntryView: View { case 4: return String(localized: "this year") default: - return "this week" + return String(localized: "this week") } } diff --git a/app/ExpenditureWidget/en.lproj/InfoPlist.strings b/app/ExpenditureWidget/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..f65a96e --- /dev/null +++ b/app/ExpenditureWidget/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +"CFBundleDisplayName" = "ExpenditureWidgetExtension"; +"CFBundleName" = "ExpenditureWidgetExtension"; diff --git a/app/ExpenditureWidget/pt-BR.lproj/InfoPlist.strings b/app/ExpenditureWidget/pt-BR.lproj/InfoPlist.strings new file mode 100644 index 0000000..3a84798 --- /dev/null +++ b/app/ExpenditureWidget/pt-BR.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +"CFBundleDisplayName" = "Widgets do Dime"; +"CFBundleName" = "Widgets do Dime"; diff --git a/app/Localizations/en.lproj/AppShortcuts.strings b/app/Localizations/en.lproj/AppShortcuts.strings new file mode 100644 index 0000000..1352d77 --- /dev/null +++ b/app/Localizations/en.lproj/AppShortcuts.strings @@ -0,0 +1,3 @@ +"Extract leftover amount for your budgets in ${applicationName}" = "Extract leftover amount for your budgets in ${applicationName}"; +"Get insights in ${applicationName}" = "Get insights in ${applicationName}"; +"Log a new transaction in ${applicationName}" = "Log a new transaction in ${applicationName}"; diff --git a/app/Localizations/en.lproj/InfoPlist.strings b/app/Localizations/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..1a23e72 --- /dev/null +++ b/app/Localizations/en.lproj/InfoPlist.strings @@ -0,0 +1,6 @@ +"Add Expense" = "Add Expense"; +"CFBundleDisplayName" = "Dime"; +"CFBundleName" = "dime"; +"NSFaceIDUsageDescription" = "We need to unlock your data."; +"NSPhotoLibraryAddUsageDescription" = "Save an image of a transaction for sharing."; +"Search Entries" = "Search Entries"; diff --git a/app/Localizations/pt-BR.lproj/AppShortcuts.strings b/app/Localizations/pt-BR.lproj/AppShortcuts.strings new file mode 100644 index 0000000..009ec90 --- /dev/null +++ b/app/Localizations/pt-BR.lproj/AppShortcuts.strings @@ -0,0 +1,3 @@ +"Extract leftover amount for your budgets in ${applicationName}" = "Consultar o valor restante dos seus orçamentos no ${applicationName}"; +"Get insights in ${applicationName}" = "Consultar análises no ${applicationName}"; +"Log a new transaction in ${applicationName}" = "Registrar um novo lançamento no ${applicationName}"; diff --git a/app/Localizations/pt-BR.lproj/InfoPlist.strings b/app/Localizations/pt-BR.lproj/InfoPlist.strings new file mode 100644 index 0000000..2b02e0c --- /dev/null +++ b/app/Localizations/pt-BR.lproj/InfoPlist.strings @@ -0,0 +1,6 @@ +"Add Expense" = "Adicionar despesa"; +"CFBundleDisplayName" = "Dime"; +"CFBundleName" = "dime"; +"NSFaceIDUsageDescription" = "O Face ID é usado para desbloquear e proteger seus dados."; +"NSPhotoLibraryAddUsageDescription" = "Permite salvar uma imagem do lançamento para compartilhamento."; +"Search Entries" = "Buscar lançamentos"; diff --git a/app/Localizations/pt-BR.lproj/Localizable.strings b/app/Localizations/pt-BR.lproj/Localizable.strings index 5ec2554..b57298d 100644 --- a/app/Localizations/pt-BR.lproj/Localizable.strings +++ b/app/Localizations/pt-BR.lproj/Localizable.strings @@ -306,3 +306,228 @@ "left" = "restante"; "SELECT BUDGET" = "SELECIONAR ORÇAMENTO"; "SELECT BUDGET IN WIDGET OPTIONS" = "SELECIONE UM ORÇAMENTO NAS OPÇÕES DO WIDGET"; + +// Source strings added after the original localization pass + +"%.0f%%" = "%.0f%%"; +"%.2f" = "%.2f"; +"%@ %@" = "%@ %@"; +"%@%.2f" = "%@%.2f"; +"%@%@ was spent %@" = "Foram gastos %1$@%2$@ %3$@"; +"%@%lld" = "%@%lld"; +"%@, %@%@, Transaction Category: %@, Transaction made at %@" = "%1$@, %2$@%3$@, categoria da transação: %4$@, transação realizada às %5$@"; +"%lld" = "%lld"; +"%lld%%" = "%lld%%"; +"%lld/^[%lld category](inflect: true) linked" = "%1$lld de %2$lld categorias vinculadas"; +"+%@" = "+%@"; +"-%@" = "-%@"; +"." = "."; +"0" = "0"; +"ADD\nBUDGET" = "ADICIONAR\nORÇAMENTO"; +"APPEARANCE" = "APARÊNCIA"; +"Add New Transaction" = "Adicionar novo lançamento"; +"Added %@" = "Categoria %@ adicionada"; +"Additional Category Filters" = "Filtros adicionais de categoria"; +"Additional Pointers" = "Orientações adicionais"; +"All dates should be of a consistent, recognizable format. If no timestamps are provided, the time of transaction will default to 12:00 am." = "Todas as datas devem estar em um formato consistente e reconhecível. Se não houver horário, a transação usará 00:00 como padrão."; +"Amount" = "Valor"; +"An early prototype also designed by @rudra_dsigns that I kinda fancy." = "Um protótipo antigo, também criado por @rudra_dsigns, do qual gosto bastante."; +"An unknown error occurred: %@" = "Ocorreu um erro desconhecido: %@"; +"App Icon" = "Ícone do app"; +"App Icons" = "Ícones do app"; +"App Locked" = "App bloqueado"; +"App tab" = "Aba do app"; +"Assign amount column" = "Atribua a coluna de valores"; +"Assign category column" = "Atribua a coluna de categorias"; +"Assign date column" = "Atribua a coluna de datas"; +"Assign note column" = "Atribua a coluna de observações"; +"Begin by adding a CSV file with 4 columns: amount, note, date, and category." = "Comece adicionando um arquivo CSV com quatro colunas: valor, observação, data e categoria."; +"Budget Type" = "Tipo de orçamento"; +"Budget and category creation screens have been completely reworked." = "As telas de criação de orçamentos e categorias foram totalmente reformuladas."; +"Budget tab" = "Aba Orçamentos"; +"Calculate ${type} for ${timeframe}" = "Calcular ${type} em ${timeframe}"; +"Calculate leftover amount for the ${budget} ${type}" = "Calcular o valor restante de ${type} para ${budget}"; +"Calculate leftover amount for the ${type}" = "Calcular o valor restante de ${type}"; +"Category Budget" = "Orçamento por categoria"; +"Category Filters" = "Filtros de categoria"; +"Category associated with the transaction" = "Categoria associada ao lançamento"; +"Category not found" = "Categoria não encontrada"; +"Change the start of a month's cycle to align with payday." = "Altere o início do ciclo mensal para que coincida com o dia do pagamento."; +"Check it out in Photos" = "Confira no app Fotos"; +"Choose from 3 new skeuomorphic icons beautifully crafted by the talented @rudra_dsigns." = "Escolha entre três novos ícones esqueumórficos criados pelo talentoso @rudra_dsigns."; +"Close" = "Fechar"; +"Confirm" = "Confirmar"; +"Couldn't save to CoreData" = "Não foi possível salvar os dados"; +"Custom Category Colors" = "Cores personalizadas de categoria"; +"Custom Time Frames" = "Períodos personalizados"; +"Custom notification time" = "Horário personalizado da notificação"; +"Data Import" = "Importação de dados"; +"Date" = "Data"; +"Date Format" = "Formato da data"; +"Delete %@ transaction confirmation. This action cannot be undone." = "Confirmação para excluir o lançamento %@. Esta ação não pode ser desfeita."; +"Designed by the brilliant @rudra_dsigns, check out his work on Twitter." = "Criado pelo brilhante @rudra_dsigns. Confira o trabalho dele no Twitter."; +"Dime definitely isn't becoming one but it never hurts to keep dreaming." = "O Dime definitivamente não está se tornando um unicórnio, mas não custa sonhar."; +"Display +/- Symbol" = "Exibir símbolo +/−"; +"Display on Log Page" = "Exibir no histórico"; +"Displays transaction suggestions whilst you type in the 'Note' field of the new transaction page." = "Exibe sugestões de lançamentos enquanto você digita no campo de observação da tela de novo lançamento."; +"Don't worry, even if you do hide them there, you will aways be able to find all upcoming transactions right here." = "Não se preocupe: mesmo que você os oculte, todos os lançamentos futuros continuarão disponíveis aqui."; +"Duplicate Emoji" = "Emoji duplicado"; +"Duplicate Name" = "Nome duplicado"; +"Emoji" = "Emoji"; +"Ensure that the values in the 'Amount' column do not contain any currency symbols." = "Certifique-se de que os valores da coluna de valor não contenham símbolos de moeda."; +"Error occurred while matching categories." = "Ocorreu um erro ao associar as categorias."; +"Excessive" = "Intenso"; +"Expense successfully logged." = "Despesa registrada com sucesso."; +"Extract leftover amount for a particular budget" = "Consultar o valor restante de um orçamento específico"; +"Extracts your total expenditure or income for a particular time period" = "Consulta o total de despesas ou receitas em um período específico"; +"Feature Request" = "Sugerir recurso"; +"Filter" = "Filtrar"; +"Follow Dime on X" = "Seguir o Dime no X"; +"Follow Rafael on X" = "Seguir o Rafael no X"; +"Frequency" = "Frequência"; +"Future Transactions" = "Lançamentos futuros"; +"Get Budget Insights" = "Consultar orçamento"; +"Get Insights" = "Consultar análises"; +"Haphazardly put together in under 30 minutes, the original Dime icon." = "Montado às pressas em menos de 30 minutos: o ícone original do Dime."; +"Haptic Feedback" = "Resposta tátil"; +"Haptics" = "Resposta tátil"; +"Here you go!" = "Aqui está!"; +"Hidden" = "Ocultos"; +"How much was transacted?" = "Qual foi o valor do lançamento?"; +"Image Saved" = "Imagem salva"; +"Import" = "Importar"; +"Import Data" = "Importar dados"; +"Import Failed" = "Falha na importação"; +"Import Successful" = "Importação concluída"; +"Import transactions" = "Importe lançamentos"; +"Income successfully logged." = "Receita registrada com sucesso."; +"Income/Day" = "RECEITA/DIA"; +"Income/Mth" = "RECEITA/MÊS"; +"Indicate budget type" = "Indique o tipo de orçamento"; +"Indicate date format" = "Informe o formato da data"; +"Insights Type" = "Tipo de análise"; +"Insights tab" = "Aba Análises"; +"Insufficient Columns" = "Colunas insuficientes"; +"Invalid Column" = "Coluna inválida"; +"Invalid File" = "Arquivo inválido"; +"Invalid dates in date column." = "Há datas inválidas na coluna de datas."; +"Invalid values in amount column." = "Há valores inválidos na coluna de valores."; +"Limit Exceeded" = "Limite excedido"; +"Limit to 14-Day Outlook" = "Limitar aos próximos 14 dias"; +"Link categories" = "Associe as categorias"; +"Log Deleted" = "Lançamento excluído"; +"Log an ${income} of ${amount}" = "Registrar ${income} no valor de ${amount}"; +"Log an ${income} of ${amount} under ${expenseCategory}" = "Registrar ${income} no valor de ${amount} na categoria ${expenseCategory}"; +"Log an ${income} of ${amount} under ${incomeCategory}" = "Registrar ${income} no valor de ${amount} na categoria ${incomeCategory}"; +"Log new transactions in a blink" = "Registre novos lançamentos rapidamente"; +"Log tab" = "Aba Histórico"; +"Match values found in the 'Category' column to the corresponding categories in Dime." = "Associe os valores da coluna de categoria às categorias correspondentes no Dime."; +"Name" = "Nome"; +"New Toasts" = "Novos alertas"; +"New Transaction" = "Novo lançamento"; +"No categories found" = "Nenhuma categoria encontrada"; +"No category with an ID matching: %@" = "Nenhuma categoria encontrada com o ID: %@"; +"No remaining\ncategories." = "Nenhuma outra\ncategoria."; +"None" = "Nenhum"; +"Note" = "Observação"; +"Over what time frame would you like to consider?" = "Qual período você deseja considerar?"; +"Please re-enable Face ID access in the Settings app to unlock application." = "Reative o acesso ao Face ID nos Ajustes para desbloquear o app."; +"Please wait while we process your new transactions." = "Aguarde enquanto processamos seus novos lançamentos."; +"Press the plus button\nto add your first entry" = "Toque no botão de mais\npara adicionar seu primeiro lançamento"; +"Processing Import" = "Processando importação"; +"Processing import" = "Processe a importação"; +"Pull to add update" = "Puxe para adicionar uma atualização"; +"Quick Add Widget" = "Widget de adição rápida"; +"Re-designed Screens" = "Telas reformuladas"; +"Recurring Frequency" = "Frequência da recorrência"; +"Recurring Transaction" = "Lançamento recorrente"; +"Referencing %@, state the format of the dates in the assigned column." = "Usando %@ como referência, informe o formato das datas na coluna atribuída."; +"Referencing this article, state the format of the dates in the assigned column." = "Usando este artigo como referência, informe o formato das datas na coluna atribuída."; +"Remove all commas in the 'Note' and 'Category' columns as they would disrupt the parsing of your file." = "Remova todas as vírgulas das colunas de observação e categoria, pois elas prejudicam a leitura do arquivo."; +"Sample Sheet" = "Planilha de exemplo"; +"Sample from 'Date' Column" = "Amostra da coluna de data"; +"Sampled Rows from Import CSV" = "Linhas de amostra do CSV"; +"Save as Photo" = "Salvar como foto"; +"Search" = "Buscar"; +"Select a categorical budget." = "Selecione um orçamento por categoria."; +"Select a column from your import that corresponds to the categories of your transactions." = "Selecione a coluna do arquivo que corresponde às categorias dos lançamentos."; +"Select a column from your import that corresponds to the dates of your transactions." = "Selecione a coluna do arquivo que corresponde às datas dos lançamentos."; +"Select a column from your import that corresponds to the notes/subtitles of your transactions." = "Selecione a coluna do arquivo que corresponde às observações dos lançamentos."; +"Select a column from your import that corresponds to the values of your transactions." = "Selecione a coluna do arquivo que corresponde aos valores dos lançamentos."; +"Settings tab" = "Aba Ajustes"; +"Show Note Suggestions" = "Exibir sugestões de observação"; +"Shown" = "Exibidos"; +"Similarly, alert toasts across the app have been re-designed." = "Os alertas exibidos pelo app também foram reformulados."; +"Siri Shortcuts" = "Atalhos da Siri"; +"Special thanks to %@ for contributing" = "Agradecimento especial a %@ pela contribuição"; +"Spent/Day" = "GASTO/DIA"; +"Spent/Mth" = "GASTO/MÊS"; +"Start" = "Início"; +"Start of Month" = "Início do mês"; +"Stop Recurring" = "Interromper recorrência"; +"Stop Recurring?" = "Interromper recorrência?"; +"Subtle" = "Sutil"; +"Tap to Undo" = "Toque para desfazer"; +"Thanks a million, ❤️ Rafael" = "Muito obrigado, ❤️ Rafael"; +"The 'Excessive' mode makes the entire numpad haptic." = "O modo intenso ativa a resposta tátil em todo o teclado numérico."; +"The overall budget tracks expenses across the board, while categorical budgets are tied to expenses of a particular type only." = "O orçamento geral acompanha todas as despesas, enquanto os orçamentos por categoria se aplicam somente a despesas de um tipo específico."; +"The transaction will no longer be automatically logged." = "O lançamento não será mais registrado automaticamente."; +"Theme" = "Tema"; +"Time Frame" = "Período"; +"Time Frame of Data" = "Período dos dados"; +"Time Frames" = "Períodos"; +"Transfer pre-existing logs from other applications with a step-by-step guide." = "Transfira lançamentos de outros aplicativos com um guia passo a passo."; +"Try your best to stay under this limit! Also, feel free to change this in the future." = "Tente permanecer abaixo desse limite. Você poderá alterá-lo depois."; +"Type" = "Tipo"; +"Type of Data" = "Tipo de dado"; +"Type of the transaction" = "Tipo do lançamento"; +"UPCOMING" = "PRÓXIMOS"; +"Unlinked Categories" = "Categorias não associadas"; +"Unknown" = "Desconhecida"; +"Unlock App" = "Desbloquear app"; +"Upcoming Logs" = "Lançamentos futuros"; +"Using the stock iOS color picker, give your categories a long-awaited aesthetic refresh." = "Use o seletor de cores do iOS para personalizar a aparência das suas categorias."; +"Value of the transaction" = "Valor do lançamento"; +"Version %@ (%@) · 18 Sep 2023" = "Versão %@ (%@) · 18 de set. de 2023"; +"What budget type would you like to extract insights from?" = "Que tipo de orçamento você deseja consultar?"; +"What category does it come under?" = "A qual categoria pertence?"; +"What's New" = "Novidades"; +"What's New?" = "Novidades"; +"Which additional category filters do you want to impose?" = "Quais filtros adicionais de categoria você deseja aplicar?"; +"Which of the following would you like to extract?" = "Qual das opções você deseja consultar?"; +"With date restrictions lifted, log and view upcoming expenses/incomes." = "Com as restrições de data removidas, registre e visualize receitas e despesas futuras."; +"Would you like to log an income or expense?" = "Você deseja registrar uma receita ou uma despesa?"; +"all time" = "todo o período"; +"button to make transaction recurring" = "botão para tornar o lançamento recorrente"; +"categorical budget" = "orçamento por categoria"; +"day" = "dia"; +"delete transaction" = "excluir lançamento"; +"expense" = "despesa"; +"hello world" = "olá, mundo"; +"income" = "receita"; +"net total" = "saldo total"; +"next day" = "próximo dia"; +"next month" = "próximo mês"; +"next week" = "próxima semana"; +"on %@" = "em %@"; +"previous day" = "dia anterior"; +"previous month" = "mês anterior"; +"previous week" = "semana anterior"; +"remove filter" = "remover filtro"; +"showing transactions from %@ to %@" = "exibindo lançamentos de %@ a %@"; +"showing transactions in %@" = "exibindo lançamentos em %@"; +"showing transactions on %@" = "exibindo lançamentos em %@"; +"total expenditure" = "total de despesas"; +"total income" = "receita total"; +"transaction recurs daily, button to edit recurring duration" = "lançamento recorrente diário; botão para editar a duração"; +"transaction recurs monthly, button to edit recurring duration" = "lançamento recorrente mensal; botão para editar a duração"; +"transaction recurs weekly, button to edit recurring duration" = "lançamento recorrente semanal; botão para editar a duração"; +"Support Email" = "Suporte do Dime"; +"3 new Siri shortcuts allow you to conveniently add new transactions or access insights." = "Três novos atalhos da Siri permitem adicionar lançamentos ou consultar análises com praticidade."; +"%@%@" = "%@%@"; +"%@0 %@ %@" = "%@0 %@ %@"; +"~%@ /day" = "~%@ /dia"; +"·" = "·"; +"•" = "•"; +"📭️" = "📭️"; +"🙈" = "🙈"; diff --git a/app/Localizations/pt-BR.lproj/Localizable.stringsdict b/app/Localizations/pt-BR.lproj/Localizable.stringsdict index 61d25da..6fc1087 100644 --- a/app/Localizations/pt-BR.lproj/Localizable.stringsdict +++ b/app/Localizations/pt-BR.lproj/Localizable.stringsdict @@ -49,11 +49,11 @@ NSStringFormatValueTypeKey lld zero - dias + 0 dias one - dia + 1 dia other - dias + %lld dias %lld weeks @@ -67,11 +67,11 @@ NSStringFormatValueTypeKey lld zero - semanas + 0 semanas one - semana + 1 semana other - semanas + %lld semanas %lld months @@ -85,11 +85,11 @@ NSStringFormatValueTypeKey lld zero - meses + 0 meses one - mês + 1 mês other - meses + %lld meses %lld weeks ago diff --git a/app/dime.xcodeproj/project.pbxproj b/app/dime.xcodeproj/project.pbxproj index ce12306..b2457cd 100644 --- a/app/dime.xcodeproj/project.pbxproj +++ b/app/dime.xcodeproj/project.pbxproj @@ -160,6 +160,13 @@ D63B20572D8728C300CAFB20 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = D63B20482D8728C300CAFB20 /* Localizable.stringsdict */; }; D63B20582D8728C300CAFB20 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = D63B20482D8728C300CAFB20 /* Localizable.stringsdict */; }; D63B20592D8728C300CAFB20 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = D63B20482D8728C300CAFB20 /* Localizable.stringsdict */; }; + D63B20602D8728C300CAFB20 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D63B20462D8728C300CAFB20 /* Localizable.strings */; }; + D63B20612D8728C300CAFB20 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D63B20462D8728C300CAFB20 /* Localizable.strings */; }; + D63B20622D8728C300CAFB20 /* AppShortcuts.strings in Resources */ = {isa = PBXBuildFile; fileRef = D63B20682D8728C300CAFB20 /* AppShortcuts.strings */; }; + D63B20632D8728C300CAFB20 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D63B20692D8728C300CAFB20 /* InfoPlist.strings */; }; + D63B20702D8728C300CAFB20 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D63B20792D8728C300CAFB20 /* InfoPlist.strings */; }; + D63B20712D8728C300CAFB20 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D63B207A2D8728C300CAFB20 /* InfoPlist.strings */; }; + D63B20722D8728C300CAFB20 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D63B207B2D8728C300CAFB20 /* InfoPlist.strings */; }; D6A4DE202D61744400F7F751 /* SwiftUIIntrospect in Frameworks */ = {isa = PBXBuildFile; productRef = D6A4DE1F2D61744400F7F751 /* SwiftUIIntrospect */; }; D6A4DE242D61744400F7F751 /* SwiftUIIntrospect-Static in Frameworks */ = {isa = PBXBuildFile; productRef = D6A4DE232D61744400F7F751 /* SwiftUIIntrospect-Static */; }; /* End PBXBuildFile section */ @@ -347,6 +354,16 @@ D63B204F2D8728C300CAFB20 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ru; path = Localizations/ru.lproj/Localizable.stringsdict; sourceTree = ""; }; D63B20502D8728C300CAFB20 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "Localizations/pt-BR.lproj/Localizable.strings"; sourceTree = ""; }; D63B20512D8728C300CAFB20 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-BR"; path = "Localizations/pt-BR.lproj/Localizable.stringsdict"; sourceTree = ""; }; + D63B20642D8728C300CAFB20 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Localizations/en.lproj/AppShortcuts.strings; sourceTree = ""; }; + D63B20652D8728C300CAFB20 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "Localizations/pt-BR.lproj/AppShortcuts.strings"; sourceTree = ""; }; + D63B20662D8728C300CAFB20 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Localizations/en.lproj/InfoPlist.strings; sourceTree = ""; }; + D63B20672D8728C300CAFB20 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "Localizations/pt-BR.lproj/InfoPlist.strings"; sourceTree = ""; }; + D63B20732D8728C300CAFB20 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + D63B20742D8728C300CAFB20 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = ""; }; + D63B20752D8728C300CAFB20 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + D63B20762D8728C300CAFB20 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = ""; }; + D63B20772D8728C300CAFB20 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + D63B20782D8728C300CAFB20 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -408,6 +425,7 @@ 5313EC5E28ACE941000EAB0C /* BudgetIntent */ = { isa = PBXGroup; children = ( + D63B20792D8728C300CAFB20 /* InfoPlist.strings */, 537152DD28AD0E6B00E93EC4 /* BudgetIntent.entitlements */, 5313EC5F28ACE941000EAB0C /* IntentHandler.swift */, 5313EC6128ACE941000EAB0C /* Info.plist */, @@ -418,6 +436,7 @@ 5313EC6928ACE941000EAB0C /* BudgetIntentUI */ = { isa = PBXGroup; children = ( + D63B207A2D8728C300CAFB20 /* InfoPlist.strings */, 537152DE28AD0E7900E93EC4 /* BudgetIntentUI.entitlements */, 5313EC6A28ACE941000EAB0C /* IntentViewController.swift */, 5313EC6C28ACE941000EAB0C /* MainInterface.storyboard */, @@ -431,6 +450,8 @@ children = ( D63B20462D8728C300CAFB20 /* Localizable.strings */, D63B20482D8728C300CAFB20 /* Localizable.stringsdict */, + D63B20682D8728C300CAFB20 /* AppShortcuts.strings */, + D63B20692D8728C300CAFB20 /* InfoPlist.strings */, 53EB931028A65F670026BE28 /* ExpenditureWidgetExtension.entitlements */, 5327D285287C697400F76ADF /* dime */, 53B1D18F288ED7E500E28062 /* ExpenditureWidget */, @@ -584,6 +605,7 @@ 53B1D18F288ED7E500E28062 /* ExpenditureWidget */ = { isa = PBXGroup; children = ( + D63B207B2D8728C300CAFB20 /* InfoPlist.strings */, 53B1D190288ED7E500E28062 /* ExpenditureWidget.swift */, 5324985428A64DA7007B34D1 /* RecentTransactionsWidget.swift */, 53D5F02228A8E0E4004573C8 /* InsightsWidget.swift */, @@ -831,6 +853,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + D63B20702D8728C300CAFB20 /* InfoPlist.strings in Resources */, + D63B20602D8728C300CAFB20 /* Localizable.strings in Resources */, D63B20582D8728C300CAFB20 /* Localizable.stringsdict in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -840,6 +864,8 @@ buildActionMask = 2147483647; files = ( 5313EC6E28ACE941000EAB0C /* MainInterface.storyboard in Resources */, + D63B20712D8728C300CAFB20 /* InfoPlist.strings in Resources */, + D63B20612D8728C300CAFB20 /* Localizable.strings in Resources */, D63B20592D8728C300CAFB20 /* Localizable.stringsdict in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -849,6 +875,8 @@ buildActionMask = 2147483647; files = ( 5327D28E287C697C00F76ADF /* Preview Assets.xcassets in Resources */, + D63B20622D8728C300CAFB20 /* AppShortcuts.strings in Resources */, + D63B20632D8728C300CAFB20 /* InfoPlist.strings in Resources */, D63B204A2D8728C300CAFB20 /* Localizable.strings in Resources */, D63B204B2D8728C300CAFB20 /* Localizable.stringsdict in Resources */, 5327D295287C698900F76ADF /* Assets.xcassets in Resources */, @@ -862,6 +890,7 @@ buildActionMask = 2147483647; files = ( 533182D328A0D61D00DEBFA3 /* Assets.xcassets in Resources */, + D63B20722D8728C300CAFB20 /* InfoPlist.strings in Resources */, D63B20562D8728C300CAFB20 /* Localizable.strings in Resources */, D63B20572D8728C300CAFB20 /* Localizable.stringsdict in Resources */, 53B1D193288ED87000E28062 /* Assets.xcassets in Resources */, @@ -1108,6 +1137,51 @@ name = Localizable.stringsdict; sourceTree = ""; }; + D63B20682D8728C300CAFB20 /* AppShortcuts.strings */ = { + isa = PBXVariantGroup; + children = ( + D63B20642D8728C300CAFB20 /* en */, + D63B20652D8728C300CAFB20 /* pt-BR */, + ); + name = AppShortcuts.strings; + sourceTree = ""; + }; + D63B20692D8728C300CAFB20 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + D63B20662D8728C300CAFB20 /* en */, + D63B20672D8728C300CAFB20 /* pt-BR */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + D63B20792D8728C300CAFB20 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + D63B20732D8728C300CAFB20 /* en */, + D63B20742D8728C300CAFB20 /* pt-BR */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + D63B207A2D8728C300CAFB20 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + D63B20752D8728C300CAFB20 /* en */, + D63B20762D8728C300CAFB20 /* pt-BR */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + D63B207B2D8728C300CAFB20 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + D63B20772D8728C300CAFB20 /* en */, + D63B20782D8728C300CAFB20 /* pt-BR */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ diff --git a/app/dime.xcodeproj/xcshareddata/xcschemes/dime.xcscheme b/app/dime.xcodeproj/xcshareddata/xcschemes/dime.xcscheme index 109d5ee..077a042 100644 --- a/app/dime.xcodeproj/xcshareddata/xcschemes/dime.xcscheme +++ b/app/dime.xcodeproj/xcshareddata/xcschemes/dime.xcscheme @@ -34,7 +34,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "en" region = "SG" launchStyle = "0" useCustomWorkingDirectory = "NO" diff --git a/app/dime/Components/Buttons/AlertButtons.swift b/app/dime/Components/Buttons/AlertButtons.swift index 33dd284..b21388f 100644 --- a/app/dime/Components/Buttons/AlertButtons.swift +++ b/app/dime/Components/Buttons/AlertButtons.swift @@ -9,7 +9,7 @@ import Foundation import SwiftUI struct DeleteButton: View { - let text: String + let text: LocalizedStringKey let red: Bool var body: some View { diff --git a/app/dime/Shortcuts/Actions/BudgetInsightsIntent.swift b/app/dime/Shortcuts/Actions/BudgetInsightsIntent.swift index e60c289..b161f2d 100644 --- a/app/dime/Shortcuts/Actions/BudgetInsightsIntent.swift +++ b/app/dime/Shortcuts/Actions/BudgetInsightsIntent.swift @@ -107,7 +107,7 @@ struct ShortcutBudgetView: View { case 4: return String(localized: "this year") default: - return "this week" + return String(localized: "this week") } } diff --git a/app/dime/Shortcuts/Actions/GetInsightsIntent.swift b/app/dime/Shortcuts/Actions/GetInsightsIntent.swift index a8913bf..15b036d 100644 --- a/app/dime/Shortcuts/Actions/GetInsightsIntent.swift +++ b/app/dime/Shortcuts/Actions/GetInsightsIntent.swift @@ -146,26 +146,26 @@ struct ShortcutInsightsView: View { var leftText: String { switch type { case .net: - return "Net total" + return String(localized: "Net total") case .income: - return "Earned" + return String(localized: "Earned") case .spent: - return "Spent" + return String(localized: "Spent") } } var rightText: String { switch timeframe { case .day: - return "today" + return String(localized: "today") case .week: - return "this week" + return String(localized: "this week") case .month: - return "this month" + return String(localized: "this month") case .year: - return "this year" + return String(localized: "this year") case .all: - return "all time" + return String(localized: "all time") } } diff --git a/app/dime/Views/BudgetView.swift b/app/dime/Views/BudgetView.swift index d79a43f..6608bfa 100644 --- a/app/dime/Views/BudgetView.swift +++ b/app/dime/Views/BudgetView.swift @@ -281,7 +281,7 @@ struct MainBudgetView: View { case 4: return String(localized: "this year") default: - return "this week" + return String(localized: "this week") } } @@ -421,7 +421,7 @@ struct MainBudgetView: View { BudgetDollarView(amount: difference, red: totalSpent >= budgetAmount, scale: 3, size: internalWidth) .frame(width: internalWidth) - Text("\(budgetAmount >= totalSpent ? "left" : "over") \(budgetType)") + Text("\(budgetAmount >= totalSpent ? String(localized: "left") : String(localized: "over")) \(budgetType)") .font(.system(.subheadline, design: .rounded).weight(.medium)) // .font(.system(size: 15, weight: .medium, design: .rounded)) .foregroundColor(Color.SubtitleText) @@ -546,7 +546,7 @@ struct SingleBudgetView: View { case 4: return String(localized: "this year") default: - return "this week" + return String(localized: "this week") } } @@ -710,7 +710,7 @@ struct SingleBudgetView: View { VStack(alignment: .trailing, spacing: -4) { BudgetDollarView(amount: difference, red: totalSpent >= budgetAmount, scale: 1, size: 80) - Text("\(budgetAmount >= totalSpent ? "left" : "over") \(budgetType)") + Text("\(budgetAmount >= totalSpent ? String(localized: "left") : String(localized: "over")) \(budgetType)") .font(.system(.caption2, design: .rounded).weight(.medium)) .foregroundColor(Color.SubtitleText) } @@ -830,7 +830,7 @@ struct SingleBudgetView: View { BudgetDollarView(amount: difference, red: totalSpent >= budgetAmount, scale: 2, size: width - 40) - Text("\(budgetAmount >= totalSpent ? "left" : "over") \(budgetType)") + Text("\(budgetAmount >= totalSpent ? String(localized: "left") : String(localized: "over")) \(budgetType)") .font(.system(.footnote, design: .rounded).weight(.medium)) .foregroundColor(Color.SubtitleText) .minimumScaleFactor(0.7) diff --git a/app/dime/Views/CategoryView.swift b/app/dime/Views/CategoryView.swift index f2a0011..37a3580 100644 --- a/app/dime/Views/CategoryView.swift +++ b/app/dime/Views/CategoryView.swift @@ -114,7 +114,7 @@ struct CategoryView: View { if disabled { showToast = true toastImage = "exclamationmark.triangle.fill" - toastTitle = "Limit Exceeded" + toastTitle = String(localized: "Limit Exceeded") positive = false } else { newCategory = true @@ -492,7 +492,7 @@ struct CategoryListView: View { .environment(\.editMode, .constant(self.isEditing ? EditMode.active : EditMode.inactive)) } else { List { - Section(header: Text("\(income ? "INCOME" : "EXPENSE") CATEGORIES").foregroundColor(Color.SubtitleText)) { + Section(header: Text(income ? LocalizedStringKey("INCOME CATEGORIES") : LocalizedStringKey("EXPENSE CATEGORIES")).foregroundColor(Color.SubtitleText)) { if categories.isEmpty { VStack(spacing: 10) { Image(systemName: "tray") @@ -501,7 +501,7 @@ struct CategoryListView: View { // .font(.system(size: 37, weight: .light)) .foregroundColor(Color.SubtitleText) - Text("No \(income ? "income" : "expense") categories found,\nclick the 'New' button to add some.") + Text(income ? LocalizedStringKey("no_income_categories") : LocalizedStringKey("no_expense_categories")) .font(.system(.body, design: .rounded).weight(.medium)) .dynamicTypeSize(...DynamicTypeSize.xxxLarge) // .font(.system(size: 17, weight: .medium, design: .rounded)) @@ -688,7 +688,7 @@ struct CategoryListView: View { } .onChange(of: showSuggestions) { newValue in if !newValue { - toastTitle = "Suggestions Hidden" + toastTitle = String(localized: "Suggestions Hidden") toastImage = "eye.slash" showToast = true positive = true @@ -985,7 +985,7 @@ struct NewCategoryAlert: View { // } // } // } - NormalTextField(text: $newName, placeholder: "Category Name", action: verification) + NormalTextField(text: $newName, placeholder: String(localized: "Category Name"), action: verification) .focused($focusedField, equals: .name) .padding(.horizontal, 15) .padding(.vertical, 5) @@ -1065,28 +1065,28 @@ struct NewCategoryAlert: View { switch outcome { case .incomplete: - toastTitle = "Incomplete Entry" + toastTitle = String(localized: "Incomplete Entry") toastImage = "questionmark.app" case .missingEmoji: - toastTitle = "Missing Emoji" + toastTitle = String(localized: "Missing Emoji") toastImage = "person.fill" focusedField = .emoji case .missingName: - toastTitle = "Missing Name" + toastTitle = String(localized: "Missing Name") toastImage = "character.cursor.ibeam" focusedField = .name case .duplicate: - toastTitle = "Duplicate Found" + toastTitle = String(localized: "Duplicate Found") toastImage = "externaldrive" case .duplicateEmoji: - toastTitle = "Duplicate Emoji" + toastTitle = String(localized: "Duplicate Emoji") toastImage = "person.fill" focusedField = .emoji case .duplicateName: - toastTitle = "Duplicate Name" + toastTitle = String(localized: "Duplicate Name") toastImage = "character.cursor.ibeam" focusedField = .name @@ -1098,7 +1098,7 @@ struct NewCategoryAlert: View { showToast = true } else { - toastTitle = "Added \(newName)" + toastTitle = String(localized: "Added \(newName)") let generator = UINotificationFeedbackGenerator() generator.notificationOccurred(.success) @@ -1386,7 +1386,7 @@ struct EditCategoryAlert: View { } } - NormalTextField(text: $newName, placeholder: "Category Name", action: verification) + NormalTextField(text: $newName, placeholder: String(localized: "Category Name"), action: verification) .focused($focusedField, equals: .name) .padding(.horizontal, 15) .padding(.vertical, 5) @@ -1460,28 +1460,28 @@ struct EditCategoryAlert: View { switch outcome { case .incomplete: - toastTitle = "Incomplete Entry" + toastTitle = String(localized: "Incomplete Entry") toastImage = "questionmark.app" case .missingEmoji: - toastTitle = "Missing Emoji" + toastTitle = String(localized: "Missing Emoji") toastImage = "person.fill" focusedField = .emoji case .missingName: - toastTitle = "Missing Name" + toastTitle = String(localized: "Missing Name") toastImage = "character.cursor.ibeam" focusedField = .name case .duplicate: - toastTitle = "Duplicate Found" + toastTitle = String(localized: "Duplicate Found") toastImage = "externaldrive" case .duplicateEmoji: - toastTitle = "Duplicate Emoji" + toastTitle = String(localized: "Duplicate Emoji") toastImage = "person.fill" focusedField = .emoji case .duplicateName: - toastTitle = "Duplicate Name" + toastTitle = String(localized: "Duplicate Name") toastImage = "character.cursor.ibeam" focusedField = .name diff --git a/app/dime/Views/CustomTabBar.swift b/app/dime/Views/CustomTabBar.swift index d1724cb..34cd57c 100644 --- a/app/dime/Views/CustomTabBar.swift +++ b/app/dime/Views/CustomTabBar.swift @@ -148,6 +148,21 @@ struct TabButton: View { var zoomed: Bool @Binding var currentTab: String + var localizedAccessibilityLabel: String { + switch image { + case "Log": + return String(localized: "Log tab") + case "Insights": + return String(localized: "Insights tab") + case "Budget": + return String(localized: "Budget tab") + case "Settings": + return String(localized: "Settings tab") + default: + return String(localized: "App tab") + } + } + var body: some View { Button { DispatchQueue.main.async { @@ -163,7 +178,7 @@ struct TabButton: View { .foregroundColor(currentTab == image ? Color.DarkIcon : Color.GreyIcon) } .buttonStyle(BouncyButton(duration: 0.3, scale: 0.6)) - .accessibilityLabel("\(image) tab") + .accessibilityLabel(localizedAccessibilityLabel) .accessibilityAddTraits( currentTab == image ? [.isButton, .isSelected] diff --git a/app/dime/Views/HomeView.swift b/app/dime/Views/HomeView.swift index 4962984..dbfeeeb 100644 --- a/app/dime/Views/HomeView.swift +++ b/app/dime/Views/HomeView.swift @@ -119,10 +119,10 @@ struct HomeView: View { } } .toast(isPresenting: $toastPresenter.showToast, duration: 4, tapToDismiss: true, offsetY: 12, alert: { - AlertToast(displayMode: .hud, type: .systemImage("checkmark.circle.fill", Color.IncomeGreen), title: "Image Saved", subTitle: "Check it out in Photos") + AlertToast(displayMode: .hud, type: .systemImage("checkmark.circle.fill", Color.IncomeGreen), title: String(localized: "Image Saved"), subTitle: String(localized: "Check it out in Photos")) }) .toast(isPresenting: $transactionManager.showToast, duration: 4, tapToDismiss: true, offsetY: 12, alert: { - AlertToast(displayMode: .hud, type: .systemImage("arrow.uturn.backward.circle.fill", Color.AlertRed), title: "Log Deleted", subTitle: "Tap to Undo") + AlertToast(displayMode: .hud, type: .systemImage("arrow.uturn.backward.circle.fill", Color.AlertRed), title: String(localized: "Log Deleted"), subTitle: String(localized: "Tap to Undo")) }, onTap: { withAnimation(.easeInOut(duration: 0.5)) { moc.rollback() diff --git a/app/dime/Views/ImportDataView.swift b/app/dime/Views/ImportDataView.swift index ea1b775..496e8d6 100644 --- a/app/dime/Views/ImportDataView.swift +++ b/app/dime/Views/ImportDataView.swift @@ -13,7 +13,8 @@ import UniformTypeIdentifiers struct ColumnLabel { let image: String - let label: String + let color: String + let label: LocalizedStringKey } enum ProcessingState { @@ -81,7 +82,7 @@ struct ImportDataView: View { @State var uniqueCategories: [MatchedCategory] = .init() @State var processingState = ProcessingState.loading - @State var errorMessage = "Invalid dates in date column." + @State var errorMessage = String(localized: "Invalid dates in date column.") @State var confettiNumber = 0 var numberOfLinkedCategories: Int { @@ -89,7 +90,7 @@ struct ImportDataView: View { } @State var showToast = false - @State var toastMessage: String = "Invalid File" + @State var toastMessage: String = String(localized: "Invalid File") @State var showingCategoryView = false @State var pageIndex = 0 @@ -108,24 +109,28 @@ struct ImportDataView: View { } let instructions: [InstructionHeadings] = [ - InstructionHeadings(title: "Import transactions", subtitle: "Begin by adding a CSV file with 4 columns: amount, note, date, and category."), - InstructionHeadings(title: "Assign category column", subtitle: "Select a column from your import that corresponds to the categories of your transactions."), - InstructionHeadings(title: "Assign note column", subtitle: "Select a column from your import that corresponds to the notes/subtitles of your transactions."), - InstructionHeadings(title: "Assign date column", subtitle: "Select a column from your import that corresponds to the dates of your transactions."), - InstructionHeadings(title: "Assign amount column", subtitle: "Select a column from your import that corresponds to the values of your transactions."), - InstructionHeadings(title: "Indicate date format", subtitle: "Referencing this article, state the format of the dates in the assigned column."), - InstructionHeadings(title: "Link categories", subtitle: "Match values found in the 'Category' column to the corresponding categories in Dime."), - InstructionHeadings(title: "Processing import", subtitle: "Please wait while we process your new transactions.") + InstructionHeadings(title: String(localized: "Import transactions"), subtitle: String(localized: "Begin by adding a CSV file with 4 columns: amount, note, date, and category.")), + InstructionHeadings(title: String(localized: "Assign category column"), subtitle: String(localized: "Select a column from your import that corresponds to the categories of your transactions.")), + InstructionHeadings(title: String(localized: "Assign note column"), subtitle: String(localized: "Select a column from your import that corresponds to the notes/subtitles of your transactions.")), + InstructionHeadings(title: String(localized: "Assign date column"), subtitle: String(localized: "Select a column from your import that corresponds to the dates of your transactions.")), + InstructionHeadings(title: String(localized: "Assign amount column"), subtitle: String(localized: "Select a column from your import that corresponds to the values of your transactions.")), + InstructionHeadings(title: String(localized: "Indicate date format"), subtitle: String(localized: "Referencing this article, state the format of the dates in the assigned column.")), + InstructionHeadings(title: String(localized: "Link categories"), subtitle: String(localized: "Match values found in the 'Category' column to the corresponding categories in Dime.")), + InstructionHeadings(title: String(localized: "Processing import"), subtitle: String(localized: "Please wait while we process your new transactions.")) ] let labels: [ColumnLabel] = [ - ColumnLabel(image: "square.grid.2x2.fill", label: "Category"), - ColumnLabel(image: "doc.plaintext.fill", label: "Note"), - ColumnLabel(image: "calendar", label: "Date"), - ColumnLabel(image: "dollarsign.circle.fill", label: "Amount") + ColumnLabel(image: "square.grid.2x2.fill", color: "Category", label: "Category"), + ColumnLabel(image: "doc.plaintext.fill", color: "Note", label: "Note"), + ColumnLabel(image: "calendar", color: "Date", label: "Date"), + ColumnLabel(image: "dollarsign.circle.fill", color: "Amount", label: "Amount") ] - let pointers = ["Ensure that the values in the 'Amount' column do not contain any currency symbols.", "All dates should be of a consistent, recognizable format. If no timestamps are provided, the time of transaction will default to 12:00 am.", "Remove all commas in the 'Note' and 'Category' columns as they would disrupt the parsing of your file."] + let pointers = [ + String(localized: "Ensure that the values in the 'Amount' column do not contain any currency symbols."), + String(localized: "All dates should be of a consistent, recognizable format. If no timestamps are provided, the time of transaction will default to 12:00 am."), + String(localized: "Remove all commas in the 'Note' and 'Category' columns as they would disrupt the parsing of your file.") + ] var incomeCategories: [Category] { dataController.getAllCategories(income: true) @@ -401,8 +406,8 @@ struct ImportDataView: View { } .padding(.horizontal, 10) .frame(width: columnWidth, height: 30, alignment: .leading) - .foregroundColor(Color(labels[index].label)) - .background(Color(labels[index].label).opacity(0.23), in: RoundedCorner(radius: 10, corners: [.topLeft, .topRight])) + .foregroundColor(Color(labels[index].color)) + .background(Color(labels[index].color).opacity(0.23), in: RoundedCorner(radius: 10, corners: [.topLeft, .topRight])) } } else { Rectangle() @@ -675,7 +680,7 @@ struct ImportDataView: View { importData() } else { showToast = true - toastMessage = "Unlinked Categories" + toastMessage = String(localized: "Unlinked Categories") } } else if progress > 5 { @@ -691,7 +696,7 @@ struct ImportDataView: View { guard sample.containsDigits else { showToast = true - toastMessage = "Invalid Column" + toastMessage = String(localized: "Invalid Column") return } } @@ -701,7 +706,7 @@ struct ImportDataView: View { guard validateDoubles(strings: stringColumn) else { showToast = true - toastMessage = "Invalid Column" + toastMessage = String(localized: "Invalid Column") return } } @@ -716,7 +721,7 @@ struct ImportDataView: View { if progress < 5 { guard !remainingColumns.isEmpty else { showToast = true - toastMessage = "Insufficient Columns" + toastMessage = String(localized: "Insufficient Columns") return } selectedColumn = remainingColumns[0] @@ -778,7 +783,7 @@ struct ImportDataView: View { if file.startAccessingSecurityScopedResource() { guard let message = try String(data: Data(contentsOf: file), encoding: .utf8) else { showToast = true - toastMessage = "Invalid File" + toastMessage = String(localized: "Invalid File") return } @@ -840,7 +845,7 @@ struct ImportDataView: View { func processCSV() { guard data.containsDigits else { showToast = true - toastMessage = "Invalid File" + toastMessage = String(localized: "Invalid File") return } @@ -852,7 +857,7 @@ struct ImportDataView: View { guard !holdingRows.isEmpty else { showToast = true - toastMessage = "Invalid File" + toastMessage = String(localized: "Invalid File") return } @@ -865,7 +870,7 @@ struct ImportDataView: View { // guard maxColumnCount > 3 else { showToast = true - toastMessage = "Invalid File" + toastMessage = String(localized: "Invalid File") return } @@ -909,17 +914,17 @@ struct ImportDataView: View { _ = dataController.newTransaction(note: row[noteColumnIndex], category: rowCategory, income: rowCategory.income, amount: abs(transactionAmount), date: transactionDate, repeatType: 0, repeatCoefficient: 1, delay: false) } else { processingState = .error - errorMessage = "Invalid values in amount column." + errorMessage = String(localized: "Invalid values in amount column.") return } } else { processingState = .error - errorMessage = "Error occurred while matching categories." + errorMessage = String(localized: "Error occurred while matching categories.") return } } else { processingState = .error - errorMessage = "Invalid dates in date column." + errorMessage = String(localized: "Invalid dates in date column.") return } } diff --git a/app/dime/Views/InsightsView.swift b/app/dime/Views/InsightsView.swift index 8e5b1f6..c301ac7 100644 --- a/app/dime/Views/InsightsView.swift +++ b/app/dime/Views/InsightsView.swift @@ -21,11 +21,11 @@ struct InsightsView: View { var chartTypeString: String { if chartType == 1 { - return "week" + return String(localized: "week") } else if chartType == 2 { - return "month" + return String(localized: "month") } else if chartType == 3 { - return "year" + return String(localized: "year") } else { return "" } diff --git a/app/dime/Views/LogView.swift b/app/dime/Views/LogView.swift index 5da5075..fad77f8 100644 --- a/app/dime/Views/LogView.swift +++ b/app/dime/Views/LogView.swift @@ -493,11 +493,11 @@ struct LogInsightsView: View { var headingText: String { if insightsType == 1 { - return "Net total" + return String(localized: "Net total") } else if insightsType == 2 { - return "Earned" + return String(localized: "Earned") } else { - return "Spent" + return String(localized: "Spent") } } @@ -505,7 +505,7 @@ struct LogInsightsView: View { VStack(spacing: -3) { VStack(spacing: 2) { HStack(spacing: 4) { - Text(LocalizedStringKey(headingText)) + Text(headingText) .font(.system(.body, design: .rounded).weight(.medium)) .foregroundColor(Color.PrimaryText.opacity(0.9)) Button { @@ -1340,7 +1340,7 @@ struct SingleTransactionView: View { } .offset(x: offset) .accessibilityElement(children: .ignore) - .accessibilityLabel("\(transaction.wrappedNote), \(currencySymbol)\(String(format: "%.2f", transaction.wrappedAmount)), Transaction Category: \(transaction.category?.wrappedName ?? "Unknown"), Transaction made at \(timeConverterAccessibilityLabel(date: transaction.wrappedDate))") + .accessibilityLabel("\(transaction.wrappedNote), \(currencySymbol)\(String(format: "%.2f", transaction.wrappedAmount)), Transaction Category: \(transaction.category?.wrappedName ?? String(localized: "Unknown")), Transaction made at \(timeConverterAccessibilityLabel(date: transaction.wrappedDate))") } .onChange(of: deletePopup) { _ in if deletePopup { @@ -2089,7 +2089,7 @@ struct WeekStepperView: View { let endComponents = DateComponents(day: 7, second: -1) let endWeekDate = Calendar.current.date(byAdding: endComponents, to: showingDate) ?? Date.now - return "showing transactions from " + dateFormatter.string(from: showingDate) + " to " + dateFormatter.string(from: endWeekDate) + return String(localized: "showing transactions from \(dateFormatter.string(from: showingDate)) to \(dateFormatter.string(from: endWeekDate))") } var body: some View { @@ -2240,15 +2240,15 @@ func dateConverterAccessibilityLabel(date: Date) -> String { let calendar = Calendar.current if calendar.isDateInToday(date) { - return "today" + return String(localized: "today") } else if calendar.isDateInYesterday(date) { - return "yesterday" + return String(localized: "yesterday") } else { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "EEE, d MMM yyyy" - return "on " + dateFormatter.string(from: date) + return String(localized: "on \(dateFormatter.string(from: date))") } } diff --git a/app/dime/Views/NewBudgetView.swift b/app/dime/Views/NewBudgetView.swift index 902100b..8d3e7c2 100644 --- a/app/dime/Views/NewBudgetView.swift +++ b/app/dime/Views/NewBudgetView.swift @@ -42,7 +42,7 @@ struct BrandNewBudgetView: View { var initialProgress: Double @State var showToast: Bool = false - @State var toastMessage: String = "Missing Category" + @State var toastMessage: String = String(localized: "Missing Category") // stage one (ignore if editing), overall budget or category budget @State var categoryBudget: Bool = false @@ -114,7 +114,8 @@ struct BrandNewBudgetView: View { numberFormatter.maximumFractionDigits = 0 - return "~" + (numberFormatter.string(from: NSNumber(value: average)) ?? "$0") + " /day" + let formattedAverage = numberFormatter.string(from: NSNumber(value: average)) ?? "$0" + return String(localized: "~\(formattedAverage) /day") } // height of pickers @@ -142,11 +143,26 @@ struct BrandNewBudgetView: View { var instructions: [InstructionHeadings] { [ - InstructionHeadings(title: "Indicate budget type", subtitle: "The overall budget tracks expenses across the board, while categorical budgets are tied to expenses of a particular type only."), - InstructionHeadings(title: "Select a category", subtitle: "Begin by linking this budget to an existing category."), - InstructionHeadings(title: "Choose a time frame", subtitle: "The budget will periodically refresh according to your preference."), - InstructionHeadings(title: "Pick a start date", subtitle: "Which day of the \(timeFrameString) do you want your budget to start from?"), - InstructionHeadings(title: "Set budget amount", subtitle: "Try your best to stay under this limit! Also, feel free to change this in the future.") + InstructionHeadings( + title: String(localized: "Indicate budget type"), + subtitle: String(localized: "The overall budget tracks expenses across the board, while categorical budgets are tied to expenses of a particular type only.") + ), + InstructionHeadings( + title: String(localized: "Select a category"), + subtitle: String(localized: "Begin by linking this budget to an existing category.") + ), + InstructionHeadings( + title: String(localized: "Choose a time frame"), + subtitle: String(localized: "The budget will periodically refresh according to your preference.") + ), + InstructionHeadings( + title: String(localized: "Pick a start date"), + subtitle: String(localized: "Which day of the \(timeFrameString) do you want your budget to start from?") + ), + InstructionHeadings( + title: String(localized: "Set budget amount"), + subtitle: String(localized: "Try your best to stay under this limit! Also, feel free to change this in the future.") + ) ] } @@ -543,7 +559,7 @@ struct BrandNewBudgetView: View { Button { if progress == 2 && selectedCategory == nil { showToast = true - toastMessage = "Missing Category" + toastMessage = String(localized: "Missing Category") UINotificationFeedbackGenerator().notificationOccurred(.error) return } @@ -709,7 +725,7 @@ struct BrandNewBudgetView: View { if price == 0 { UINotificationFeedbackGenerator().notificationOccurred(.error) showToast = true - toastMessage = "Missing Amount" + toastMessage = String(localized: "Missing Amount") return } diff --git a/app/dime/Views/Settings/Settings Subviews/SettingsAppIconView.swift b/app/dime/Views/Settings/Settings Subviews/SettingsAppIconView.swift index fbb066b..1ef5d9d 100644 --- a/app/dime/Views/Settings/Settings Subviews/SettingsAppIconView.swift +++ b/app/dime/Views/Settings/Settings Subviews/SettingsAppIconView.swift @@ -23,16 +23,16 @@ struct SettingsAppIconView: View { let options: [AppIconBundle] = [ AppIconBundle( actualFileName: "AppIcon1", exampleFileName: "AppIcon1_EG", displayName: "V2.0", - displaySubtitle: "Designed by the brilliant @rudra_dsigns, check out his work on Twitter."), + displaySubtitle: String(localized: "Designed by the brilliant @rudra_dsigns, check out his work on Twitter.")), AppIconBundle( actualFileName: "AppIcon2", exampleFileName: "AppIcon2_EG", displayName: "Unicorn", - displaySubtitle: "Dime definitely isn't becoming one but it never hurts to keep dreaming."), + displaySubtitle: String(localized: "Dime definitely isn't becoming one but it never hurts to keep dreaming.")), AppIconBundle( actualFileName: "AppIcon3", exampleFileName: "AppIcon3_EG", displayName: "V1.5", - displaySubtitle: "An early prototype also designed by @rudra_dsigns that I kinda fancy."), + displaySubtitle: String(localized: "An early prototype also designed by @rudra_dsigns that I kinda fancy.")), AppIconBundle( actualFileName: "AppIcon4", exampleFileName: "AppIcon4_EG", displayName: "O.G.", - displaySubtitle: "Haphazardly put together in under 30 minutes, the original Dime icon.") + displaySubtitle: String(localized: "Haphazardly put together in under 30 minutes, the original Dime icon.")) ] @State private var position: Int? diff --git a/app/dime/Views/Settings/Settings Subviews/SettingsFeatureLabView.swift b/app/dime/Views/Settings/Settings Subviews/SettingsFeatureLabView.swift index b3fe94c..5bf0c44 100644 --- a/app/dime/Views/Settings/Settings Subviews/SettingsFeatureLabView.swift +++ b/app/dime/Views/Settings/Settings Subviews/SettingsFeatureLabView.swift @@ -110,7 +110,7 @@ struct SettingsGoofyView: View { } @ViewBuilder - func ToggleRow(text: String, bool: Binding, id: Int) -> some View { + func ToggleRow(text: LocalizedStringKey, bool: Binding, id: Int) -> some View { HStack { Text(text) .font(.system(.body, design: .rounded)) diff --git a/app/dime/Views/Settings/SettingsView.swift b/app/dime/Views/Settings/SettingsView.swift index 7eebdd3..9c01bd1 100644 --- a/app/dime/Views/Settings/SettingsView.swift +++ b/app/dime/Views/Settings/SettingsView.swift @@ -82,9 +82,9 @@ struct SettingsView: View { } @Environment(\.openURL) var openURL - let supportEmail = SupportEmail(toAddress: "rafasohhh@gmail.com", subject: "Support Email") + let supportEmail = SupportEmail(toAddress: "rafasohhh@gmail.com", subject: String(localized: "Support Email")) let featureRequestEmail = SupportEmail( - toAddress: "rafasohhh@gmail.com", subject: "Feature Request") + toAddress: "rafasohhh@gmail.com", subject: String(localized: "Feature Request")) @AppStorage("numberEntryType", store: UserDefaults(suiteName: "group.com.rafaelsoh.dime")) var numberEntryType: Int = 2 @@ -498,7 +498,7 @@ struct SettingsView: View { } @ViewBuilder - func ToggleRow(icon: String, color: String, text: String, bool: Bool, smaller: Bool = false, onTap: @escaping () -> Void) + func ToggleRow(icon: String, color: String, text: LocalizedStringKey, bool: Bool, smaller: Bool = false, onTap: @escaping () -> Void) -> some View { HStack(spacing: 12) { Image(systemName: icon) @@ -617,11 +617,11 @@ struct TipJarAlert: View { var bottomCaption: String { if unlockManager.failedTransaction { - return "Tip failed to go through, please try again!" + return String(localized: "Tip failed to go through, please try again!") } else if unlockManager.purchaseCount > 0 { - return "Thanks a million, \(Image(systemName: "heart.fill")) Rafael" + return String(localized: "Thanks a million, ❤️ Rafael") } else { - return "Have a great day ahead!" + return String(localized: "Have a great day ahead!") } } @@ -838,7 +838,7 @@ struct ProductView: View { struct SettingsRowView: View { var systemImage: String - var title: String + var title: LocalizedStringKey var colour: Int var optionalText: String? @@ -858,7 +858,7 @@ struct SettingsRowView: View { ) .background(Color("\(colour)"), in: RoundedRectangle(cornerRadius: 6, style: .continuous)) - Text(LocalizedStringKey(title)) + Text(title) .font(.system(.body, design: .rounded).weight(.medium)) // .font(.system(size: 17, weight: .medium, design: .rounded)) diff --git a/app/dime/Views/TemplateTransactionView.swift b/app/dime/Views/TemplateTransactionView.swift index bca7b73..d9b93cc 100644 --- a/app/dime/Views/TemplateTransactionView.swift +++ b/app/dime/Views/TemplateTransactionView.swift @@ -31,9 +31,9 @@ struct TemplateTransactionView: View { var transactionTypeString: String { if income { - return "Income" + return String(localized: "Income") } else { - return "Expense" + return String(localized: "Expense") } } @@ -132,13 +132,13 @@ struct TemplateTransactionView: View { var repeatButtonAccessibility: String { if repeatType == 1 { - return "transaction recurs daily, button to edit recurring duration" + return String(localized: "transaction recurs daily, button to edit recurring duration") } else if repeatType == 2 { - return "transaction recurs weekly, button to edit recurring duration" + return String(localized: "transaction recurs weekly, button to edit recurring duration") } else if repeatType == 3 { - return "transaction recurs monthly, button to edit recurring duration" + return String(localized: "transaction recurs monthly, button to edit recurring duration") } else { - return "button to make transaction recurring" + return String(localized: "button to make transaction recurring") } } @@ -649,20 +649,20 @@ struct TemplateTransactionView: View { if transactionValue == 0 && category == nil { toastImage = "questionmark.app" - toastTitle = "Incomplete Entry" + toastTitle = String(localized: "Incomplete Entry") showToast = true generator.notificationOccurred(.error) return } else if transactionValue == 0 { toastImage = "centsign.circle" - toastTitle = "Missing Amount" + toastTitle = String(localized: "Missing Amount") showToast = true generator.notificationOccurred(.error) return } else if category == nil { toastImage = "tray" - toastTitle = "Missing Category" + toastTitle = String(localized: "Missing Category") showToast = true generator.notificationOccurred(.error) return diff --git a/app/dime/Views/TransactionView.swift b/app/dime/Views/TransactionView.swift index 5945aaf..4da5f17 100644 --- a/app/dime/Views/TransactionView.swift +++ b/app/dime/Views/TransactionView.swift @@ -38,9 +38,9 @@ struct TransactionView: View { var transactionTypeString: String { if income { - return "Income" + return String(localized: "Income") } else { - return "Expense" + return String(localized: "Expense") } } @@ -90,13 +90,13 @@ struct TransactionView: View { var repeatButtonAccessibility: String { if repeatType == 1 { - return "transaction recurs daily, button to edit recurring duration" + return String(localized: "transaction recurs daily, button to edit recurring duration") } else if repeatType == 2 { - return "transaction recurs weekly, button to edit recurring duration" + return String(localized: "transaction recurs weekly, button to edit recurring duration") } else if repeatType == 3 { - return "transaction recurs monthly, button to edit recurring duration" + return String(localized: "transaction recurs monthly, button to edit recurring duration") } else { - return "button to make transaction recurring" + return String(localized: "button to make transaction recurring") } } @@ -991,7 +991,7 @@ struct TransactionView: View { if price == 0 && category == nil { toastImage = "questionmark.app" - toastTitle = "Incomplete Entry" + toastTitle = String(localized: "Incomplete Entry") showToast = true toggleFieldColors() @@ -1000,13 +1000,13 @@ struct TransactionView: View { return } else if price == 0 { toastImage = "centsign.circle" - toastTitle = "Missing Amount" + toastTitle = String(localized: "Missing Amount") showToast = true generator.notificationOccurred(.error) return } else if category == nil { toastImage = "tray" - toastTitle = "Missing Category" + toastTitle = String(localized: "Missing Category") showToast = true toggleFieldColors() diff --git a/app/dime/Views/UpdateSheet.swift b/app/dime/Views/UpdateSheet.swift index ebb63f7..f3af2e4 100644 --- a/app/dime/Views/UpdateSheet.swift +++ b/app/dime/Views/UpdateSheet.swift @@ -19,14 +19,14 @@ struct UpdateAlert: View { @State var opacity = 0.0 let welcomeFeatures = [ - WelcomeSheetFeatureRow(icon: "appclip", header: "Siri Shorcuts", subtitle: "3 new Siri shortcuts allow you to conveniently add new transactions or access insights."), - WelcomeSheetFeatureRow(icon: "arrow.down.doc.fill", header: "Data Import", subtitle: "Transfer pre-existing logs from other applications with a step-by-step guide."), - WelcomeSheetFeatureRow(icon: "sun.haze.fill", header: "Future Transactions", subtitle: "With date restrictions lifted, log and view upcoming expenses/incomes."), - WelcomeSheetFeatureRow(icon: "app.gift.fill", header: "App Icons", subtitle: "Choose from 3 new skeuomorphic icons beautifully crafted by the talented @rudra_dsigns."), - WelcomeSheetFeatureRow(icon: "circle.grid.2x2.fill", header: "Re-designed Screens", subtitle: "Budget and category creation screens have been completely reworked."), - WelcomeSheetFeatureRow(icon: "swatchpalette.fill", header: "Custom Category Colors", subtitle: "Using the stock iOS color picker, give your categories a long-awaited aesthetic refresh."), - WelcomeSheetFeatureRow(icon: "exclamationmark.octagon.fill", header: "New Toasts", subtitle: "Similarly, alert toasts across the app have been re-designed."), - WelcomeSheetFeatureRow(icon: "calendar", header: "Custom Time Frames", subtitle: "Change the start of a month's cycle to align with payday.") + WelcomeSheetFeatureRow(icon: "appclip", header: String(localized: "Siri Shortcuts"), subtitle: String(localized: "3 new Siri shortcuts allow you to conveniently add new transactions or access insights.")), + WelcomeSheetFeatureRow(icon: "arrow.down.doc.fill", header: String(localized: "Data Import"), subtitle: String(localized: "Transfer pre-existing logs from other applications with a step-by-step guide.")), + WelcomeSheetFeatureRow(icon: "sun.haze.fill", header: String(localized: "Future Transactions"), subtitle: String(localized: "With date restrictions lifted, log and view upcoming expenses/incomes.")), + WelcomeSheetFeatureRow(icon: "app.gift.fill", header: String(localized: "App Icons"), subtitle: String(localized: "Choose from 3 new skeuomorphic icons beautifully crafted by the talented @rudra_dsigns.")), + WelcomeSheetFeatureRow(icon: "circle.grid.2x2.fill", header: String(localized: "Re-designed Screens"), subtitle: String(localized: "Budget and category creation screens have been completely reworked.")), + WelcomeSheetFeatureRow(icon: "swatchpalette.fill", header: String(localized: "Custom Category Colors"), subtitle: String(localized: "Using the stock iOS color picker, give your categories a long-awaited aesthetic refresh.")), + WelcomeSheetFeatureRow(icon: "exclamationmark.octagon.fill", header: String(localized: "New Toasts"), subtitle: String(localized: "Similarly, alert toasts across the app have been re-designed.")), + WelcomeSheetFeatureRow(icon: "calendar", header: String(localized: "Custom Time Frames"), subtitle: String(localized: "Change the start of a month's cycle to align with payday.")) ] var body: some View { @@ -103,12 +103,12 @@ struct UpdateAlert: View { .offset(y: 2) VStack(alignment: .leading, spacing: 3.5) { - Text(LocalizedStringKey(row.header)) + Text(row.header) .font(.system(.body, design: .rounded).weight(.medium)) // .font(.system(size: 18, weight: .medium, design: .rounded)) .foregroundColor(Color.PrimaryText) - Text(LocalizedStringKey(row.subtitle)) + Text(row.subtitle) .font(.system(.subheadline, design: .rounded).weight(.medium)) // .font(.system(size: 16, weight: .regular, design: .rounded)) // .lineSpacing(0.6) diff --git a/app/dime/Views/WelcomeSheetView.swift b/app/dime/Views/WelcomeSheetView.swift index 650859e..84d8d3a 100644 --- a/app/dime/Views/WelcomeSheetView.swift +++ b/app/dime/Views/WelcomeSheetView.swift @@ -27,9 +27,9 @@ struct WelcomeSheetView: View { let timer = Timer.publish(every: 0.16, on: .main, in: .common).autoconnect() let welcomeFeatures = [ - WelcomeSheetFeatureRow(icon: "list.bullet.rectangle.fill", header: "welcome_header_1", subtitle: "welcome_subtitle_1"), - WelcomeSheetFeatureRow(icon: "chart.bar.xaxis", header: "welcome_header_2", subtitle: "welcome_subtitle_2"), - WelcomeSheetFeatureRow(icon: "archivebox.fill", header: "welcome_header_3", subtitle: "welcome_subtitle_3") + WelcomeSheetFeatureRow(icon: "list.bullet.rectangle.fill", header: String(localized: "welcome_header_1"), subtitle: String(localized: "welcome_subtitle_1")), + WelcomeSheetFeatureRow(icon: "chart.bar.xaxis", header: String(localized: "welcome_header_2"), subtitle: String(localized: "welcome_subtitle_2")), + WelcomeSheetFeatureRow(icon: "archivebox.fill", header: String(localized: "welcome_header_3"), subtitle: String(localized: "welcome_subtitle_3")) ] var body: some View { @@ -66,11 +66,11 @@ struct WelcomeSheetView: View { .offset(y: 2) VStack(alignment: .leading, spacing: 5) { - Text(LocalizedStringKey(welcomeFeatures[rowIndex].header)) + Text(welcomeFeatures[rowIndex].header) .font(.system(size: 18, weight: .medium, design: .rounded)) .foregroundColor(Color.PrimaryText) - Text(LocalizedStringKey(welcomeFeatures[rowIndex].subtitle)) + Text(welcomeFeatures[rowIndex].subtitle) .font(.system(size: 16, weight: .regular, design: .rounded)) .fixedSize(horizontal: false, vertical: true) .foregroundColor(Color.SubtitleText)