| 
									
										
										
										
											2022-03-19 16:33:55 -08:00
										 |  |  |  | import pathlib | 
					
						
							| 
									
										
										
										
											2024-01-19 10:56:36 -06:00
										 |  |  |  | from dataclasses import dataclass | 
					
						
							| 
									
										
										
										
											2022-10-18 14:49:41 -08:00
										 |  |  |  | from pathlib import Path | 
					
						
							| 
									
										
										
										
											2022-03-19 16:33:55 -08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | import dotenv | 
					
						
							|  |  |  |  | import requests | 
					
						
							|  |  |  |  | from jinja2 import Template | 
					
						
							| 
									
										
										
										
											2024-03-10 12:58:52 -05:00
										 |  |  |  | from pydantic import ConfigDict | 
					
						
							| 
									
										
										
										
											2022-03-19 16:33:55 -08:00
										 |  |  |  | from requests import Response | 
					
						
							| 
									
										
										
										
											2022-10-18 14:49:41 -08:00
										 |  |  |  | from utils import CodeDest, CodeKeys, inject_inline, log | 
					
						
							| 
									
										
										
										
											2022-03-19 16:33:55 -08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-25 10:56:49 -08:00
										 |  |  |  | from mealie.schema._mealie import MealieModel | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-19 16:33:55 -08:00
										 |  |  |  | BASE = pathlib.Path(__file__).parent.parent.parent | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | API_KEY = dotenv.get_key(BASE / ".env", "CROWDIN_API_KEY") | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-02 10:41:44 -08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-19 10:56:36 -06:00
										 |  |  |  | @dataclass | 
					
						
							|  |  |  |  | class LocaleData: | 
					
						
							|  |  |  |  |     name: str | 
					
						
							|  |  |  |  |     dir: str = "ltr" | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | LOCALE_DATA: dict[str, LocaleData] = { | 
					
						
							|  |  |  |  |     "en-US": LocaleData(name="American English"), | 
					
						
							|  |  |  |  |     "en-GB": LocaleData(name="British English"), | 
					
						
							|  |  |  |  |     "af-ZA": LocaleData(name="Afrikaans (Afrikaans)"), | 
					
						
							|  |  |  |  |     "ar-SA": LocaleData(name="العربية (Arabic)", dir="rtl"), | 
					
						
							|  |  |  |  |     "ca-ES": LocaleData(name="Català (Catalan)"), | 
					
						
							|  |  |  |  |     "cs-CZ": LocaleData(name="Čeština (Czech)"), | 
					
						
							|  |  |  |  |     "da-DK": LocaleData(name="Dansk (Danish)"), | 
					
						
							|  |  |  |  |     "de-DE": LocaleData(name="Deutsch (German)"), | 
					
						
							|  |  |  |  |     "el-GR": LocaleData(name="Ελληνικά (Greek)"), | 
					
						
							|  |  |  |  |     "es-ES": LocaleData(name="Español (Spanish)"), | 
					
						
							|  |  |  |  |     "fi-FI": LocaleData(name="Suomi (Finnish)"), | 
					
						
							|  |  |  |  |     "fr-FR": LocaleData(name="Français (French)"), | 
					
						
							| 
									
										
										
										
											2024-08-08 16:50:14 +02:00
										 |  |  |  |     "fr-BE": LocaleData(name="Belge (Belgian)"), | 
					
						
							| 
									
										
										
										
											2024-04-19 05:42:50 -05:00
										 |  |  |  |     "gl-ES": LocaleData(name="Galego (Galician)"), | 
					
						
							| 
									
										
										
										
											2024-01-19 10:56:36 -06:00
										 |  |  |  |     "he-IL": LocaleData(name="עברית (Hebrew)", dir="rtl"), | 
					
						
							| 
									
										
										
										
											2024-04-19 05:42:50 -05:00
										 |  |  |  |     "hr-HR": LocaleData(name="Hrvatski (Croatian)"), | 
					
						
							| 
									
										
										
										
											2024-01-19 10:56:36 -06:00
										 |  |  |  |     "hu-HU": LocaleData(name="Magyar (Hungarian)"), | 
					
						
							| 
									
										
										
										
											2024-04-19 05:42:50 -05:00
										 |  |  |  |     "is-IS": LocaleData(name="Íslenska (Icelandic)"), | 
					
						
							| 
									
										
										
										
											2024-01-19 10:56:36 -06:00
										 |  |  |  |     "it-IT": LocaleData(name="Italiano (Italian)"), | 
					
						
							|  |  |  |  |     "ja-JP": LocaleData(name="日本語 (Japanese)"), | 
					
						
							|  |  |  |  |     "ko-KR": LocaleData(name="한국어 (Korean)"), | 
					
						
							| 
									
										
										
										
											2024-04-19 05:42:50 -05:00
										 |  |  |  |     "lt-LT": LocaleData(name="Lietuvių (Lithuanian)"), | 
					
						
							|  |  |  |  |     "lv-LV": LocaleData(name="Latviešu (Latvian)"), | 
					
						
							| 
									
										
										
										
											2024-01-19 10:56:36 -06:00
										 |  |  |  |     "nl-NL": LocaleData(name="Nederlands (Dutch)"), | 
					
						
							| 
									
										
										
										
											2024-04-19 05:42:50 -05:00
										 |  |  |  |     "no-NO": LocaleData(name="Norsk (Norwegian)"), | 
					
						
							| 
									
										
										
										
											2024-01-19 10:56:36 -06:00
										 |  |  |  |     "pl-PL": LocaleData(name="Polski (Polish)"), | 
					
						
							|  |  |  |  |     "pt-BR": LocaleData(name="Português do Brasil (Brazilian Portuguese)"), | 
					
						
							|  |  |  |  |     "pt-PT": LocaleData(name="Português (Portuguese)"), | 
					
						
							|  |  |  |  |     "ro-RO": LocaleData(name="Română (Romanian)"), | 
					
						
							|  |  |  |  |     "ru-RU": LocaleData(name="Pусский (Russian)"), | 
					
						
							| 
									
										
										
										
											2024-04-19 05:42:50 -05:00
										 |  |  |  |     "sl-SI": LocaleData(name="Slovenščina (Slovenian)"), | 
					
						
							| 
									
										
										
										
											2024-01-19 10:56:36 -06:00
										 |  |  |  |     "sr-SP": LocaleData(name="српски (Serbian)"), | 
					
						
							|  |  |  |  |     "sv-SE": LocaleData(name="Svenska (Swedish)"), | 
					
						
							|  |  |  |  |     "tr-TR": LocaleData(name="Türkçe (Turkish)"), | 
					
						
							|  |  |  |  |     "uk-UA": LocaleData(name="Українська (Ukrainian)"), | 
					
						
							|  |  |  |  |     "vi-VN": LocaleData(name="Tiếng Việt (Vietnamese)"), | 
					
						
							|  |  |  |  |     "zh-CN": LocaleData(name="简体中文 (Chinese simplified)"), | 
					
						
							|  |  |  |  |     "zh-TW": LocaleData(name="繁體中文 (Chinese traditional)"), | 
					
						
							| 
									
										
										
										
											2022-03-19 16:33:55 -08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-10 12:58:52 -05:00
										 |  |  |  | LOCALE_TEMPLATE = """// This Code is auto generated by gen_ts_locales.py
 | 
					
						
							| 
									
										
										
										
											2022-03-19 16:33:55 -08:00
										 |  |  |  | export const LOCALES = [{% for locale in locales %} | 
					
						
							|  |  |  |  |   { | 
					
						
							|  |  |  |  |     name: "{{ locale.name }}", | 
					
						
							|  |  |  |  |     value: "{{ locale.locale }}", | 
					
						
							|  |  |  |  |     progress: {{ locale.progress }}, | 
					
						
							| 
									
										
										
										
											2024-01-19 10:56:36 -06:00
										 |  |  |  |     dir: "{{ locale.dir }}", | 
					
						
							| 
									
										
										
										
											2022-03-19 16:33:55 -08:00
										 |  |  |  |   },{% endfor %} | 
					
						
							|  |  |  |  | ] | 
					
						
							| 
									
										
										
										
											2022-08-02 10:41:44 -08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-19 16:33:55 -08:00
										 |  |  |  | """
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-25 10:56:49 -08:00
										 |  |  |  | class TargetLanguage(MealieModel): | 
					
						
							| 
									
										
										
										
											2024-03-10 12:58:52 -05:00
										 |  |  |  |     model_config = ConfigDict(populate_by_name=True, extra="allow") | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-19 16:33:55 -08:00
										 |  |  |  |     id: str | 
					
						
							|  |  |  |  |     name: str | 
					
						
							|  |  |  |  |     locale: str | 
					
						
							| 
									
										
										
										
											2024-01-20 10:34:57 -06:00
										 |  |  |  |     dir: str = "ltr" | 
					
						
							| 
									
										
										
										
											2022-03-19 16:33:55 -08:00
										 |  |  |  |     threeLettersCode: str | 
					
						
							|  |  |  |  |     twoLettersCode: str | 
					
						
							|  |  |  |  |     progress: float = 0.0 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | class CrowdinApi: | 
					
						
							|  |  |  |  |     project_name = "Mealie" | 
					
						
							|  |  |  |  |     project_id = "451976" | 
					
						
							|  |  |  |  |     api_key = API_KEY | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def __init__(self, api_key: str): | 
					
						
							|  |  |  |  |         api_key = api_key | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     @property | 
					
						
							|  |  |  |  |     def headers(self) -> dict: | 
					
						
							|  |  |  |  |         return { | 
					
						
							|  |  |  |  |             "Content-Type": "application/json", | 
					
						
							|  |  |  |  |             "Authorization": f"Bearer {self.api_key}", | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def get_projects(self) -> Response: | 
					
						
							|  |  |  |  |         return requests.get("https://api.crowdin.com/api/v2/projects", headers=self.headers) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def get_project(self) -> Response: | 
					
						
							|  |  |  |  |         return requests.get(f"https://api.crowdin.com/api/v2/projects/{self.project_id}", headers=self.headers) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def get_languages(self) -> list[TargetLanguage]: | 
					
						
							|  |  |  |  |         response = self.get_project() | 
					
						
							|  |  |  |  |         tls = response.json()["data"]["targetLanguages"] | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         models = [TargetLanguage(**t) for t in tls] | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         models.insert( | 
					
						
							|  |  |  |  |             0, | 
					
						
							|  |  |  |  |             TargetLanguage( | 
					
						
							| 
									
										
										
										
											2024-01-19 10:56:36 -06:00
										 |  |  |  |                 id="en-US", | 
					
						
							|  |  |  |  |                 name="English", | 
					
						
							|  |  |  |  |                 locale="en-US", | 
					
						
							|  |  |  |  |                 dir="ltr", | 
					
						
							|  |  |  |  |                 threeLettersCode="en", | 
					
						
							|  |  |  |  |                 twoLettersCode="en", | 
					
						
							|  |  |  |  |                 progress=100, | 
					
						
							| 
									
										
										
										
											2022-03-19 16:33:55 -08:00
										 |  |  |  |             ), | 
					
						
							|  |  |  |  |         ) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         progress: list[dict] = self.get_progress()["data"] | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         for model in models: | 
					
						
							| 
									
										
										
										
											2024-01-19 10:56:36 -06:00
										 |  |  |  |             if model.locale in LOCALE_DATA: | 
					
						
							|  |  |  |  |                 locale_data = LOCALE_DATA[model.locale] | 
					
						
							|  |  |  |  |                 model.name = locale_data.name | 
					
						
							|  |  |  |  |                 model.dir = locale_data.dir | 
					
						
							| 
									
										
										
										
											2022-03-19 16:33:55 -08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |             for p in progress: | 
					
						
							|  |  |  |  |                 if p["data"]["languageId"] == model.id: | 
					
						
							|  |  |  |  |                     model.progress = p["data"]["translationProgress"] | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         models.sort(key=lambda x: x.locale, reverse=True) | 
					
						
							|  |  |  |  |         return models | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     def get_progress(self) -> dict: | 
					
						
							|  |  |  |  |         response = requests.get( | 
					
						
							|  |  |  |  |             f"https://api.crowdin.com/api/v2/projects/{self.project_id}/languages/progress?limit=500", | 
					
						
							|  |  |  |  |             headers=self.headers, | 
					
						
							|  |  |  |  |         ) | 
					
						
							|  |  |  |  |         return response.json() | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-02 10:41:44 -08:00
										 |  |  |  | PROJECT_DIR = Path(__file__).parent.parent.parent | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | datetime_dir = PROJECT_DIR / "frontend" / "lang" / "dateTimeFormats" | 
					
						
							|  |  |  |  | locales_dir = PROJECT_DIR / "frontend" / "lang" / "messages" | 
					
						
							|  |  |  |  | nuxt_config = PROJECT_DIR / "frontend" / "nuxt.config.js" | 
					
						
							| 
									
										
										
										
											2024-03-10 12:58:52 -05:00
										 |  |  |  | reg_valid = PROJECT_DIR / "mealie" / "schema" / "_mealie" / "validators.py" | 
					
						
							| 
									
										
										
										
											2022-08-02 10:41:44 -08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | """
 | 
					
						
							|  |  |  |  | This snippet walks the message and dat locales directories and generates the import information | 
					
						
							|  |  |  |  | for the nuxt.config.js file and automatically injects it into the nuxt.config.js file. Note that | 
					
						
							|  |  |  |  | the code generation ID is hardcoded into the script and required in the nuxt config. | 
					
						
							|  |  |  |  | """
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | def inject_nuxt_values(): | 
					
						
							|  |  |  |  |     all_date_locales = [ | 
					
						
							|  |  |  |  |         f'"{match.stem}": require("./lang/dateTimeFormats/{match.name}"),' for match in datetime_dir.glob("*.json") | 
					
						
							|  |  |  |  |     ] | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     all_langs = [] | 
					
						
							|  |  |  |  |     for match in locales_dir.glob("*.json"): | 
					
						
							|  |  |  |  |         lang_string = f'{{ code: "{match.stem}", file: "{match.name}" }},' | 
					
						
							|  |  |  |  |         all_langs.append(lang_string) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-18 14:49:41 -08:00
										 |  |  |  |     log.debug(f"injecting locales into nuxt config -> {nuxt_config}") | 
					
						
							| 
									
										
										
										
											2022-08-02 10:41:44 -08:00
										 |  |  |  |     inject_inline(nuxt_config, CodeKeys.nuxt_local_messages, all_langs) | 
					
						
							|  |  |  |  |     inject_inline(nuxt_config, CodeKeys.nuxt_local_dates, all_date_locales) | 
					
						
							| 
									
										
										
										
											2022-03-19 16:33:55 -08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-10 12:58:52 -05:00
										 |  |  |  | def inject_registration_validation_values(): | 
					
						
							|  |  |  |  |     all_langs = [] | 
					
						
							|  |  |  |  |     for match in locales_dir.glob("*.json"): | 
					
						
							|  |  |  |  |         lang_string = f'"{match.stem}",' | 
					
						
							|  |  |  |  |         all_langs.append(lang_string) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     # sort | 
					
						
							|  |  |  |  |     all_langs.sort() | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     log.debug(f"injecting locales into user registration validation -> {reg_valid}") | 
					
						
							|  |  |  |  |     inject_inline(reg_valid, CodeKeys.nuxt_local_messages, all_langs) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-02 10:41:44 -08:00
										 |  |  |  | def generate_locales_ts_file(): | 
					
						
							| 
									
										
										
										
											2022-03-19 16:33:55 -08:00
										 |  |  |  |     api = CrowdinApi("") | 
					
						
							|  |  |  |  |     models = api.get_languages() | 
					
						
							|  |  |  |  |     tmpl = Template(LOCALE_TEMPLATE) | 
					
						
							|  |  |  |  |     rendered = tmpl.render(locales=models) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-18 14:49:41 -08:00
										 |  |  |  |     log.debug(f"generating locales ts file -> {CodeDest.use_locales}") | 
					
						
							|  |  |  |  |     with open(CodeDest.use_locales, "w") as f: | 
					
						
							| 
									
										
										
										
											2022-03-19 16:33:55 -08:00
										 |  |  |  |         f.write(rendered)  # type:ignore | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-02 10:41:44 -08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | def main(): | 
					
						
							| 
									
										
										
										
											2022-10-18 14:49:41 -08:00
										 |  |  |  |     if API_KEY is None or API_KEY == "": | 
					
						
							|  |  |  |  |         log.error("CROWDIN_API_KEY is not set") | 
					
						
							|  |  |  |  |         return | 
					
						
							| 
									
										
										
										
											2022-08-02 10:41:44 -08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-18 14:49:41 -08:00
										 |  |  |  |     generate_locales_ts_file() | 
					
						
							| 
									
										
										
										
											2022-08-02 10:41:44 -08:00
										 |  |  |  |     inject_nuxt_values() | 
					
						
							| 
									
										
										
										
											2024-03-10 12:58:52 -05:00
										 |  |  |  |     inject_registration_validation_values() | 
					
						
							| 
									
										
										
										
											2022-08-02 10:41:44 -08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-19 16:33:55 -08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |  |     main() |