diff --git a/i18n/nw_pt.ts b/i18n/nw_pt.ts index 1d3974f3..cce1aeca 100644 --- a/i18n/nw_pt.ts +++ b/i18n/nw_pt.ts @@ -2694,7 +2694,7 @@ GuiPreferences - + Some changes will not be applied until novelWriter has been restarted. Algumas alterações não serão aplicadas enquanto a aplicação não for reiniciada. @@ -2747,117 +2747,117 @@ GuiPreferencesAutomation - + Automatic Features Funcionalidades Automáticas - + Auto-select word under cursor Selecionar automaticamente a palavra sob o cursor - + Apply formatting to word under cursor if no selection is made. Aplicar a formatação à palavra sob o cursor se nenhuma seleção for feita. - + Auto-replace text as you type Substituir automaticamente o texto enquanto digita - + Allow the editor to replace symbols as you type. Permite que o editor substituia símbolos emquanto você digita. - + Replace as You Type Substituição Durante a Digitação - + Auto-replace single quotes Substituir automaticamente aspas simples - + Try to guess which is an opening or a closing single quote. Tenta adivinhar se a aspa simples é de abertura ou de fechamento. - + Auto-replace double quotes Subsitituir automaticamente aspas duplas - + Try to guess which is an opening or a closing double quote. Tenta adivinhar se a aspa dupla é de abertura ou de fechamento. - + Auto-replace dashes Substituir automaticamente os travessões - + Double and triple hyphens become short and long dashes. Hífens duplos ou triplos se tornam travessões curtos ou longos. - + Auto-replace dots Substituir automaticamente os pontos - + Three consecutive dots become ellipsis. Três pontos consecutivos se tornam uma reticência. - + Quotation Style Estilo de Aspas - + Single quote open style Estilo da aspa de abertura simples - + The symbol to use for a leading single quote. O símbolo usado para a aspa simples à esquerda. - + Single quote close style Estilo da aspa de fechamento simples - + The symbol to use for a trailing single quote. O símbolo usado para a aspa simples à esquerda. - + Double quote open style Estilo da aspa de abertura dupla - + The symbol to use for a leading double quote. O símbolo usado para a aspa dupla à esquerda. - + Double quote close style Estilo da aspa de fechamento dupla - + The symbol to use for a trailing double quote. O símbolo usado para a aspa dupla à direita. @@ -2865,107 +2865,107 @@ GuiPreferencesDocuments - + Text Style Estilo do Texto - + Font family Família da fonte - + Font for the document editor and viewer. Fonte para o editor e visualizador de documentos. - + Font size Tamanho da fonte - + Font size for the document editor and viewer. Tamanho da fonte para o editor e visualizador de documentos. - + Text Flow Fluxo do Texto - + Maximum text width in "Normal Mode" Largura máxima do texto no "Modo Normal" - + Horizontal margins are scaled automatically. Margens horizontais são redimensionadas automaticamente. - + Maximum text width in "Focus Mode" Largura máxima do texto no "Modo Foco" - + Disable maximum text width in "Normal Mode" Desabilita a largura máxima do texto no "Modo Normal" - + Text width is defined by the margins only. A largura do texto é definida apenas pelas margens. - + Hide document footer in "Focus Mode" Oculta o rodapé do documento no "Modo Foco" - + Hide the information bar at the bottom of the document. Oculta a barra de informações na parte de baixo do documento. - + Justify the text margins in editor and viewer Justifica as margens do texto no editor e visualizador - + Lay out text with straight edges in the editor and viewer. Organiza o texto com cantos retos no editor e visualizador. - + Text margin Margem do texto - + If maximum width is set, this becomes the minimum margin. Se a largura máxima for definida, esta se torna a margem mínima. - + Tab width Largura da tabulação - + The width of a tab key press in the editor and viewer. A largura de uma tabulação no editor e visualizador. - + px px - + pt pt @@ -2973,127 +2973,127 @@ GuiPreferencesEditor - + Spell Checking Correção Ortográfica - + Internal Interno - + Spell check provider Provedor de correção ortográfica - + Note that the internal spell check tool is quite slow. Note que o corretor ortográfico interno é significativamente lento. - + Spell check language Idioma do corretor ortográfico - + Available languages are determined by your system. Os idiomas disponíveis são determinados pelo seu sistema. - + Big document limit Limite de documento grande - + Full spell checking is disabled above this limit. A verificação ortográfica é desabilitada acima desse limite. - + Writing Guides Guias de Escrita - + Show tabs and spaces Mostrar tabulações e espaços - + Add symbols to indicate tabs and spaces in the editor. Adiciona símbolos para indicar tabulações e espaços no editor. - + Show line endings Mostrar terminações de linha - + Add a symbol to indicate line endings in the editor. Adiciona um símbolo para indicar a terminação de linha no editor. - + Scroll Behaviour Comportamento da Rolagem - + Scroll past end of the document Rolar após o final do documento - + Also improves trypewriter scrolling for short documents. Também melhora a rolagem de máquina de escrever em documentos curtos. - + Typewriter style scrolling when you type Rolagem no estilo de máquina de escrever quando digita - + Try to keep the cursor at a fixed vertical position. Tenta manter o cursor em uma posição vertical fixa. - + Minimum position for Typewriter scrolling Posição máxima da rolagem de máquina de escrever - + Percentage of the editor height from the top. Porcentagem da altura do editor desde o topo. - + kB kB - + Word Count Contagem de Palavras - + Word count interval Intervalo de contagem de palavras - + How often the word count is updated. Com qual frequência a contagem de palavras é atualizada. - + seconds segundos @@ -3101,77 +3101,77 @@ GuiPreferencesGeneral - + Look and Feel Aparência - + Main GUI theme Tema da interface - + Changing this requires restarting novelWriter. Alterações nessa configuração exigem reinício da aplicação. - + Main icon theme Tema dos ícones - + Prefer icons for dark backgrounds Preferir ícones para fundos escuros - + May improve the look of icons on dark themes. Pode melhorar a aparência dos ícones em temas escuros. - + Font family Família da fonte - + Font size Tamanho da fonte - + GUI Settings Configurações da Interface - + Show full path in document header Mostrar o caminho completo do documento no cabeçalho - + Add the parent folder names to the header. Adiciona o nome dos diretórios-pai ao cabeçalho. - + Hide vertical scroll bars in main windows Ocultar a barra de rolagem vertical nas janelas principais - + Scrolling available with mouse wheel and keys only. A rolagem de tela estará diponível apenas com o mouse ou teclado. - + Hide horizontal scroll bars in main windows Ocultar a barra de rolagem horizontal nas janelas principais - + pt pt @@ -3179,107 +3179,107 @@ GuiPreferencesProjects - + Automatic Save Salvamento Automático - + Save document interval Intervalo de salvamento do documento - + How often the open document is automatically saved. Com qual frequência o documento aberto é salvo automaticamente. - + Save project interval Intervalo de salvamento do projeto - + How often the open project is automatically saved. Com qual frequencia o projeto aberto é salvo automaticamente. - + Project Backup Cópia de Segurança - + Browse Procurar - + Backup storage location Localização da cópia de segurança - + Run backup when the project is closed Executar a cópia de segurança quando o projeto é fechado - + Can be overridden for individual projects in project settings. Pode ser sobrescrito para projetos individuais nas configurações do projeto. - + Ask before running backup Perguntar antes de executar a cópia de segurança - + If off, backups will run in the background. Se desativado, cópias de segurança serão executadas em segundo plano. - + Backup Directory Diretório das Cópias de Segurança - + seconds segundos - + Session Timer Temporizador da Sessão - + Pause the session timer when not writing Pausa o temporizador da sessão quando não estiver escrevendo - + Also pauses when the application window does not have focus. Também pausa quando a janela da aplicação não estiver em foco. - + Editor inactive time before pausing timer Tempo inativo do editor antes de pausar o temporizador - + User activity includes typing and changing the content. Atividades de usuário incluem escrever e alterar o conteúdo. - + minutes minutos - + Path: {0} Caminho: {0} @@ -3287,67 +3287,67 @@ GuiPreferencesSyntax - + Highlighting Theme Tema do Destaque - + Highlighting theme Tema do destaque - + Colour theme to apply to the editor and viewer. Tema de cores para aplicar ao editor e visualizador. - + Quotes & Dialogue Citações e Diálogos - + Highlight text wrapped in quotes Destaca o texto em citações - + Applies to single, double and straight quotes. Aplica-se a citações com aspas simples, duplas e retas. - + Allow open-ended single quotes Permite citações com aspas simples sem fechamento - + Highlight single-quoted line with no closing quote. Destaca a linha com citação de aspas simples sem aspas de fechamento. - + Allow open-ended double quotes Permite citações com aspas duplas sem fechamento - + Highlight double-quoted line with no closing quote. Destaca a linha com citação de aspas duplas sem aspas de fechamento. - + Text Emphasis Ênfase de Texto - + Add highlight colour to emphasised text Adiciona destaque de cor ao texto enfatizado - + Applies to emphasis (italic) and strong (bold). Aplica-se à ênfase (itálico) e ênfase forte (negrito). @@ -4180,312 +4180,312 @@ NWProject - + Trash Lixeira - + Chapter %ch%: %title% Capítulo %ch%: %title% - + New Novo - + Note Nota - + Draft Rascunho - + Finished Finalizado - + Minor Menor - + Major Maior - + Main Principal - + New Project Novo Projeto - + Novel Livro - + Plot Enredo - + Characters Personagens - + World Mundo - + Title Page Página de Título - + New Chapter Novo Capítulo - + New Scene Nova Cena - + Failed to parse project xml. Houve uma falha ao interpretar o conteúdo XML do projeto. - + Attempting to open backup project file instead. Tentando abrir a cópia de segurança do projeto. - + Unknown Desconhecido - + Project file does not appear to be a novelWriterXML file. O arquivo do projeto não parece ser um arquivo XML do novelWriter. - + Old Project Version Versão Antiga de Projeto - + The project file and data is created by a novelWriter version lower than 0.7. Do you want to upgrade the project to the most recent format? O arquivo do projeto e os dados foram criados por uma versão anterior à 0.7 do novelWriter. Deseja atualizar o projeto para o formato mais recente? - + Note that after the upgrade, you cannot open the project with an older version of novelWriter any more, so make sure you have a recent backup. Observe que após a atualização, não será possível abrir o projeto com uma versão mais antiga do novelWriter, tenha certeza de ter uma cópia de segurança recente. - + Version Conflict Conflito de Versão - + Opened Project: {0} Projeto Aberto: {0} - + Project path not set, cannot save project. O caminho do projeto não foi definido, não é possível salvar o projeto. - + Failed to save project. Houve uma falha ao salvar o projeto. - + Saved Project: {0} Projeto Salvo: {0} - + Backing up project ... Realizando uma cópia de segurança do projeto... - + Cannot backup project because no backup path is set. Please set a valid backup location in Tools > Preferences. Não foi possível realizar uma cópia de segurança do projeto porquê o caminho das cópias de segurança não foi definido. Por favor, defina um caminho válido para as cópias de segurança em Ferramentas > Preferências. - + Cannot backup project because no project name is set. Please set a Working Title in Project > Project Settings. Não foi possível realizar a cópia de segurança do projeto porque o nome do projeto não está definido. Por favor defina o Nome do Projeto em Projeto > Configurações do Projeto. - + Cannot backup project because the backup path does not exist. Please set a valid backup location in Tools > Preferences. Não foi possível realizar a cópia de segurança do projeto porque o caminho das cópias de segurança não exite. Por favor, defina um cainho válido para as cópias de segurança em Ferramentas > Preferências. - + Could not create backup folder. Não foi possível ler o diretório de cópias de segurança. - + Cannot backup project because the backup path is within the project folder to be backed up. Please choose a different backup path in Tools > Preferences. Não foi possível realizar a cópia de segurança do projeto porque o caminho das cópias de segurança está em um caminho dentro do diretório do projeto. Por favor, escolha um caminho diferente para as cópias de segurança em Ferramentas> Preferências. - + Could not write backup archive. Não foi possível escrever o arquivo da cópia de segurança. - + Failed to create a new example project. Houve uma falha ao criar um novo projeto de exemplo. - + Failed to create a new example project. Could not find the necessary files. They seem to be missing from this installation. Houve uma falha ao criar um novo projeto de exemplo. Não foi possível encontrar os arquivos necessários. Eles parecem estar faltando nesta instalação. - + Could not create new project folder. Não foi possível criar o diretório do novo projeto. - + New project folder is not empty. Each project requires a dedicated project folder. O diretório do novo projeto não está vazio. Cada projeto requer um diretório dedicado. - + You must set a valid backup path in preferences to use the automatic project backup feature. Deve ser definido um caminho válido para as cópias de segurança nas preferências para usar a funcionalidade de cópias de segurança automáticas. - + You must set a valid project name in project settings to use the automatic project backup feature. Deve ser definido um nome de projeto válido nas preferências do projeto para usar a funcionalidade de cópias de segurança automáticas. - + Recovered Recuperado - + One or more orphaned files could not be added back into the project. Make sure at least a Novel root folder exists. Um ou mais arquivos-órfãos não puderam ser readicionados ao projeto. Verifique que pelo menos um diretório-raiz de Livro exista. - + Could not move: {0} Não foi possível mover: {0} - + Could not delete: {0} Não foi possível remover: {0} - + Could not make folder: {0} Não foi possível criar o diretório: {0} - + Chapter {0} Capítulo {0} - + Scene {0} Cena {0} - + File not found: {0} Arquivo não encontrado: {0} - + Backup from {0} Cópia de segurança de {0} - + Backup archive file written to: {0} Arquivo da cópia de segurança escrito em: {0} - + Project backed up to '{0}' Cópia de segurança realizada para '{0}' - + Found {0} orphaned file(s) in project folder. Foram encontrados {0} arquivos-órfãos no diretório do projeto. - + Recovered File {0} Arquivo Recuperado {0} - + Not a folder: {0} Não é um diretório: {0} - + Unknown or unsupported novelWriter project file format. The project cannot be opened by this version of novelWriter. The file was saved with novelWriter version {0}. Format de arquivo de projeto do novelWriter desconhecido ou não-suportado. O projeto não pode ser aberto por essa versão do novelWriter. O arquivo foi salvo com a versão {0} do novelWriter. - + [{0}] {1} - + Could not move item {0} to {1}. Não foi possível mover o item {0} para {1}. - + This project was saved by a newer version of novelWriter, version {0}. This is version {1}. If you continue to open the project, some attributes and settings may not be preserved, but the overall project should be fine. Continue opening the project? O projeto foi salvo por uma versão mais nova do novelWriter, versão {0}. Esta é a versão {1}. Caso deseje continuar a abrir o projeto, alguns atributos e configurações podem não ser preservados, o restante do projeto, porém, deve funcionar. Continuar a abrir o projeto? - + By Por - + and e @@ -4676,32 +4676,12 @@ Tokenizer - - Notes - Notas - - - + ERROR ERRO - - {0}: {1} - - - - - Synopsis - Sinopse - - - - Comment - Comentário - - - + Document '{0}' is too big ({1} MB). Skipping. O documento '{0}' é muito grande ({1} MB). Ignorando. diff --git a/nw/assets/lang/project_en.json b/nw/assets/lang/project_en.json new file mode 100644 index 00000000..e1bfbb37 --- /dev/null +++ b/nw/assets/lang/project_en.json @@ -0,0 +1,105 @@ +{ + "Synopsis": "Synopsis", + "Comment": "Comment", + "Notes": "Notes", + "0": "Zero", + "1": "One", + "2": "Two", + "3": "Three", + "4": "Four", + "5": "Five", + "6": "Six", + "7": "Seven", + "8": "Eight", + "9": "Nine", + "10": "Ten", + "11": "Eleven", + "12": "Twelve", + "13": "Thirteen", + "14": "Fourteen", + "15": "Fifteen", + "16": "Sixteen", + "17": "Seventeen", + "18": "Eighteen", + "19": "Nineteen", + "20": "Twenty", + "21": "Twenty-One", + "22": "Twenty-Two", + "23": "Twenty-Three", + "24": "Twenty-Four", + "25": "Twenty-Five", + "26": "Twenty-Six", + "27": "Twenty-Seven", + "28": "Twenty-Eight", + "29": "Twenty-Nine", + "30": "Thirty", + "31": "Thirty-One", + "32": "Thirty-Two", + "33": "Thirty-Three", + "34": "Thirty-Four", + "35": "Thirty-Five", + "36": "Thirty-Six", + "37": "Thirty-Seven", + "38": "Thirty-Eight", + "39": "Thirty-Nine", + "40": "Forty", + "41": "Forty-One", + "42": "Forty-Two", + "43": "Forty-Three", + "44": "Forty-Four", + "45": "Forty-Five", + "46": "Forty-Six", + "47": "Forty-Seven", + "48": "Forty-Eight", + "49": "Forty-Nine", + "50": "Fifty", + "51": "Fifty-One", + "52": "Fifty-Two", + "53": "Fifty-Three", + "54": "Fifty-Four", + "55": "Fifty-Five", + "56": "Fifty-Six", + "57": "Fifty-Seven", + "58": "Fifty-Eight", + "59": "Fifty-Nine", + "60": "Sixty", + "61": "Sixty-One", + "62": "Sixty-Two", + "63": "Sixty-Three", + "64": "Sixty-Four", + "65": "Sixty-Five", + "66": "Sixty-Six", + "67": "Sixty-Seven", + "68": "Sixty-Eight", + "69": "Sixty-Nine", + "70": "Seventy", + "71": "Seventy-One", + "72": "Seventy-Two", + "73": "Seventy-Three", + "74": "Seventy-Four", + "75": "Seventy-Five", + "76": "Seventy-Six", + "77": "Seventy-Seven", + "78": "Seventy-Eight", + "79": "Seventy-Nine", + "80": "Eighty", + "81": "Eighty-One", + "82": "Eighty-Two", + "83": "Eighty-Three", + "84": "Eighty-Four", + "85": "Eighty-Five", + "86": "Eighty-Six", + "87": "Eighty-Seven", + "88": "Eighty-Eight", + "89": "Eighty-Nine", + "90": "Ninety", + "91": "Ninety-One", + "92": "Ninety-Two", + "93": "Ninety-Three", + "94": "Ninety-Four", + "95": "Ninety-Five", + "96": "Ninety-Six", + "97": "Ninety-Seven", + "98": "Ninety-Eight", + "99": "Ninety-Nine" +} \ No newline at end of file diff --git a/nw/assets/lang/project_nb_NO.json b/nw/assets/lang/project_nb_NO.json new file mode 100644 index 00000000..06ce164b --- /dev/null +++ b/nw/assets/lang/project_nb_NO.json @@ -0,0 +1,105 @@ +{ + "Synopsis": "Sammendrag", + "Comment": "Kommentar", + "Notes": "Notat", + "0": "null", + "1": "én", + "2": "to", + "3": "tre", + "4": "fire", + "5": "fem", + "6": "seks", + "7": "sju", + "8": "åtte", + "9": "ni", + "10": "ti", + "11": "elleve", + "12": "tolv", + "13": "tretten", + "14": "fjorten", + "15": "femten", + "16": "seksten", + "17": "sytten", + "18": "atten", + "19": "nitten", + "20": "tjue", + "21": "tjueén", + "22": "tjueto", + "23": "tjuetre", + "24": "tjuefire", + "25": "tjuefem", + "26": "tjueseks", + "27": "tjuesju", + "28": "tjueåtte", + "29": "tjueni", + "30": "tretti", + "31": "trettién", + "32": "trettito", + "33": "trettitre", + "34": "trettifire", + "35": "trettifem", + "36": "trettiseks", + "37": "trettisju", + "38": "trettiåtte", + "39": "trettini", + "40": "førti", + "41": "førtién", + "42": "førtito", + "43": "førtitre", + "44": "førtifire", + "45": "førtifem", + "46": "førtiseks", + "47": "førtisju", + "48": "førtiåtte", + "49": "førtini", + "50": "femti", + "51": "femtién", + "52": "femtito", + "53": "femtitre", + "54": "femtifire", + "55": "femtifem", + "56": "femtiseks", + "57": "femtisju", + "58": "femtiåtte", + "59": "femtini", + "60": "seksti", + "61": "sekstién", + "62": "sekstito", + "63": "sekstitre", + "64": "sekstifire", + "65": "sekstifem", + "66": "sekstiseks", + "67": "sekstisju", + "68": "sekstiåtte", + "69": "sekstini", + "70": "sytti", + "71": "syttién", + "72": "syttito", + "73": "syttitre", + "74": "syttifire", + "75": "syttifem", + "76": "syttiseks", + "77": "syttisju", + "78": "syttiåtte", + "79": "syttini", + "80": "åtti", + "81": "åttién", + "82": "åttito", + "83": "åttitre", + "84": "åttifire", + "85": "åttifem", + "86": "åttiseks", + "87": "åttisju", + "88": "åttiåtte", + "89": "åttini", + "90": "nitti", + "91": "nittién", + "92": "nittito", + "93": "nittitre", + "94": "nittifire", + "95": "nittifem", + "96": "nittiseks", + "97": "nittisju", + "98": "nittiåtte", + "99": "nittini" +} \ No newline at end of file diff --git a/nw/assets/lang/project_nn_NO.json b/nw/assets/lang/project_nn_NO.json new file mode 100644 index 00000000..1ee2f2ee --- /dev/null +++ b/nw/assets/lang/project_nn_NO.json @@ -0,0 +1,105 @@ +{ + "Synopsis": "Samandrag", + "Comment": "Kommentar", + "Notes": "Notat", + "0": "null", + "1": "ein", + "2": "to", + "3": "tre", + "4": "fire", + "5": "fem", + "6": "seks", + "7": "sju", + "8": "åtte", + "9": "ni", + "10": "ti", + "11": "elleve", + "12": "tolv", + "13": "tretten", + "14": "fjorten", + "15": "femten", + "16": "seksten", + "17": "sytten", + "18": "atten", + "19": "nitten", + "20": "tjue", + "21": "tjueein", + "22": "tjueto", + "23": "tjuetre", + "24": "tjuefire", + "25": "tjuefem", + "26": "tjueseks", + "27": "tjuesju", + "28": "tjueåtte", + "29": "tjueni", + "30": "tretti", + "31": "trettiein", + "32": "trettito", + "33": "trettitre", + "34": "trettifire", + "35": "trettifem", + "36": "trettiseks", + "37": "trettisju", + "38": "trettiåtte", + "39": "trettini", + "40": "førti", + "41": "førtiein", + "42": "førtito", + "43": "førtitre", + "44": "førtifire", + "45": "førtifem", + "46": "førtiseks", + "47": "førtisju", + "48": "førtiåtte", + "49": "førtini", + "50": "femti", + "51": "femtiein", + "52": "femtito", + "53": "femtitre", + "54": "femtifire", + "55": "femtifem", + "56": "femtiseks", + "57": "femtisju", + "58": "femtiåtte", + "59": "femtini", + "60": "seksti", + "61": "sekstiein", + "62": "sekstito", + "63": "sekstitre", + "64": "sekstifire", + "65": "sekstifem", + "66": "sekstiseks", + "67": "sekstisju", + "68": "sekstiåtte", + "69": "sekstini", + "70": "sytti", + "71": "syttiein", + "72": "syttito", + "73": "syttitre", + "74": "syttifire", + "75": "syttifem", + "76": "syttiseks", + "77": "syttisju", + "78": "syttiåtte", + "79": "syttini", + "80": "åtti", + "81": "åttiein", + "82": "åttito", + "83": "åttitre", + "84": "åttifire", + "85": "åttifem", + "86": "åttiseks", + "87": "åttisju", + "88": "åttiåtte", + "89": "åttini", + "90": "nitti", + "91": "nittiein", + "92": "nittito", + "93": "nittitre", + "94": "nittifire", + "95": "nittifem", + "96": "nittiseks", + "97": "nittisju", + "98": "nittiåtte", + "99": "nittini" +} \ No newline at end of file diff --git a/nw/config.py b/nw/config.py index a4f483a4..5cf71b85 100644 --- a/nw/config.py +++ b/nw/config.py @@ -76,6 +76,7 @@ class Config: self.themeRoot = None # The full path to the nw/assets/themes folder self.dictPath = None # The full path to the nw/assets/dict folder self.iconPath = None # The full path to the nw/assets/icons folder + self.langPath = None # The full path to the nw/assets/lang folder self.helpPath = None # The full path to the novelwriter .qhc help file # Runtime Settings and Variables @@ -297,6 +298,7 @@ class Config: self.themeRoot = os.path.join(self.assetPath, "themes") self.dictPath = os.path.join(self.assetPath, "dict") self.iconPath = os.path.join(self.assetPath, "icons") + self.langPath = os.path.join(self.assetPath, "lang") self.appIcon = os.path.join(self.iconPath, "novelwriter.svg") # Internationalisation diff --git a/nw/core/__init__.py b/nw/core/__init__.py index 61046eec..be0aeeb2 100644 --- a/nw/core/__init__.py +++ b/nw/core/__init__.py @@ -7,12 +7,11 @@ from nw.core.spellcheck import NWSpellCheck, NWSpellEnchant, NWSpellSimple from nw.core.tohtml import ToHtml from nw.core.toodt import ToOdt from nw.core.tomd import ToMarkdown -from nw.core.tools import countWords, numberToRoman, numberToWord +from nw.core.tools import countWords, numberToRoman __all__ = [ "countWords", "numberToRoman", - "numberToWord", "NWDoc", "NWIndex", "NWProject", diff --git a/nw/core/project.py b/nw/core/project.py index 7d70b39a..02fa3fd3 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -27,6 +27,7 @@ along with this program. If not, see . import nw import logging import os +import json import shutil from lxml import etree @@ -61,6 +62,7 @@ class NWProject(): # Core Elements self.optState = OptionState(self) # Project-specific GUI options self.projTree = NWTree(self) # The project tree + self.langData = {} # Localisation data # Project Status self.projOpened = 0 # The time stamp of when the project file was opened @@ -622,6 +624,7 @@ class NWProject(): self.theParent.setStatus(self.tr("Opened Project: {0}").format(self.projName)) self._scanProjectFolder() + self.loadProjectLocalisation(self.projLang) self.currWCount = self.lastWCount self.projOpened = time() @@ -1046,6 +1049,7 @@ class NWProject(): theLang = checkString(theLang, None, True) if self.projLang != theLang: self.projLang = theLang + self.loadProjectLocalisation(theLang) self.setProjectChanged(True) return True @@ -1233,6 +1237,44 @@ class NWProject(): self.importItems.countEntry(nwItem.itemStatus) return + def localLookup(self, theWord): + """Look up a word in the translation map for the project and + return it. The variable is cast to a string before lookup. If + the word does not exist, it returns itself. + """ + theValue = str(theWord) + return self.langData.get(theValue, theValue) + + def loadProjectLocalisation(self, theLang): + """Load the language data for the current project language. + """ + if theLang is None: + theLang = self.mainConf.spellLanguage + if theLang is None: + theLang = "en" + + lngShort = theLang.split("_")[0] + loadFile = os.path.join(self.mainConf.langPath, "project_en.json") + chkFile1 = os.path.join(self.mainConf.langPath, "project_%s.json" % theLang) + chkFile2 = os.path.join(self.mainConf.langPath, "project_%s.json" % lngShort) + + if os.path.isfile(chkFile1): + loadFile = chkFile1 + elif os.path.isfile(chkFile2): + loadFile = chkFile2 + + try: + with open(loadFile, mode="r", encoding="utf8") as inFile: + self.langData = json.load(inFile) + logger.debug("Loaded project language file: %s" % os.path.basename(loadFile)) + + except Exception: + logger.error("Failed to load index file") + nw.logException() + return False + + return True + ## # Internal Functions ## diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py index 04882855..242554bf 100644 --- a/nw/core/tohtml.py +++ b/nw/core/tohtml.py @@ -405,24 +405,20 @@ class ToHtml(Tokenizer): def _formatSynopsis(self, tText): """Apply HTML formatting to synopsis. """ + sSynop = self._localLookup("Synopsis") if self.genMode == self.M_PREVIEW: - return "

%s: %s

\n" % ( - self._trSynopsis, tText - ) + return f"

{sSynop}: {tText}

\n" else: - return "

%s: %s

\n" % ( - self._trSynopsis, tText - ) + return f"

{sSynop}: {tText}

\n" def _formatComments(self, tText): """Apply HTML formatting to comments. """ if self.genMode == self.M_PREVIEW: - return "

%s

\n" % tText + return f"

{tText}

\n" else: - return "

%s: %s

\n" % ( - self._trComment, tText - ) + sComm = self._localLookup("Comment") + return f"

{sComm}: {tText}

\n" def _formatKeywords(self, tText): """Apply HTML formatting to keywords. diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py index a830ca9d..260a230e 100644 --- a/nw/core/tokenizer.py +++ b/nw/core/tokenizer.py @@ -33,7 +33,7 @@ from functools import partial from PyQt5.QtCore import QCoreApplication, QRegularExpression from nw.core.document import NWDoc -from nw.core.tools import numberToWord, numberToRoman +from nw.core.tools import numberToRoman from nw.constants import nwConst, nwUnicode, nwItemLayout, nwItemType, nwRegEx logger = logging.getLogger(__name__) @@ -143,18 +143,10 @@ class Tokenizer(): # Error Handling self.errData = [] - # Internal Mappings + # Function Mapping + self._localLookup = self.theProject.localLookup self.tr = partial(QCoreApplication.translate, "Tokenizer") - # Localisation - self._trSynopsis = self.tr("Synopsis") - self._trComment = self.tr("Comment") - self._trNotes = self.tr("Notes") - - self._spellLang = self.theProject.projLang - if self._spellLang is None: - self._spellLang = self.theParent.mainConf.spellLanguage - return ## @@ -263,7 +255,7 @@ class Tokenizer(): if theItem.itemType != nwItemType.ROOT: return False - theTitle = self.tr("{0}: {1}").format(self._trNotes, theItem.itemName) + theTitle = "%s: %s" % (self._localLookup("Notes"), theItem.itemName) self.theTokens = [] self.theTokens.append(( self.T_TITLE, 0, theTitle, None, self.A_PBB | self.A_CENTRE @@ -667,11 +659,12 @@ class Tokenizer(): theTitle = theTitle.replace(r"%sc%", str(self.numChScene)) theTitle = theTitle.replace(r"%sca%", str(self.numAbsScene)) if r"%chw%" in theTitle: - theTitle = theTitle.replace(r"%chw%", numberToWord(self.numChapter, self._spellLang)) + theTitle = theTitle.replace(r"%chw%", self._localLookup(self.numChapter)) if r"%chi%" in theTitle: theTitle = theTitle.replace(r"%chi%", numberToRoman(self.numChapter, True)) if r"%chI%" in theTitle: theTitle = theTitle.replace(r"%chI%", numberToRoman(self.numChapter, False)) - return theTitle + + return theTitle[:1].upper() + theTitle[1:] # END Class Tokenizer diff --git a/nw/core/tomd.py b/nw/core/tomd.py index fe6da8fa..b45d2e6c 100644 --- a/nw/core/tomd.py +++ b/nw/core/tomd.py @@ -134,10 +134,10 @@ class ToMarkdown(Tokenizer): thisPar.append(tTemp.rstrip() + " ") elif tType == self.T_SYNOPSIS and self.doSynopsis: - tmpResult.append("**Synopsis:** %s\n\n" % tText) + tmpResult.append("**%s:** %s\n\n" % (self._localLookup("Synopsis"), tText)) elif tType == self.T_COMMENT and self.doComments: - tmpResult.append("**Comment:** %s\n\n" % tText) + tmpResult.append("**%s:** %s\n\n" % (self._localLookup("Comment"), tText)) elif tType == self.T_KEYWORD and self.doKeywords: tmpResult.append(self._formatKeywords(tText, tStyle)) diff --git a/nw/core/toodt.py b/nw/core/toodt.py index b9cffc3c..0de95281 100644 --- a/nw/core/toodt.py +++ b/nw/core/toodt.py @@ -507,15 +507,17 @@ class ToOdt(Tokenizer): def _formatSynopsis(self, tText): """Apply formatting to synopsis lines. """ - rTxt = "**Synopsis:** %s" % tText - rFmt = "_B b_ %s" % (" "*len(tText)) + sSynop = self._localLookup("Synopsis") + rTxt = "**%s:** %s" % (sSynop, tText) + rFmt = "_B%s b_ %s" % (" "*len(sSynop), " "*len(tText)) return rTxt, rFmt def _formatComments(self, tText): """Apply formatting to comments. """ - rTxt = "**Comment:** %s" % tText - rFmt = "_B b_ %s" % (" "*len(tText)) + sComm = self._localLookup("Comment") + rTxt = "**%s:** %s" % (sComm, tText) + rFmt = "_B%s b_ %s" % (" "*len(sComm), " "*len(tText)) return rTxt, rFmt def _formatKeywords(self, tText): @@ -530,7 +532,7 @@ class ToOdt(Tokenizer): if theBits[0] in nwLabels.KEY_NAME: tText = nwLabels.KEY_NAME[theBits[0]] rTxt += "**%s:** " % tText - rFmt += "_B%s b_ " % (" "*len(tText)) + rFmt += "_B%s b_ " % (" "*len(tText)) if len(theBits) > 1: if theBits[0] == nwKeyWords.TAG_KEY: rTxt += "%s" % theBits[1] diff --git a/nw/core/tools.py b/nw/core/tools.py index 7e538d6d..a3dff45b 100644 --- a/nw/core/tools.py +++ b/nw/core/tools.py @@ -6,8 +6,6 @@ Various core tool functions File History: Created: 2019-04-22 [0.0.1] countWords -Created: 2019-10-13 [0.2.3] numberToWord, _numberToWordEN -Merged: 2020-05-08 [0.4.5] All of the above into this file Created: 2020-07-05 [0.10.0] numberToRoman This file is a part of novelWriter @@ -118,126 +116,3 @@ def numberToRoman(numVal, isLower=False): break return romNum.lower() if isLower else romNum - -# =============================================================================================== # -# Convert an Integer to a Number String -# =============================================================================================== # - -def numberToWord(numVal, theLang): - """Wrapper for converting numbers to words for chapter headings. - """ - if not isinstance(numVal, int): - return "[NaN]" - - if numVal < 0: - return "[<0]" - - if numVal > 999: - return "[>999]" - - numWord = "" - theCode = theLang.split("_")[0] - if theCode == "en": - numWord = _numberToWord_EN(numVal) - elif theCode in ("nb", "nn"): - numWord = _numberToWord_NO(numVal, theCode) - else: - # Default to return the number as a string - numWord = str(numVal) - - return numWord - -def _numberToWord_EN(numVal): - """Convert numbers to English words. - """ - oneWord = "" - tenWord = "" - hunWord = "" - - if numVal == 0: - return "Zero" - - oneVal = numVal % 10 - tenVal = (numVal - oneVal) % 100 - hunVal = (numVal - tenVal - oneVal) % 1000 - - theWords = { - 0: "", 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five", - 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine", 10: "Ten", - 11: "Eleven", 12: "Twelve", 13: "Thirteen", 14: "Fourteen", - 15: "Fifteen", 16: "Sixteen", 17: "Seventeen", 18: "Eighteen", - 19: "Nineteen", 20: "Twenty", 30: "Thirty", 40: "Forty", - 50: "Fifty", 60: "Sixty", 70: "Seventy", 80: "Eighty", - 90: "Ninety", 100: "One Hundred", 200: "Two Hundred", - 300: "Three Hundred", 400: "Four Hundred", 500: "Five Hundred", - 600: "Six Hundred", 700: "Seven Hundred", 800: "Eight Hundred", - 900: "Nine Hundred", - } - - retVal = "" - hunWord = theWords.get(hunVal, "") - if tenVal == 10: - oneWord = theWords.get(oneVal+10, "") - retVal = f"{hunWord} {oneWord}".strip() - else: - oneWord = theWords.get(oneVal, "") - if tenVal == 0: - retVal = f"{hunWord} {oneWord}".strip() - else: - tenWord = theWords.get(tenVal, "") - if oneVal == 0: - retVal = f"{hunWord} {tenWord}".strip() - else: - retVal = f"{hunWord} {tenWord}-{oneWord}".strip() - - return retVal - -def _numberToWord_NO(numVal, theCode): - """Convert numbers to Norwegian words. - """ - oneWord = "" - tenWord = "" - hunWord = "" - - if numVal == 0: - return "null" - - oneVal = numVal % 10 - tenVal = (numVal - oneVal) % 100 - hunVal = (numVal - tenVal - oneVal) % 1000 - - theWords = { - 0: "", 1: "én", 2: "to", 3: "tre", 4: "fire", 5: "fem", 6: "seks", - 7: "sju", 8: "åtte", 9: "ni", 10: "ti", 11: "elleve", 12: "tolv", - 13: "tretten", 14: "fjorten", 15: "femten", 16: "seksten", 17: "sytten", - 18: "atten", 19: "nitten", 20: "tjue", 30: "tretti", 40: "førti", - 50: "femti", 60: "seksti", 70: "sytti", 80: "åtti", 90: "nitti", - 100: "ett hundre", 200: "to hundre", 300: "tre hundre", 400: "fire hundre", - 500: "fem hundre", 600: "seks hundre", 700: "sju hundre", - 800: "åtte hundre", 900: "ni hundre", - } - - if theCode == "nn": - theWords[1] = "ein" - theWords[100] = "eitt hundre" - - retVal = "" - hunWord = theWords.get(hunVal, "") - if tenVal == 10: - oneWord = theWords.get(oneVal+10, "") - sepVal = " og " if hunWord and oneWord else " " - retVal = f"{hunWord}{sepVal}{oneWord}" - else: - oneWord = theWords.get(oneVal, "") - if tenVal == 0: - sepVal = " og " if hunWord and oneWord else " " - retVal = f"{hunWord}{sepVal}{oneWord}".strip() - else: - tenWord = theWords.get(tenVal, "") - sepVal = " og " if hunWord and tenWord else " " - if oneVal == 0: - retVal = f"{hunWord}{sepVal}{tenWord}".strip() - else: - retVal = f"{hunWord}{sepVal}{tenWord}{oneWord}".strip() - - return retVal.strip() diff --git a/nw/gui/preferences.py b/nw/gui/preferences.py index 74a2af90..8234ebf3 100644 --- a/nw/gui/preferences.py +++ b/nw/gui/preferences.py @@ -91,6 +91,7 @@ class GuiPreferences(PagedDialog): logger.debug("Saving new preferences") needsRestart = self.tabGeneral.saveValues() + prevSpell = self.mainConf.spellLanguage self.tabProjects.saveValues() self.tabDocs.saveValues() @@ -98,6 +99,9 @@ class GuiPreferences(PagedDialog): self.tabSyntax.saveValues() self.tabAuto.saveValues() + if prevSpell != self.mainConf.spellLanguage: + self.theProject.loadProjectLocalisation(self.mainConf.spellLanguage) + if needsRestart: self.theParent.makeAlert( self.tr("Some changes will not be applied until novelWriter has been restarted."), diff --git a/tests/test_core/test_core_tokenizer.py b/tests/test_core/test_core_tokenizer.py index 8f8604f9..cad615fd 100644 --- a/tests/test_core/test_core_tokenizer.py +++ b/tests/test_core/test_core_tokenizer.py @@ -112,11 +112,14 @@ def testCoreToken_Setters(dummyGUI): # END Test testCoreToken_Setters @pytest.mark.core -def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI): +def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI, tmpConf): """Test handling files and text in the Tokenizer class. """ theProject = NWProject(dummyGUI) + theProject.mainConf = tmpConf theProject.projTree.setSeed(42) + theProject.loadProjectLocalisation("en") + theToken = Tokenizer(theProject, dummyGUI) theToken.setKeepMarkdown(True) @@ -408,10 +411,12 @@ def testCoreToken_Tokenize(dummyGUI): # END Test testCoreToken_Tokenize @pytest.mark.core -def testCoreToken_Headers(dummyGUI): +def testCoreToken_Headers(dummyGUI, tmpConf): """Test the header and page parser of the Tokenizer class. """ theProject = NWProject(dummyGUI) + theProject.mainConf = tmpConf + theProject.loadProjectLocalisation("en") theToken = Tokenizer(theProject, dummyGUI) # Nothing diff --git a/tests/test_core/test_core_tools.py b/tests/test_core/test_core_tools.py index 802eb94f..77faf444 100644 --- a/tests/test_core/test_core_tools.py +++ b/tests/test_core/test_core_tools.py @@ -22,7 +22,7 @@ along with this program. If not, see . import pytest -from nw.core.tools import countWords, numberToRoman, numberToWord +from nw.core.tools import countWords, numberToRoman @pytest.mark.core def testCoreTools_CountWords(): @@ -81,80 +81,3 @@ def testCoreTools_RomanNumbers(): assert numberToRoman(999, True) == "cmxcix" # END Test testCoreTools_RomanNumbers - -@pytest.mark.core -def testCoreTools_NumberWords(): - """Test the conversion of integer to English words. - """ - # English - assert numberToWord(0, "en") == "Zero" - assert numberToWord(1, "en") == "One" - assert numberToWord(2, "en") == "Two" - assert numberToWord(3, "en") == "Three" - assert numberToWord(4, "en") == "Four" - assert numberToWord(5, "en") == "Five" - assert numberToWord(6, "en") == "Six" - assert numberToWord(7, "en") == "Seven" - assert numberToWord(8, "en") == "Eight" - assert numberToWord(9, "en") == "Nine" - assert numberToWord(10, "en") == "Ten" - assert numberToWord(11, "en") == "Eleven" - assert numberToWord(12, "en") == "Twelve" - assert numberToWord(13, "en") == "Thirteen" - assert numberToWord(14, "en") == "Fourteen" - assert numberToWord(15, "en") == "Fifteen" - assert numberToWord(16, "en") == "Sixteen" - assert numberToWord(17, "en") == "Seventeen" - assert numberToWord(18, "en") == "Eighteen" - assert numberToWord(19, "en") == "Nineteen" - assert numberToWord(20, "en") == "Twenty" - assert numberToWord(21, "en") == "Twenty-One" - assert numberToWord(29, "en") == "Twenty-Nine" - assert numberToWord(42, "en") == "Forty-Two" - assert numberToWord(114, "en") == "One Hundred Fourteen" - assert numberToWord(142, "en") == "One Hundred Forty-Two" - assert numberToWord(999, "en") == "Nine Hundred Ninety-Nine" - - # Norwegian - assert numberToWord(0, "nb") == "null" - assert numberToWord(1, "nb") == "én" - assert numberToWord(10, "nb") == "ti" - assert numberToWord(20, "nb") == "tjue" - assert numberToWord(21, "nb") == "tjueén" - assert numberToWord(29, "nb") == "tjueni" - assert numberToWord(42, "nb") == "førtito" - assert numberToWord(60, "nb") == "seksti" - assert numberToWord(100, "nb") == "ett hundre" - assert numberToWord(114, "nb") == "ett hundre og fjorten" - assert numberToWord(142, "nb") == "ett hundre og førtito" - assert numberToWord(999, "nb") == "ni hundre og nittini" - - assert numberToWord(0, "nn") == "null" - assert numberToWord(1, "nn") == "ein" - assert numberToWord(10, "nn") == "ti" - assert numberToWord(20, "nn") == "tjue" - assert numberToWord(21, "nn") == "tjueein" - assert numberToWord(29, "nn") == "tjueni" - assert numberToWord(42, "nn") == "førtito" - assert numberToWord(60, "nn") == "seksti" - assert numberToWord(100, "nn") == "eitt hundre" - assert numberToWord(114, "nn") == "eitt hundre og fjorten" - assert numberToWord(142, "nn") == "eitt hundre og førtito" - assert numberToWord(999, "nn") == "ni hundre og nittini" - - # Check complex language setting - assert numberToWord(1, "en_GB") == "One" - assert numberToWord(2, "en_GB") == "Two" - assert numberToWord(3, "en_GB") == "Three" - - # Check a few with a nonsense language setting - assert numberToWord(1, "foo") == "1" - assert numberToWord(2, "foo") == "2" - assert numberToWord(3, "foo") == "3" - - # Test out of range values - assert numberToWord(12345, "en") == "[>999]" - assert numberToWord(-2345, "en") == "[<0]" - assert numberToWord("234", "en") == "[NaN]" - -# END Test testCoreTools_NumberWords