From 33f758bbbc5d9de221ae1bff96257eb0cf5ec81b Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Tue, 20 May 2025 09:02:15 +0200 Subject: [PATCH] Add translations --- scripts/i18n-checker.py | 60 +++++++++++++++++++----------- src/hooks/useHandlePrivateApps.tsx | 20 +++++++--- src/i18n/locales/en/core.json | 3 ++ 3 files changed, 57 insertions(+), 26 deletions(-) diff --git a/scripts/i18n-checker.py b/scripts/i18n-checker.py index 604d222..12bd976 100644 --- a/scripts/i18n-checker.py +++ b/scripts/i18n-checker.py @@ -16,35 +16,53 @@ JSX_TEXT_REGEX = re.compile(r'>\s*([A-Z][a-z].*?)\s*<') def is_excluded(path): return any(excluded in path for excluded in EXCLUDED_DIRS) +def is_ignorable(text): + return ( + re.fullmatch(r'[A-Z0-9_]+', text) and 'action' in text + ) + +def is_console_log_line(line): + return any(kw in line for kw in ['console.log', 'console.error', 'console.warn']) + def find_untranslated_strings(file_path): issues = [] with open(file_path, 'r', encoding='utf-8') as f: content = f.read() + lines = content.splitlines() - # Match suspicious string literals - for match in STRING_LITERAL_REGEX.finditer(content): - string = match.group(1) - if not any(fn + '(' in content[match.start()-10:match.start()] for fn in I18N_FUNCTIONS): - issues.append({ - 'file': file_path, - 'position': match.start(), - 'type': 'StringLiteral', - 'text': string.strip() - }) + for idx, line in enumerate(lines, start=1): + if is_console_log_line(line): + continue # Skip entire line if it's a console log statement - # Match JSX text nodes - for match in JSX_TEXT_REGEX.finditer(content): - text = match.group(1) - if not text.strip().startswith('{t('): # naive check - issues.append({ - 'file': file_path, - 'position': match.start(), - 'type': 'JSXText', - 'text': text.strip() - }) + # Match suspicious string literals + for match in STRING_LITERAL_REGEX.finditer(line): + string = match.group(1).strip() + if is_ignorable(string): + continue + if not any(fn + '(' in line[:match.start()] for fn in I18N_FUNCTIONS): + issues.append({ + 'file': file_path, + 'line': idx, + 'type': 'StringLiteral', + 'text': string + }) + + # Match JSX text nodes + for match in JSX_TEXT_REGEX.finditer(line): + text = match.group(1).strip() + if is_ignorable(text): + continue + if not text.startswith('{t('): + issues.append({ + 'file': file_path, + 'line': idx, + 'type': 'JSXText', + 'text': text + }) return issues + def scan_directory(directory): all_issues = [] for root, _, files in os.walk(directory): @@ -64,7 +82,7 @@ def save_report(results, output_file): json.dump(results, f, indent=2) elif ext.lower() == '.csv': with open(output_file, 'w', newline='', encoding='utf-8') as f: - writer = csv.DictWriter(f, fieldnames=['file', 'position', 'type', 'text']) + writer = csv.DictWriter(f, fieldnames=['file', 'line', 'type', 'text']) writer.writeheader() for row in results: writer.writerow(row) diff --git a/src/hooks/useHandlePrivateApps.tsx b/src/hooks/useHandlePrivateApps.tsx index be79d41..4e694c0 100644 --- a/src/hooks/useHandlePrivateApps.tsx +++ b/src/hooks/useHandlePrivateApps.tsx @@ -10,6 +10,7 @@ import { saveToLocalStorage } from '../components/Apps/AppsNavBarDesktop'; import { base64ToUint8Array } from '../qdn/encryption/group-encryption'; import { uint8ArrayToObject } from '../backgroundFunctions/encryption'; import { useSetAtom } from 'jotai'; +import { useTranslation } from 'react-i18next'; export const useHandlePrivateApps = () => { const [status, setStatus] = useState(''); @@ -20,8 +21,8 @@ export const useHandlePrivateApps = () => { setInfoSnackCustom, } = useContext(MyContext); const setSortablePinnedApps = useSetAtom(sortablePinnedAppsAtom); - const setSettingsLocalLastUpdated = useSetAtom(settingsLocalLastUpdatedAtom); + const { t } = useTranslation(['auth', 'core', 'group']); const openApp = async ( privateAppProperties, @@ -29,7 +30,7 @@ export const useHandlePrivateApps = () => { setLoadingStatePrivateApp ) => { try { - if (setLoadingStatePrivateApp) { + if (setLoadingStatePrivateApp) { // TODO translate setLoadingStatePrivateApp(`Downloading and decrypting private app.`); } setOpenSnackGlobal(true); @@ -173,7 +174,7 @@ export const useHandlePrivateApps = () => { }); setInfoSnackCustom({ type: 'success', - message: 'Opened', + message: {t('core:message.generic.opened', { postProcess: 'capitalizeFirstChar' })}, }); if (setLoadingStatePrivateApp) { setLoadingStatePrivateApp(``); @@ -206,7 +207,12 @@ export const useHandlePrivateApps = () => { } catch (error) { if (setLoadingStatePrivateApp) { setLoadingStatePrivateApp( - `Error! ${error?.message || 'Unable to build private app.'}` + `Error! ${ + error?.message || + t('core:message.error.unable_build_app', { + postProcess: 'capitalizeFirstChar', + }) + }` ); } throw error; @@ -214,7 +220,11 @@ export const useHandlePrivateApps = () => { } catch (error) { setInfoSnackCustom({ type: 'error', - message: error?.message || 'Unable to fetch app', + message: + error?.message || + t('core:message.error.unable_fetch_app', { + postProcess: 'capitalizeFirstChar', + }), }); } }; diff --git a/src/i18n/locales/en/core.json b/src/i18n/locales/en/core.json index 2a63179..179cf85 100644 --- a/src/i18n/locales/en/core.json +++ b/src/i18n/locales/en/core.json @@ -172,8 +172,10 @@ "rating_option": "cannot find rating option", "save_qdn": "unable to save to QDN", "send_failed": "failed to send", + "unable_build_app": "unable to build private app", "unable_download_image": "unable to download IMAGE. Please try again later by clicking the refresh button", "unable_encrypt_app": "unable to encrypt app. App not published'", + "unable_fetch_app": "unable to build private app", "unable_publish_app": "unable to publish app", "unable_publish_image": "unable to publish image", "unable_rate": "unable to rate", @@ -215,6 +217,7 @@ "no_pinned_changes": "you currently do not have any changes to your pinned apps", "no_results": "no results", "one_app_per_name": "note: Currently, only one App and Website is allowed per Name.", + "opened": "opened", "overwrite_qdn": "overwrite to QDN", "password_confirm": "please confirm a password", "password_enter": "please enter a password",