# ContentDB # Copyright (C) 2024 rubenwardy # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import os import re class Translation: language: str textdomain: str entries: dict[str] def __init__(self, language: str, textdomain: str, entries: dict): self.language = language self.textdomain = textdomain self.entries = entries def parse_tr(filepath: str) -> Translation: entries = {} filename = os.path.basename(filepath) filename_parts = filename.split(".") assert len(filename_parts) >= 3 assert filename_parts[-1] == "tr" language = filename_parts[-2] textdomain = ".".join(filename_parts[0:-2]) had_textdomain_comment = False with open(filepath, "r", encoding="utf-8") as existing_file: lines = existing_file.readlines() line_index = 0 while line_index < len(lines): line = lines[line_index].rstrip('\n') if line.strip() == "": pass # Comment lines elif line.startswith("#"): # Store first occurrence of textdomain # discard all subsequent textdomain lines if line.startswith("# textdomain:"): line_textdomain = line[13:].strip() had_textdomain_comment = True if line_textdomain != textdomain: raise SyntaxError( f"Line {line_index + 1}: The filename's textdomain ({textdomain}) should match the comment ({line_textdomain})") elif not had_textdomain_comment: raise SyntaxError(f"Missing `# textdomain: {textdomain}` at the top of the file") else: i = 0 had_equals = False source = "" current_part = "" next_variable = 1 while i < len(line): if line[i] == "@": if i + 1 < len(line): i += 1 code = line[i] if code == "=": current_part += "=" elif code == "@": current_part += "@" elif code == "n": current_part += "\n" elif code.isdigit(): current_part += "@" + code if had_equals: if int(code) >= next_variable: raise SyntaxError( f"Line {line_index + 1}: Unknown argument @{code} in translated string") else: if int(code) != next_variable: raise SyntaxError( f"Line {line_index + 1}: Arguments out of order in source, found @{code} and expected @{next_variable}." + " Arguments in source must be in increasing order, without gaps or repetitions, starting from 1") next_variable += 1 else: raise SyntaxError(f"Line {line_index + 1}: Unknown escape character: {code}") else: # @\n -> add new line line_index += 1 if line_index >= len(lines): raise SyntaxError(f"Line {line_index + 1}: Unexpected end of file") line = lines[line_index] current_part += "\n" i = 0 continue elif not had_equals and line[i] == "=": had_equals = True source = current_part current_part = "" else: current_part += line[i] i += 1 translation = current_part if not had_equals: raise SyntaxError(f"Line {line_index + 1}: Missing = in line") entries[source.strip()] = translation.strip() line_index += 1 return Translation(language, textdomain, entries)