diff --git a/next-i18next.config.js b/next-i18next.config.js index eccc62ec3..8400cb5d8 100644 --- a/next-i18next.config.js +++ b/next-i18next.config.js @@ -3,7 +3,7 @@ const path = require("path"); module.exports = { i18n: { defaultLocale: "en", - locales: ["en", "es", "de", "fr", "it", "sv", "pt-BR"], + locales: ["en", "es", "de", "fr", "it", "ko", "sv", "pt", "pt-BR"], localePath: path.resolve("./public/locales"), reloadOnPrerender: process.env.NODE_ENV === "development", }, diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 3f61b1d53..7ae2b7c3e 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -9,8 +9,10 @@ "german": "German", "home": "Home", "italian": "Italian", + "korean": "Korean", "language": "Language", "links": "Links", + "portuguese": "Portuguese", "portugueseBr": "Portuguese (Brazil)", "poweredBy": "Powered by", "privacyPolicy": "Privacy Policy", diff --git a/public/locales/ko/app.json b/public/locales/ko/app.json new file mode 100644 index 000000000..6384c6034 --- /dev/null +++ b/public/locales/ko/app.json @@ -0,0 +1,131 @@ +{ + "12h": "12시간제", + "24h": "24시간제", + "addParticipant": "참여자 추가하기", + "addTimeOption": "시간 선택지 추가하기", + "alreadyVoted": "이미 투표하였습니다", + "applyToAllDates": "모든 선택지에 투표하기", + "areYouSure": "확실합니까?", + "back": "뒤로가기", + "calendarHelp": "옵션이 없는 투표는 생성할 수 없습니다. 최소 1개의 옵션을 추가해주세요.", + "calendarHelpTitle": "잊으신게 있나요?", + "cancel": "취소하기", + "comment": "댓글 남기기", + "commentPlaceholder": "이 투표에 댓글 남기기 (모두에게 공개됩니다)", + "comments": "댓글들", + "continue": "계속하기", + "copied": "복사됨", + "copyLink": "링크 복사하기", + "createdBy": "{{name}} 에 의해 생성됨", + "createPoll": "투표 만들기", + "creatingDemo": "데모 투표 만드는 중...", + "delete": "삭제하기", + "deleteComment": "댓글 삭제하기", + "deleteDate": "날짜 삭제하기", + "deletedPoll": "삭제된 투표", + "deletedPollInfo": "이 투표는 더 이상 존재하지 않습니다.", + "deletePoll": "투표 삭제하기", + "deletePollDescription": "이 투표의 모든 데이터가 삭제됩니다. 계속하시려면 “{{confirmText}}” 를 아래 입력창에 입력해주세요.", + "deletingOptionsWarning": "해당 옵션과 참여자들의 투표 내역을 모두 삭제합니다.", + "demoPollNotice": "데모 투표는 하루 뒤에 자동으로 삭제됩니다.", + "description": "설명", + "descriptionPlaceholder": "안녕하세요, 당신이 원하는 날짜를 선택해주세요.", + "donate": "후원하기", + "edit": "수정하기", + "editDetails": "세부정보 수정하기", + "editOptions": "옵션 수정하기", + "email": "이메일", + "emailPlaceholder": "jessie.smith@email.com", + "endingGuestSessionNotice": "비회원 세션이 종료되면 되돌릴 수 없으며 이 세션에서 생성한 투표나 댓글들을 더 이상 수정할 수 없습니다.", + "endSession": "세션 종료하기", + "errorCreate": "투표 만들기에 문제가 생겼어요! 나중에 고칠 수 있도록 준비 중 입니다~!", + "exportToCsv": "CSV로 내보내기", + "finish": "완료하기", + "forgetMe": "세션 지우기", + "goToAdmin": "관리페이지로 이동하기", + "guest": "비회원", + "guestSessionNotice": "당신은 비회원 세션을 이용하고 있으며, 자신의 투표를 나중에 수정할 수 있습니다.", + "guestSessionReadMore": "비회원 세션에 대해 더 알아보기", + "hide": "숨기기", + "ifNeedBe": "꼭 필요하다면", + "linkHasExpired": "링크가 만료되었거나 더 이상 유효하지 않습니다.", + "loading": "불러오는 중...", + "loadingParticipants": "참여자를 불러오는 중...", + "location": "장소", + "locationPlaceholder": "Jeo의 카페", + "lockPoll": "투표 잠그기", + "login": "로그인", + "loginCheckInbox": "메일함을 확인해주세요.", + "loginMagicLinkSent": "매직링크가 다음으로 전송되었습니다.", + "loginSendMagicLink": "매징링크 전송하기", + "loginViaMagicLink": "매직링크로 로그인하기", + "loginViaMagicLinkDescription": "로그인 할 수 있는 매직링크가 담긴 이메일을 전송했습니다.", + "loginWithValidEmail": "올바른 이메일 주소를 입력해주세요.", + "logout": "로그아웃", + "manage": "관리하기", + "menu": "메뉴", + "mixedOptionsDescription": "한 투표에 날짜와 시간이 동일한 옵션을 추가할 수 없습니다. 어떤 옵션을 유지하시겠습니까?", + "mixedOptionsKeepDates": "날짜 옵션 유지하기", + "mixedOptionsKeepTimes": "시간 옵션 유지하기", + "mixedOptionsTitle": "잠시 기다려주세요… 🤔", + "monday": "월요일", + "monthView": "월간 보기", + "name": "이름", + "namePlaceholder": "홍길동", + "new": "신규", + "newPoll": "새 투표", + "next": "다음", + "nextMonth": "다음달", + "no": "안돼요", + "noDatesSelected": "날짜가 선택되지 않았습니다.", + "notificationsDisabled": "알림이 비활성화 되었습니다.", + "notificationsOff": "알림 꺼짐", + "notificationsOn": "알림 켜짐", + "notificationsOnDescription": "이 투표에 업데이트가 생기면 {{email}} 으로 메일이 전송됩니다.", + "notificationsVerifyEmail": "알림을 활성화하려면 이메일을 인증해주세요.", + "noVotes": "이 옵션은 아무도 선택하지 않았습니다.", + "ok": "확인", + "options": "옵션들", + "participant": "참여자", + "participantCount_other": "{{count}} 명의 참여자", + "participantCount": "{{count}} 명의 참여자", + "pollHasBeenLocked": "이 투표는 잠겼습니다.", + "pollHasBeenVerified": "투표가 확인되었습니다.", + "pollOwnerNotice": "안녕하세요 {{name}}, 이 투표의 생성자시군요.", + "pollsEmpty": "생성된 투표가 없습니다.", + "possibleAnswers": "가능한 답변들", + "preferences": "설정", + "previousMonth": "지난달", + "profileLogin": "프로필 - 로그인", + "profileUser": "프로필 - {{username}}", + "requiredNameError": "이름을 입력해주세요.", + "save": "저장하기", + "saveInstruction": "가능여부를 선택한 후 {{save}} 를 클릭하세요", + "share": "공유하기", + "shareDescription": "이 링크를 참여자들에게 전달하여 투표하도록 하세요", + "shareLink": "링크 공유하기", + "specifyTimes": "시간 지정하기", + "specifyTimesDescription": "각 날짜별로 시작시간과 종료시간을 추가합니다.", + "stepSummary": "{{total}} 단계 중 {{current}}", + "sunday": "일요일", + "timeAndDate": "날짜 & 시간", + "timeFormat": "시간 형식:", + "timeZone": "표준시간대:", + "title": "제목", + "titlePlaceholder": "월간 회의", + "today": "오늘", + "unlockPoll": "투표 잠금 해제", + "unverifiedMessage": "인증 링크가 담긴 이메일이 {{email}} 로 전송되었습니다.", + "user": "사용자", + "vote": "투표", + "voteCount_other": "{{count}} 표", + "voteCount": "{{count}} 표", + "weekStartsOn": "한 주의 시작", + "weekView": "주간 보기", + "whatsThis": "이게 뭐죠?", + "yes": "가능해요", + "you": "당신", + "yourDetails": "세부 정보", + "yourName": "이름", + "yourPolls": "당신의 투표" +} diff --git a/public/locales/ko/common.json b/public/locales/ko/common.json new file mode 100644 index 000000000..87273204d --- /dev/null +++ b/public/locales/ko/common.json @@ -0,0 +1,22 @@ +{ + "blog": "블로그", + "discussions": "토론", + "donate": "후원", + "english": "영어", + "footerCredit": "만든이 @imlukevella", + "footerSponsor": "이 프로젝트는 유저 후원으로 진행됩니다. 후원하기 를 통해 프로젝트를 지원해주세요!", + "french": "프랑스어", + "german": "독일어", + "home": "홈페이지", + "italian": "이탈리아어", + "language": "언어", + "links": "링크", + "portugueseBr": "포르투갈어 (브라질)", + "poweredBy": "기술 지원", + "privacyPolicy": "개인정보 취급방침", + "spanish": "스페인어", + "starOnGithub": "Github에서 좋아요 누르기", + "support": "사용자가이드", + "swedish": "스웨덴어", + "volunteerTranslator": "이 페이지 번역 돕기" +} diff --git a/public/locales/ko/errors.json b/public/locales/ko/errors.json new file mode 100644 index 000000000..d4541193d --- /dev/null +++ b/public/locales/ko/errors.json @@ -0,0 +1,6 @@ +{ + "notFoundTitle": "404 찾을 수 없음", + "notFoundDescription": "요청한 페이지를 찾을 수 없습니다.", + "goToHome": "홈으로 돌아가기", + "startChat": "채팅 시작하기" +} diff --git a/public/locales/ko/homepage.json b/public/locales/ko/homepage.json new file mode 100644 index 000000000..29c6ffbdd --- /dev/null +++ b/public/locales/ko/homepage.json @@ -0,0 +1,36 @@ +{ + "3Ls": "맞아요 - L 이 3개에요!", + "adFree": "광고 없음", + "adFreeDescription": "Ad-blocker 는 신경쓰지 마세요 - 여기서는 필요 없을겁니다.", + "comments": "댓글들", + "commentsDescription": "참여자들은 투표에 댓글을 남길 수 있고 댓글들은 모두에게 공개될 것입니다.", + "features": "특징", + "featuresSubheading": "똑똑하게 계획하기", + "follow": "팔로우하기", + "getStarted": "시작하기", + "heroSubText": "빈 틈 없는 최적의 날짜를 찾아보세요", + "heroText": "쉽게
단체 미팅
시간 정하기", + "links": "링크", + "liveDemo": "라이브 데모", + "metaDescription": "최적의 시간이나 날짜를 찾기 위해 투표를 생성하세요. Doodle의 무료 대체제입니다.", + "metaTitle": "Rallly - 단체 미팅 시간 정하기", + "mobileFriendly": "모바일 지원", + "mobileFriendlyDescription": "모바일에서 사용가능하며 참여자들은 어디서든 응답할 수 있습니다.", + "new": "신규", + "noLoginRequired": "로그인은 필요하지 않습니다.", + "noLoginRequiredDescription": "투표를 생성하거나 투표하기 위해 로그인 할 필요가 없습니다.", + "notifications": "알림", + "notificationsDescription": "참여자들의 응답을 지켜봅니다. 당신의 투표에서 일어나는 모든일들에 대해 알림을 받으세요.", + "openSource": "오픈 소스", + "openSourceDescription": "소스코드는 완전히 오픈소스이며. Github 에서 이용 가능합니다.", + "participant": "참여자", + "participantCount_other": "{{count}} 명", + "participantCount": "{{count}} 명", + "perfect": "완벽해요!", + "principles": "원칙", + "principlesSubheading": "우리는 다릅니다", + "selfHostable": "자체 운영 가능", + "selfHostableDescription": "데이터 전체를 제어하기 위해 자신만의 서버를 구축할 수 있습니다.", + "timeSlots": "시간대 설정", + "timeSlotsDescription": "투표를 생성할 때 시작 시간과 끝 시간을 각 날짜별로 설정할 수 있습니다. 각 시간은 참여자들의 표준시간대에 맞춰 자동으로 조절되거나 표준시간대를 완전히 무시하도록 설정할 수 있습니다." +} diff --git a/public/locales/pt/app.json b/public/locales/pt/app.json new file mode 100644 index 000000000..f2f179c23 --- /dev/null +++ b/public/locales/pt/app.json @@ -0,0 +1,131 @@ +{ + "12h": "12 Horas", + "24h": "24 Horas", + "addParticipant": "Adicionar participante", + "addTimeOption": "Adicionar opção de horário", + "alreadyVoted": "Já votou nesta sondagem", + "applyToAllDates": "Aplicar a todas as datas", + "areYouSure": "Tem a certeza?", + "back": "Voltar", + "calendarHelp": "Não é possível criar uma sondagem sem opções. Adicionar no mínimo uma opção para continuar.", + "calendarHelpTitle": "Esqueceste algo?", + "cancel": "Cancelar", + "comment": "Comentar", + "commentPlaceholder": "Deixa um comentário nesta sondagem (visível para todos)", + "comments": "Comentários", + "continue": "Continuar", + "copied": "Copiado", + "copyLink": "Copiar link", + "createdBy": "por {{name}}", + "createPoll": "Criar sondagem", + "creatingDemo": "A criar sondagem de demonstração…", + "delete": "Eliminar", + "deleteComment": "Apagar comentário", + "deleteDate": "Apagar data", + "deletedPoll": "Sondagem eliminada", + "deletedPollInfo": "Esta sondagem já não existe.", + "deletePoll": "Apagar sondagem", + "deletePollDescription": "Todos os dados relacionados a esta sondagem serão apagados. Para confirmar, digite “{{confirmText}} na entrada abaixo:", + "deletingOptionsWarning": "Está a apagar opções em que os participantes já votaram. Os votos deles serão também ser apagados.", + "demoPollNotice": "As sondagens de demonstração são excluídas automaticamente após 1 dia", + "description": "Descrição", + "descriptionPlaceholder": "Olá, por favor escolha as datas mais convenientes!", + "donate": "Doar", + "edit": "Editar", + "editDetails": "Editar detalhes", + "editOptions": "Editar opções", + "email": "E-mail", + "emailPlaceholder": "antonio.silva@email.pt", + "endingGuestSessionNotice": "Uma vez que uma sessão de convidado termine, ela não poderá ser retomada. Não poderá editar nenhum voto ou comentário que tenha feito nesta sessão.", + "endSession": "Terminar sessão", + "errorCreate": "Uh oh! Houve um problema ao criar a sondagem. O erro foi registado e tentaremos corrigi-lo.", + "exportToCsv": "Exportar para CSV", + "finish": "Concluir", + "forgetMe": "Esquecer-me", + "goToAdmin": "Ir para Administrador", + "guest": "Convidado", + "guestSessionNotice": "Está a utilizar uma sessão de convidado. Isto permite reconhecê-lo se voltar mais tarde para poder editar os seus votos.", + "guestSessionReadMore": "Leia mais sobre as sessões de convidados.", + "hide": "Esconder", + "ifNeedBe": "Se necessário", + "linkHasExpired": "O seu link expirou ou já não é válido", + "loading": "A carregar…", + "loadingParticipants": "A carregar participantes…", + "location": "Local", + "locationPlaceholder": "Café do Júlio", + "lockPoll": "Bloquear sondagem", + "login": "Iniciar sessão", + "loginCheckInbox": "Por favor, consulte a sua caixa de email.", + "loginMagicLinkSent": "Um link mágico foi enviado para:", + "loginSendMagicLink": "Envie-me um link mágico", + "loginViaMagicLink": "Iniciar sessão via link mágico", + "loginViaMagicLinkDescription": "Enviaremos um e-mail com um link mágico que pode usar para iniciar sessão.", + "loginWithValidEmail": "Por favor, introduza um endereço de email válido", + "logout": "Terminar sessão", + "manage": "Gerir", + "menu": "Menu", + "mixedOptionsDescription": "Não pode ter opções de data e hora iguais na mesma sondagem. Qual gostaria de manter?", + "mixedOptionsKeepDates": "Manter opções de data", + "mixedOptionsKeepTimes": "Manter opções de hora", + "mixedOptionsTitle": "Aguarde um minuto…🤔", + "monday": "Segunda-feira", + "monthView": "Vista mensal", + "name": "Nome", + "namePlaceholder": "António Silva", + "new": "Novo", + "newPoll": "Nova sondagem", + "next": "Seguinte", + "nextMonth": "Mês seguinte", + "no": "Não", + "noDatesSelected": "Nenhuma data selecionada", + "notificationsDisabled": "As notificações foram desativadas", + "notificationsOff": "Notificações desativadas", + "notificationsOn": "Notificações ativadas", + "notificationsOnDescription": "Um e-mail será enviado para {{email}} quando houver atividade nesta sondagem.", + "notificationsVerifyEmail": "Precisa verificar o seu e-mail para ativar as notificações", + "noVotes": "Ninguém votou nesta opção", + "ok": "Ok", + "options": "Opções", + "participant": "Participante", + "participantCount_other": "{{count}} participantes", + "participantCount": "{{count}} participante", + "pollHasBeenLocked": "Esta sondagem foi bloqueada", + "pollHasBeenVerified": "A sondagem foi verificada", + "pollOwnerNotice": "Olá {{name}}, parece que é o proprietário desta sondagem.", + "pollsEmpty": "Nenhuma sondagem criada", + "possibleAnswers": "Possíveis respostas", + "preferences": "Preferências", + "previousMonth": "Mês anterior", + "profileLogin": "Perfil - Iniciar sessão", + "profileUser": "Perfil - {{username}}", + "requiredNameError": "O nome é obrigatório", + "save": "Guardar", + "saveInstruction": "Selecione a sua disponibilidade e clique em {{save}}", + "share": "Partilhar", + "shareDescription": "Dê este link aos seus participantes para permitir que eles votem na sua sondagem.", + "shareLink": "Partilhar via link", + "specifyTimes": "Especificar horas", + "specifyTimesDescription": "Incluir os horários de início e fim para cada opção", + "stepSummary": "Passo {{current}} de {{total}}", + "sunday": "Domingo", + "timeAndDate": "Data e hora", + "timeFormat": "Formato da hora:", + "timeZone": "Fuso horário:", + "title": "Título", + "titlePlaceholder": "Encontro Mensal", + "today": "Hoje", + "unlockPoll": "Desbloquear sondagem", + "unverifiedMessage": "Um e-mail foi enviado para {{email}} com um link para verificar o endereço de e-mail.", + "user": "Utilizador", + "vote": "Votação", + "voteCount_other": "{{count}} votos", + "voteCount": "{{count}} voto", + "weekStartsOn": "A semana começa em", + "weekView": "Vista semanal", + "whatsThis": "O que é isto?", + "yes": "Sim", + "you": "Tu", + "yourDetails": "Os seus detalhes", + "yourName": "O seu nome…", + "yourPolls": "As suas sondagens" +} diff --git a/public/locales/pt/common.json b/public/locales/pt/common.json new file mode 100644 index 000000000..6c9e856fa --- /dev/null +++ b/public/locales/pt/common.json @@ -0,0 +1,22 @@ +{ + "blog": "Blogue", + "discussions": "Discussões", + "donate": "Doar", + "english": "Inglês", + "footerCredit": "Criado por @imlukevella", + "footerSponsor": "Este projeto é financiado pelos utilizadores. Por favor, considere apoiá-lo doando.", + "french": "Francês", + "german": "Alemão", + "home": "Início", + "italian": "Italiano", + "language": "Idioma", + "links": "Links", + "portugueseBr": "Português (Brasil)", + "poweredBy": "Powered by", + "privacyPolicy": "Política de Privacidade", + "spanish": "Espanhol", + "starOnGithub": "Adicione uma estrela no GitHub", + "support": "Ajuda", + "swedish": "Sueco", + "volunteerTranslator": "Ajude a traduzir esta página" +} diff --git a/public/locales/pt/errors.json b/public/locales/pt/errors.json new file mode 100644 index 000000000..5ee38cfc6 --- /dev/null +++ b/public/locales/pt/errors.json @@ -0,0 +1,6 @@ +{ + "notFoundTitle": "404 Página não encontrada", + "notFoundDescription": "Não conseguimos encontrar a página que procuras.", + "goToHome": "Ir para a página inicial", + "startChat": "Iniciar chat" +} diff --git a/public/locales/pt/homepage.json b/public/locales/pt/homepage.json new file mode 100644 index 000000000..47eaae193 --- /dev/null +++ b/public/locales/pt/homepage.json @@ -0,0 +1,36 @@ +{ + "3Ls": "Sim—com 3 Ls", + "adFree": "Sem anúncios", + "adFreeDescription": "Pode descansar o seu bloqueador de anúncios — Não vai precisar dele aqui.", + "comments": "Comentários", + "commentsDescription": "Os participantes podem comentar na sua sondagem e os comentários ficarão visíveis para todos.", + "features": "Funcionalidades", + "featuresSubheading": "Agendamento, a maneira inteligente", + "follow": "Seguir", + "getStarted": "Começar", + "heroSubText": "Encontre a data certa sem andar às voltas", + "heroText": "Agende
reuniões de grupo
com facilidade", + "links": "Links", + "liveDemo": "Demonstração ao vivo", + "metaDescription": "Crie sondagens e vote para encontrar o melhor dia ou hora. Uma alternativa gratuita ao Doodle.", + "metaTitle": "Rallly - Agendar reuniões de grupo", + "mobileFriendly": "Preparado para dispositivos móveis", + "mobileFriendlyDescription": "Funciona muito bem em dispositivos móveis para que os participantes possam responder a sondagens onde estiverem.", + "new": "Novo", + "noLoginRequired": "Não é necessário iniciar sessão", + "noLoginRequiredDescription": "Não precisa de iniciar sessão para criar ou participar numa sondagem", + "notifications": "Notificações", + "notificationsDescription": "Acompanhe quem respondeu. Seja notificado quando os participantes votarem ou comentarem na sua sondagem.", + "openSource": "Código aberto", + "openSourceDescription": "Desenvolvido em código aberto e disponível no GitHub.", + "participant": "Participante", + "participantCount_other": "{{count}} participantes", + "participantCount": "{{count}} participante", + "perfect": "Perfeito!", + "principles": "Princípios", + "principlesSubheading": "Não somos como os outros", + "selfHostable": "Auto-alojável", + "selfHostableDescription": "Execute no seu próprio servidor para ter controlo total dos seus dados", + "timeSlots": "Intervalo de tempo", + "timeSlotsDescription": "Defina os horários de início e fim individuais para cada opção em sua sondagem. Os horários podem ser automaticamente ajustados ao fuso horário de cada participante ou podem ser definidos para ignorar completamente os fusos horários." +} diff --git a/src/components/poll/language-selector.tsx b/src/components/poll/language-selector.tsx index 0035a389d..0f2999516 100644 --- a/src/components/poll/language-selector.tsx +++ b/src/components/poll/language-selector.tsx @@ -23,6 +23,8 @@ export const LanguageSelect: React.VoidFunctionComponent<{ + + diff --git a/src/middleware.ts b/src/middleware.ts index 4effbacc7..a6d719a3d 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,7 +1,17 @@ import languageParser from "accept-language-parser"; import { NextRequest, NextResponse } from "next/server"; -const supportedLocales = ["en", "es", "de", "fr", "it", "sv", "pt-BR"]; +const supportedLocales = [ + "en", + "es", + "de", + "fr", + "it", + "ko", + "sv", + "pt", + "pt-BR", +]; export function middleware({ headers, cookies, nextUrl }: NextRequest) { const newUrl = nextUrl.clone(); diff --git a/src/utils/dayjs.tsx b/src/utils/dayjs.tsx index 219699c7d..bc3c56157 100644 --- a/src/utils/dayjs.tsx +++ b/src/utils/dayjs.tsx @@ -55,11 +55,21 @@ const dayjsLocales: Record< timeFormat: "24h", import: () => import("dayjs/locale/sv"), }, + pt: { + weekStartsOn: "sunday", + timeFormat: "24h", + import: () => import("dayjs/locale/pt"), + }, "pt-BR": { weekStartsOn: "sunday", timeFormat: "24h", import: () => import("dayjs/locale/pt-br"), }, + ko: { + weekStartsOn: "sunday", + timeFormat: "12h", + import: () => import("dayjs/locale/ko"), + }, }; dayjs.extend(localizedFormat);