123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- #!/usr/bin/env python3
- '''
- languageExport.py [--single]
- Export LCD language strings to CSV files for easier translation.
- Use languageImport.py to import CSV into the language files.
- Use --single to export all languages to a single CSV file.
- '''
- import re
- from pathlib import Path
- from sys import argv
- from languageUtil import namebyid
- LANGHOME = "Marlin/src/lcd/language"
- # Write multiple sheets if true, otherwise write one giant sheet
- MULTISHEET = '--single' not in argv[1:]
- OUTDIR = 'out-csv'
- # Check for the path to the language files
- if not Path(LANGHOME).is_dir():
- print("Error: Couldn't find the '%s' directory." % LANGHOME)
- print("Edit LANGHOME or cd to the root of the repo before running.")
- exit(1)
- # A limit just for testing
- LIMIT = 0
- # A dictionary to contain strings for each language.
- # Init with 'en' so English will always be first.
- language_strings = { 'en': {} }
- # A dictionary to contain all distinct LCD string names
- names = {}
- # Get all "language_*.h" files
- langfiles = sorted(list(Path(LANGHOME).glob('language_*.h')))
- # Read each language file
- for langfile in langfiles:
- # Get the language code from the filename
- langcode = langfile.name.replace('language_', '').replace('.h', '')
- # Skip 'test' and any others that we don't want
- if langcode in ['test']: continue
- # Open the file
- f = open(langfile, 'r', encoding='utf-8')
- if not f: continue
- # Flags to indicate a wide or tall section
- wideflag, tallflag = False, False
- # A counter for the number of strings in the file
- stringcount = 0
- # A dictionary to hold all the strings
- strings = { 'narrow': {}, 'wide': {}, 'tall': {} }
- # Read each line in the file
- for line in f:
- # Clean up the line for easier parsing
- line = line.split("//")[0].strip()
- if line.endswith(';'): line = line[:-1].strip()
- # Check for wide or tall sections, assume no complicated nesting
- if line.startswith("#endif") or line.startswith("#else"):
- wideflag, tallflag = False, False
- elif re.match(r'#if.*WIDTH\s*>=?\s*2[01].*', line): wideflag = True
- elif re.match(r'#if.*LCD_HEIGHT\s*>=?\s*4.*', line): tallflag = True
- # For string-defining lines capture the string data
- match = re.match(r'LSTR\s+([A-Z0-9_]+)\s*=\s*(.+)\s*', line)
- if match:
- # Name and quote-sanitized value
- name, value = match.group(1), match.group(2).replace('\\"', '$$$')
- # Remove all _UxGT wrappers from the value in a non-greedy way
- value = re.sub(r'_UxGT\((".*?")\)', r'\1', value)
- # Multi-line strings get one or more bars | for identification
- multiline = 0
- multimatch = re.match(r'.*MSG_(\d)_LINE\s*\(\s*(.+?)\s*\).*', value)
- if multimatch:
- multiline = int(multimatch.group(1))
- value = '|' + re.sub(r'"\s*,\s*"', '|', multimatch.group(2))
- # Wrap inline defines in parentheses
- value = re.sub(r' *([A-Z0-9]+_[A-Z0-9_]+) *', r'(\1)', value)
- # Remove quotes around strings
- value = re.sub(r'"(.*?)"', r'\1', value).replace('$$$', '""')
- # Store all unique names as dictionary keys
- names[name] = 1
- # Store the string as narrow or wide
- strings['tall' if tallflag else 'wide' if wideflag else 'narrow'][name] = value
- # Increment the string counter
- stringcount += 1
- # Break for testing
- if LIMIT and stringcount >= LIMIT: break
- # Close the file
- f.close()
- # Store the array in the dict
- language_strings[langcode] = strings
- # Get the language codes from the dictionary
- langcodes = list(language_strings.keys())
- # Print the array
- #print(language_strings)
- # Report the total number of unique strings
- print("Found %s distinct LCD strings." % len(names))
- # Write a single language entry to the CSV file with narrow, wide, and tall strings
- def write_csv_lang(f, strings, name):
- f.write(',')
- if name in strings['narrow']: f.write('"%s"' % strings['narrow'][name])
- f.write(',')
- if name in strings['wide']: f.write('"%s"' % strings['wide'][name])
- f.write(',')
- if name in strings['tall']: f.write('"%s"' % strings['tall'][name])
- if MULTISHEET:
- #
- # Export a separate sheet for each language
- #
- Path.mkdir(Path(OUTDIR), exist_ok=True)
- for lang in langcodes:
- with open("%s/language_%s.csv" % (OUTDIR, lang), 'w', encoding='utf-8') as f:
- lname = lang + ' ' + namebyid(lang)
- header = ['name', lname, lname + ' (wide)', lname + ' (tall)']
- f.write('"' + '","'.join(header) + '"\n')
- for name in names.keys():
- f.write('"' + name + '"')
- write_csv_lang(f, language_strings[lang], name)
- f.write('\n')
- else:
- #
- # Export one large sheet containing all languages
- #
- with open("languages.csv", 'w', encoding='utf-8') as f:
- header = ['name']
- for lang in langcodes:
- lname = lang + ' ' + namebyid(lang)
- header += [lname, lname + ' (wide)', lname + ' (tall)']
- f.write('"' + '","'.join(header) + '"\n')
- for name in names.keys():
- f.write('"' + name + '"')
- for lang in langcodes: write_csv_lang(f, language_strings[lang], name)
- f.write('\n')
|