feat: add Polish localization (#1123)
* Add Polish (pl) localization * translations * fix: add missing Polish translations * copy en keys to pl * fix: complete Polish settings translations * fix: localize PWA install prompt * fix pwa install prompt lifecycle and complete Polish locale keys * polish polish locale wording for natural phrasing --------- Co-authored-by: levy52 <levy52@vp.pl> Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
levy52
Bohdan Triapitsyn
parent
ce4c5ae99f
commit
7eff2f725f
@@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import { toast } from '@/components/ui';
|
import { toast } from '@/components/ui';
|
||||||
import { isWebRuntime } from '@/lib/desktop';
|
import { isWebRuntime } from '@/lib/desktop';
|
||||||
import { usePwaDetection } from '@/hooks/usePwaDetection';
|
import { usePwaDetection } from '@/hooks/usePwaDetection';
|
||||||
|
import { useI18n } from '@/lib/i18n';
|
||||||
import { getSafeSessionStorage } from '@/stores/utils/safeStorage';
|
import { getSafeSessionStorage } from '@/stores/utils/safeStorage';
|
||||||
|
|
||||||
type InstallPromptOutcome = 'accepted' | 'dismissed';
|
type InstallPromptOutcome = 'accepted' | 'dismissed';
|
||||||
@@ -15,6 +16,12 @@ const INSTALL_TOAST_SESSION_KEY = 'pwa-install-toast-shown';
|
|||||||
|
|
||||||
export const usePwaInstallPrompt = () => {
|
export const usePwaInstallPrompt = () => {
|
||||||
const { browserTab } = usePwaDetection();
|
const { browserTab } = usePwaDetection();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const tRef = React.useRef(t);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
tRef.current = t;
|
||||||
|
}, [t]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (typeof window === 'undefined' || !isWebRuntime() || !browserTab) {
|
if (typeof window === 'undefined' || !isWebRuntime() || !browserTab) {
|
||||||
@@ -44,7 +51,7 @@ export const usePwaInstallPrompt = () => {
|
|||||||
await promptEvent.prompt();
|
await promptEvent.prompt();
|
||||||
const { outcome } = await promptEvent.userChoice;
|
const { outcome } = await promptEvent.userChoice;
|
||||||
if (outcome === 'accepted') {
|
if (outcome === 'accepted') {
|
||||||
toast.success('Install started');
|
toast.success(tRef.current('pwa.installPrompt.started'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,10 +75,10 @@ export const usePwaInstallPrompt = () => {
|
|||||||
|
|
||||||
sessionStorage.setItem(INSTALL_TOAST_SESSION_KEY, 'true');
|
sessionStorage.setItem(INSTALL_TOAST_SESSION_KEY, 'true');
|
||||||
|
|
||||||
installToastId = toast.info('Install OpenChamber for quicker access', {
|
installToastId = toast.info(tRef.current('pwa.installPrompt.description'), {
|
||||||
duration: Infinity,
|
duration: Infinity,
|
||||||
action: {
|
action: {
|
||||||
label: 'Install',
|
label: tRef.current('pwa.installPrompt.action'),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
void triggerInstall();
|
void triggerInstall();
|
||||||
},
|
},
|
||||||
@@ -82,7 +89,7 @@ export const usePwaInstallPrompt = () => {
|
|||||||
const onAppInstalled = () => {
|
const onAppInstalled = () => {
|
||||||
deferredPrompt = null;
|
deferredPrompt = null;
|
||||||
dismissInstallToast();
|
dismissInstallToast();
|
||||||
toast.success('OpenChamber installed');
|
toast.success(tRef.current('pwa.installPrompt.installed'));
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('beforeinstallprompt', onBeforeInstallPrompt as EventListener);
|
window.addEventListener('beforeinstallprompt', onBeforeInstallPrompt as EventListener);
|
||||||
|
|||||||
@@ -10,9 +10,14 @@ export const dict = {
|
|||||||
'common.language.spanish': 'Spanish',
|
'common.language.spanish': 'Spanish',
|
||||||
'common.language.brazilianPortuguese': 'Brazilian Portuguese',
|
'common.language.brazilianPortuguese': 'Brazilian Portuguese',
|
||||||
'common.language.korean': 'Korean',
|
'common.language.korean': 'Korean',
|
||||||
|
'common.language.polish': 'Polish',
|
||||||
'common.revealPath.finder': 'Reveal in Finder',
|
'common.revealPath.finder': 'Reveal in Finder',
|
||||||
'common.revealPath.fileExplorer': 'Open in File Explorer',
|
'common.revealPath.fileExplorer': 'Open in File Explorer',
|
||||||
'common.revealPath.fileManager': 'Open in File Manager',
|
'common.revealPath.fileManager': 'Open in File Manager',
|
||||||
|
'pwa.installPrompt.description': 'Install OpenChamber for quicker access',
|
||||||
|
'pwa.installPrompt.action': 'Install',
|
||||||
|
'pwa.installPrompt.started': 'Install started',
|
||||||
|
'pwa.installPrompt.installed': 'OpenChamber installed',
|
||||||
'layout.mainTab.chat': 'Chat',
|
'layout.mainTab.chat': 'Chat',
|
||||||
'layout.mainTab.plan': 'Plan',
|
'layout.mainTab.plan': 'Plan',
|
||||||
'layout.mainTab.diff': 'Diff',
|
'layout.mainTab.diff': 'Diff',
|
||||||
|
|||||||
@@ -11,9 +11,14 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"common.language.spanish": "Español",
|
"common.language.spanish": "Español",
|
||||||
"common.language.brazilianPortuguese": "Portugués brasileño",
|
"common.language.brazilianPortuguese": "Portugués brasileño",
|
||||||
"common.language.korean": "Coreano",
|
"common.language.korean": "Coreano",
|
||||||
|
"common.language.polish": "Polaco",
|
||||||
"common.revealPath.finder": "Mostrar en Finder",
|
"common.revealPath.finder": "Mostrar en Finder",
|
||||||
"common.revealPath.fileExplorer": "Abrir en File Explorer",
|
"common.revealPath.fileExplorer": "Abrir en File Explorer",
|
||||||
"common.revealPath.fileManager": "Abrir en gestor de archivos",
|
"common.revealPath.fileManager": "Abrir en gestor de archivos",
|
||||||
|
"pwa.installPrompt.description": "Instala OpenChamber para acceder más rápido",
|
||||||
|
"pwa.installPrompt.action": "Instalar",
|
||||||
|
"pwa.installPrompt.started": "Instalación iniciada",
|
||||||
|
"pwa.installPrompt.installed": "OpenChamber se instaló",
|
||||||
"layout.mainTab.chat": "Chat",
|
"layout.mainTab.chat": "Chat",
|
||||||
"layout.mainTab.plan": "Plan",
|
"layout.mainTab.plan": "Plan",
|
||||||
"layout.mainTab.diff": "Diff",
|
"layout.mainTab.diff": "Diff",
|
||||||
|
|||||||
@@ -11,9 +11,14 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'common.language.spanish': '스페인어',
|
'common.language.spanish': '스페인어',
|
||||||
'common.language.brazilianPortuguese': '브라질 포르투갈어',
|
'common.language.brazilianPortuguese': '브라질 포르투갈어',
|
||||||
'common.language.korean': '한국어',
|
'common.language.korean': '한국어',
|
||||||
|
'common.language.polish': '폴란드어',
|
||||||
'common.revealPath.finder': 'Finder에서 보기',
|
'common.revealPath.finder': 'Finder에서 보기',
|
||||||
'common.revealPath.fileExplorer': 'File Explorer에서 열기',
|
'common.revealPath.fileExplorer': 'File Explorer에서 열기',
|
||||||
'common.revealPath.fileManager': '파일 관리자에서 열기',
|
'common.revealPath.fileManager': '파일 관리자에서 열기',
|
||||||
|
'pwa.installPrompt.description': '더 빠르게 접근하려면 OpenChamber를 설치하세요',
|
||||||
|
'pwa.installPrompt.action': '설치',
|
||||||
|
'pwa.installPrompt.started': '설치가 시작되었습니다',
|
||||||
|
'pwa.installPrompt.installed': 'OpenChamber가 설치되었습니다',
|
||||||
'layout.mainTab.chat': '채팅',
|
'layout.mainTab.chat': '채팅',
|
||||||
'layout.mainTab.plan': '계획',
|
'layout.mainTab.plan': '계획',
|
||||||
'layout.mainTab.diff': '변경사항',
|
'layout.mainTab.diff': '변경사항',
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -11,9 +11,14 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"common.language.spanish": "Espanhol",
|
"common.language.spanish": "Espanhol",
|
||||||
"common.language.brazilianPortuguese": "Português brasileiro",
|
"common.language.brazilianPortuguese": "Português brasileiro",
|
||||||
"common.language.korean": "Coreano",
|
"common.language.korean": "Coreano",
|
||||||
|
"common.language.polish": "Polonês",
|
||||||
"common.revealPath.finder": "Mostrar no Finder",
|
"common.revealPath.finder": "Mostrar no Finder",
|
||||||
"common.revealPath.fileExplorer": "Abrir no File Explorer",
|
"common.revealPath.fileExplorer": "Abrir no File Explorer",
|
||||||
"common.revealPath.fileManager": "Abrir no gerenciador de arquivos",
|
"common.revealPath.fileManager": "Abrir no gerenciador de arquivos",
|
||||||
|
"pwa.installPrompt.description": "Instale o OpenChamber para acessar mais rápido",
|
||||||
|
"pwa.installPrompt.action": "Instalar",
|
||||||
|
"pwa.installPrompt.started": "Instalação iniciada",
|
||||||
|
"pwa.installPrompt.installed": "OpenChamber instalado",
|
||||||
"layout.mainTab.chat": "Chat",
|
"layout.mainTab.chat": "Chat",
|
||||||
"layout.mainTab.plan": "Plano",
|
"layout.mainTab.plan": "Plano",
|
||||||
"layout.mainTab.diff": "Diff",
|
"layout.mainTab.diff": "Diff",
|
||||||
|
|||||||
@@ -11,9 +11,14 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"common.language.spanish": "Іспанська",
|
"common.language.spanish": "Іспанська",
|
||||||
"common.language.brazilianPortuguese": "Бразильська португальська",
|
"common.language.brazilianPortuguese": "Бразильська португальська",
|
||||||
"common.language.korean": "Корейська",
|
"common.language.korean": "Корейська",
|
||||||
|
"common.language.polish": "Польська",
|
||||||
"common.revealPath.finder": "Показати у Finder",
|
"common.revealPath.finder": "Показати у Finder",
|
||||||
"common.revealPath.fileExplorer": "Відкрити у File Explorer",
|
"common.revealPath.fileExplorer": "Відкрити у File Explorer",
|
||||||
"common.revealPath.fileManager": "Відкрити у файловому менеджері",
|
"common.revealPath.fileManager": "Відкрити у файловому менеджері",
|
||||||
|
"pwa.installPrompt.description": "Установіть OpenChamber для швидшого доступу",
|
||||||
|
"pwa.installPrompt.action": "Установити",
|
||||||
|
"pwa.installPrompt.started": "Установлення розпочато",
|
||||||
|
"pwa.installPrompt.installed": "OpenChamber встановлено",
|
||||||
"layout.mainTab.chat": "Чат",
|
"layout.mainTab.chat": "Чат",
|
||||||
"layout.mainTab.plan": "План",
|
"layout.mainTab.plan": "План",
|
||||||
"layout.mainTab.diff": "Diff",
|
"layout.mainTab.diff": "Diff",
|
||||||
|
|||||||
@@ -11,9 +11,14 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'common.language.spanish': '西班牙语',
|
'common.language.spanish': '西班牙语',
|
||||||
'common.language.brazilianPortuguese': '巴西葡萄牙语',
|
'common.language.brazilianPortuguese': '巴西葡萄牙语',
|
||||||
'common.language.korean': '韩语',
|
'common.language.korean': '韩语',
|
||||||
|
'common.language.polish': '波兰语',
|
||||||
'common.revealPath.finder': '在 Finder 中显示',
|
'common.revealPath.finder': '在 Finder 中显示',
|
||||||
'common.revealPath.fileExplorer': '在文件资源管理器中打开',
|
'common.revealPath.fileExplorer': '在文件资源管理器中打开',
|
||||||
'common.revealPath.fileManager': '在文件管理器中打开',
|
'common.revealPath.fileManager': '在文件管理器中打开',
|
||||||
|
'pwa.installPrompt.description': '安装 OpenChamber 以便更快访问',
|
||||||
|
'pwa.installPrompt.action': '安装',
|
||||||
|
'pwa.installPrompt.started': '已开始安装',
|
||||||
|
'pwa.installPrompt.installed': 'OpenChamber 已安装',
|
||||||
'layout.mainTab.chat': '聊天',
|
'layout.mainTab.chat': '聊天',
|
||||||
'layout.mainTab.plan': '计划',
|
'layout.mainTab.plan': '计划',
|
||||||
'layout.mainTab.diff': '差异',
|
'layout.mainTab.diff': '差异',
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
export type Locale = 'en' | 'zh-CN' | 'uk' | 'es' | 'pt-BR' | 'ko';
|
export type Locale = 'en' | 'zh-CN' | 'uk' | 'es' | 'pt-BR' | 'ko' | 'pl';
|
||||||
|
|
||||||
export const LOCALES = ['en', 'zh-CN', 'uk', 'es', 'pt-BR', 'ko'] as const satisfies readonly Locale[];
|
export const LOCALES = ['en', 'zh-CN', 'uk', 'es', 'pt-BR', 'ko', 'pl'] as const satisfies readonly Locale[];
|
||||||
|
|
||||||
export const DEFAULT_LOCALE: Locale = 'en';
|
export const DEFAULT_LOCALE: Locale = 'en';
|
||||||
|
|
||||||
export const LOCALE_LABEL_KEYS: Record<Locale, 'common.language.english' | 'common.language.simplifiedChinese' | 'common.language.ukrainian' | 'common.language.spanish' | 'common.language.brazilianPortuguese' | 'common.language.korean'> = {
|
export const LOCALE_LABEL_KEYS: Record<Locale, 'common.language.english' | 'common.language.simplifiedChinese' | 'common.language.ukrainian' | 'common.language.spanish' | 'common.language.brazilianPortuguese' | 'common.language.korean' | 'common.language.polish'> = {
|
||||||
en: 'common.language.english',
|
en: 'common.language.english',
|
||||||
'zh-CN': 'common.language.simplifiedChinese',
|
'zh-CN': 'common.language.simplifiedChinese',
|
||||||
uk: 'common.language.ukrainian',
|
uk: 'common.language.ukrainian',
|
||||||
es: 'common.language.spanish',
|
es: 'common.language.spanish',
|
||||||
'pt-BR': 'common.language.brazilianPortuguese',
|
'pt-BR': 'common.language.brazilianPortuguese',
|
||||||
ko: 'common.language.korean',
|
ko: 'common.language.korean',
|
||||||
|
pl: 'common.language.polish',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LOCALE_STORAGE_KEY = 'openchamber.i18n.v1';
|
export const LOCALE_STORAGE_KEY = 'openchamber.i18n.v1';
|
||||||
@@ -46,6 +47,9 @@ export function normalizeLocale(value: string | undefined | null): Locale {
|
|||||||
if (normalized === 'ko' || normalized.startsWith('ko-')) {
|
if (normalized === 'ko' || normalized.startsWith('ko-')) {
|
||||||
return 'ko';
|
return 'ko';
|
||||||
}
|
}
|
||||||
|
if (normalized === 'pl' || normalized.startsWith('pl-')) {
|
||||||
|
return 'pl';
|
||||||
|
}
|
||||||
return DEFAULT_LOCALE;
|
return DEFAULT_LOCALE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,13 +25,15 @@ async function loadDictionary(locale: Locale): Promise<I18nDictionary> {
|
|||||||
? await import('./messages/zh-CN') as { dict: I18nDictionary }
|
? await import('./messages/zh-CN') as { dict: I18nDictionary }
|
||||||
: locale === 'es'
|
: locale === 'es'
|
||||||
? await import('./messages/es') as { dict: I18nDictionary }
|
? await import('./messages/es') as { dict: I18nDictionary }
|
||||||
: locale === 'pt-BR'
|
: locale === 'pt-BR'
|
||||||
? await import('./messages/pt-BR') as { dict: I18nDictionary }
|
? await import('./messages/pt-BR') as { dict: I18nDictionary }
|
||||||
: locale === 'uk'
|
: locale === 'uk'
|
||||||
? await import('./messages/uk') as { dict: I18nDictionary }
|
? await import('./messages/uk') as { dict: I18nDictionary }
|
||||||
: locale === 'ko'
|
: locale === 'ko'
|
||||||
? await import('./messages/ko') as { dict: I18nDictionary }
|
? await import('./messages/ko') as { dict: I18nDictionary }
|
||||||
: { dict: enDict };
|
: locale === 'pl'
|
||||||
|
? await import('./messages/pl') as { dict: I18nDictionary }
|
||||||
|
: { dict: enDict };
|
||||||
dictionaries.set(locale, mod.dict);
|
dictionaries.set(locale, mod.dict);
|
||||||
return mod.dict;
|
return mod.dict;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user