Merge develop into main (#305)
## New features - Implement translations for apps - Created `tt::settings::setLanguage` and `::getLanguage()` - External app errors are now reported to the user via an AlertDialog - Store system settings in `/data/settings.properties` - Created a "Region & Language" app and moved the timezone setting there. ## Other changes - Change `/data` and `/system` filesystem sector size from 4096 to 512 bytes to allow for more small files (60+ files of 4kB were over the limit of 256kB for the filesystem) - Increased size of `/data` and `/system` - Moved `tt::time::*` to `tt::settings` - Removed the timezone setting from the "Time & Date" setting app - Reverse encoder direction of Lilygo T-Lora Pager - Improved partability of `Time.cpp` (removed separate set of functions for PC/sim)
This commit is contained in:
committed by
GitHub
parent
ee5a5a7181
commit
8c8ccd8783
@@ -0,0 +1,2 @@
|
||||
*.csv
|
||||
*.ods#
|
||||
@@ -0,0 +1,25 @@
|
||||
# Translations
|
||||
|
||||
## Requirements
|
||||
|
||||
- Install [LibreOffice Calc](https://libreoffice.org/)
|
||||
- Ensure you have Python 3 installed (Python 2 is not supported)
|
||||
|
||||
## Steps
|
||||
|
||||
To add new translations or edit existing ones, please follow these steps:
|
||||
|
||||
1. Edit `Translations.ods` (see chapter below)
|
||||
2. In LibreOffice Calc, select the relevant tab that you want to export
|
||||
3. Click on `File` -> `Save a copy...` and save the file as `[tabname].csv` (without the "[]")
|
||||
4. Repeat step 2 and 3 for all tabs that you updated
|
||||
5. Run `python generate-all.py`
|
||||
|
||||
## Notes
|
||||
|
||||
- Do not commit the CSV files
|
||||
- When editing the ODS file, make sure you don't paste in formatted data (use CTRL+Shift+V instead of CTRL+V)
|
||||
- ODS export settings:
|
||||
- Field delimiter: `,`
|
||||
- String delimiter: `"`
|
||||
- Encoding: `UTF-8`
|
||||
Binary file not shown.
@@ -0,0 +1,37 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
def get_project_root():
|
||||
return Path(__file__).parent.parent.resolve()
|
||||
|
||||
def generate(csv_file, header_file, header_namespace, data_path):
|
||||
csv_file_path = f"{get_project_root()}/Translations/{csv_file}"
|
||||
if os.path.isfile(csv_file_path):
|
||||
print(f"Processing {csv_file}")
|
||||
script_path = f"{get_project_root()}/Translations/generate.py"
|
||||
os.system(f"python {script_path} {csv_file} {header_file} {header_namespace} {data_path}")
|
||||
else:
|
||||
print(f"Skipping {csv_file} (not found)")
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Core translations
|
||||
generate(
|
||||
"Core.csv",
|
||||
"Tactility/Include/Tactility/i18n/CoreTextResources.h",
|
||||
"tt::i18n::core",
|
||||
"Data/system/i18n/core"
|
||||
)
|
||||
# Launcher app
|
||||
generate(
|
||||
"Launcher.csv",
|
||||
"Tactility/Private/Tactility/app/launcher/TextResources.h",
|
||||
"tt::app::launcher::i18n",
|
||||
"Data/system/app/Launcher/i18n"
|
||||
)
|
||||
# LocaleSettings app
|
||||
generate(
|
||||
"LocaleSettings.csv",
|
||||
"Tactility/Private/Tactility/app/localesettings/TextResources.h",
|
||||
"tt::app::localesettings::i18n",
|
||||
"Data/system/app/LocaleSettings/i18n"
|
||||
)
|
||||
@@ -0,0 +1,96 @@
|
||||
import csv
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
def get_project_root():
|
||||
return Path(__file__).parent.parent.resolve()
|
||||
|
||||
def load_csv(path: str, delimiter: str = ",", quotechar: str = '"', encoding: str = "utf-8", skip_header: bool = False) -> List[List[str]]:
|
||||
"""
|
||||
Load a CSV file into a list of rows, where each row is a list of strings.
|
||||
|
||||
Args:
|
||||
path: Path to the CSV file.
|
||||
delimiter: Field delimiter character.
|
||||
quotechar: Quote character.
|
||||
encoding: File encoding.
|
||||
skip_header: If True, skip the first row (header).
|
||||
|
||||
Returns:
|
||||
List of rows (list of lists of strings).
|
||||
"""
|
||||
rows: List[List[str]] = []
|
||||
with open(path, "r", encoding=encoding, newline="") as f:
|
||||
reader = csv.reader(f, delimiter=delimiter, quotechar=quotechar)
|
||||
if skip_header:
|
||||
next(reader, None)
|
||||
for row in reader:
|
||||
rows.append(row)
|
||||
return rows
|
||||
|
||||
def print_help():
|
||||
print("Usage: python generate.py [csv_file] [header_file_path] [header_namespace] [i18n_directory]\n\n")
|
||||
print("\t[csv_file] the CSV file containing the translations, exported from the .ods file")
|
||||
print("\t[header_file_path] the path to the header file to be generated")
|
||||
print("\t[header_namespace] the C++ namespace to use for the generated header file")
|
||||
print("\t[i18n_directory] the directory where the .i18n files will be generated")
|
||||
|
||||
def open_i18n_files(row, i18n_path):
|
||||
result = []
|
||||
for i in range(1, len(row)):
|
||||
filepath = f"{i18n_path}/{row[i]}.i18n"
|
||||
print(f"Opening {filepath}")
|
||||
file = open(filepath, "w")
|
||||
result.append(file)
|
||||
return result
|
||||
|
||||
def close_i18n_files(files):
|
||||
for file in files:
|
||||
file.close()
|
||||
|
||||
def generate_header(filepath, namespace, rows):
|
||||
file = open(filepath, "w")
|
||||
file.write("#pragma once\n\n")
|
||||
file.write("#include \"Tactility/i18n/TextResources.h\"\n\n")
|
||||
file.write("// WARNING: This file is auto-generated. Do not edit manually.\n\n")
|
||||
file.write(f"namespace {namespace}")
|
||||
file.write(" {\n\n")
|
||||
file.write("enum class Text {\n")
|
||||
for i in range(1, len(rows)):
|
||||
key = rows[i][0].upper()
|
||||
file.write(f" {key} = {i - 1},\n")
|
||||
file.write("};\n")
|
||||
file.write("\n}\n")
|
||||
file.close()
|
||||
|
||||
def translate(rows, language_index, file):
|
||||
for i in range(1, len(rows)):
|
||||
value = rows[i][language_index]
|
||||
if value == "":
|
||||
value = f"{rows[i][0]}_untranslated"
|
||||
file.write(value)
|
||||
file.write("\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if "--help" in sys.argv:
|
||||
print_help()
|
||||
sys.exit()
|
||||
if len(sys.argv) != 5:
|
||||
print_help()
|
||||
sys.exit()
|
||||
project_root_path = get_project_root()
|
||||
csv_file = f"{project_root_path}/Translations/{sys.argv[1]}"
|
||||
header_path = f"{project_root_path}/{sys.argv[2]}"
|
||||
header_namespace = sys.argv[3]
|
||||
i18n_path = f"{project_root_path}/{sys.argv[4]}"
|
||||
rows = load_csv(csv_file)
|
||||
if len(rows) == 0:
|
||||
print("Error: CSV file is empty.")
|
||||
sys.exit(1)
|
||||
generate_header(header_path, header_namespace, rows)
|
||||
i18n_files = open_i18n_files(rows[0], i18n_path)
|
||||
for i in range(0, len(i18n_files)):
|
||||
i18n_file = i18n_files[i]
|
||||
translate(rows, i + 1, i18n_file)
|
||||
close_i18n_files(i18n_files)
|
||||
Reference in New Issue
Block a user