Created
January 18, 2024 10:49
-
-
Save akfreas/28fd30f6ae91f79003a0bf0b2beb8bf4 to your computer and use it in GitHub Desktop.
Auto Translate iOS Strings
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import argparse | |
import os | |
import openai | |
from pprint import pprint | |
import os | |
from openai import OpenAI | |
from tqdm import tqdm | |
client = OpenAI( | |
# This is the default and can be omitted | |
api_key=os.environ.get("OPENAI_API_KEY"), | |
) | |
def translate_text(text, source_lang, target_lang_code, app_context, filename_comment): | |
languages = { | |
"en": "English", | |
"es": "Spanish", | |
"fr": "French", | |
"de": "German", | |
"it": "Italian", | |
"ja": "Japanese", | |
"ko": "Korean", | |
"pt": "Portuguese", | |
"ru": "Russian", | |
} | |
target_lang = languages[target_lang_code] | |
prompt = f"Translate the following iOS app strings from {source_lang} to {target_lang}, keeping the format intact.\n{text}" | |
system = f""" | |
You are translating strings from an app from English to {target_lang}. | |
Do not translate the keys (left side of the = sign), only the values. | |
If the string is already in {target_lang}, and the meaning is not significantly different than the existing string, you can leave it as is. | |
{app_context} | |
""" | |
messages = [ | |
{"role": "system", "content": system}, | |
{"role": "user", "content": prompt} | |
] | |
print("Translating chunk:", filename_comment) | |
response = client.chat.completions.create(model="gpt-4-1106-preview", messages=messages) | |
print(f"Finished translating chunk: {filename_comment}. Used {response.usage.total_tokens} tokens.") | |
return response.choices[0].message.content | |
def parse_localization_file(file_path): | |
with open(file_path, 'r', encoding='utf-8') as file: | |
lines = file.readlines() | |
localization_dict = {} | |
current_file = None | |
for line in lines: | |
line = line.strip() | |
if line.startswith('/*') and line.endswith('*/'): | |
current_file = line[2:-2].strip() | |
elif '=' in line: | |
key_value = line.split('=') | |
key = key_value[0].strip().strip('"') | |
value = key_value[1].strip().strip('";') | |
if current_file not in localization_dict: | |
localization_dict[current_file] = [] | |
localization_dict[current_file].append({'key': key, 'value': value}) | |
return localization_dict | |
def write_translations(localization_dict, target_lang_code, app_context_path, target_directory): | |
if "lproj" in target_directory: | |
print("Target directory should be the directory that contains the lproj directories, not the lproj directory itself") | |
return | |
if not os.path.exists(target_directory): | |
print("Target directory not found") | |
return | |
directory = os.path.join(target_directory, f"{target_lang_code}.lproj") | |
if os.path.exists(directory): | |
print(f"Target directory {directory} already exists. Overwrite? [y/n]") | |
response = input() | |
if response.lower() != 'y': | |
return | |
if not os.path.exists(app_context_path): | |
print("App context file not found") | |
return | |
with open(app_context_path, 'r', encoding='utf-8') as file: | |
app_context = file.read() | |
os.makedirs(directory, exist_ok=True) | |
with open(os.path.join(directory, "Localizable.strings"), 'w', encoding='utf-8') as file: | |
# Wrap localization_dict.items() with tqdm for progress tracking | |
for filename_comment, texts in tqdm(localization_dict.items(), desc="Processing", unit="chunk"): | |
text = list(map(lambda text: f"\"{text['key']}\" = \"{text['value']}\";", texts)) | |
joined = "\n".join(text) | |
translated_text = translate_text(joined, "English", target_lang_code, app_context, filename_comment) | |
file.write(f"/* {filename_comment} */\n{translated_text} \n\n") | |
print("Wrote translated chunk to file:", filename_comment) | |
def main(): | |
parser = argparse.ArgumentParser(description="Localize iOS app strings") | |
parser.add_argument("--source_file", type=str, help="Path to the source localization file", required=True) | |
parser.add_argument("--target_dir", type=str, help="Path to the target strings resource directory", required=True) | |
parser.add_argument("--target_lang_code", type=str, help="Target language for translation", required=True) | |
parser.add_argument("--app_context", type=str, help="Path to the app context file that describes the app that the strings are from", required=True) | |
args = parser.parse_args() | |
localization_data = parse_localization_file(args.source_file) | |
write_translations(localization_data, args.target_lang_code, args.app_context, args.target_dir) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment