Skip to content

Commit

Permalink
Merge pull request #7 from MOVACT/new-locales
Browse files Browse the repository at this point in the history
New locales
  • Loading branch information
mxmtsk authored Jul 31, 2021
2 parents d94801f + d57aea1 commit e4eadb8
Show file tree
Hide file tree
Showing 22 changed files with 530 additions and 697 deletions.
675 changes: 0 additions & 675 deletions LICENSE.md

This file was deleted.

9 changes: 3 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# VoteSwiper / WahlSwiper - App

[![License](https://img.shields.io/badge/License-GPL%203.0-green.svg)](./LICENSE) [![Build status](https://build.appcenter.ms/v0.1/apps/0f3f9d82-48f5-436f-929f-221e97867550/branches/master/badge)](https://appcenter.ms) [![Last Commit](https://img.shields.io/github/last-commit/movact/voteswiper-app)](https://github.com/MOVACT/voteswiper-app/commits) [![Open issues](https://img.shields.io/github/issues/movact/voteswiper-app)](https://github.com/MOVACT/voteswiper-app/issues) [![Follow WahlSwiper](https://img.shields.io/twitter/follow/wahlswiper)](https://www.twitter.com/wahlswiper)
[![Last Commit](https://img.shields.io/github/last-commit/movact/voteswiper-app)](https://github.com/MOVACT/voteswiper-app/commits) [![Open issues](https://img.shields.io/github/issues/movact/voteswiper-app)](https://github.com/MOVACT/voteswiper-app/issues) [![Follow WahlSwiper](https://img.shields.io/twitter/follow/wahlswiper)](https://www.twitter.com/wahlswiper)

VoteSwiper (in Germany better known as WahlSwiper) is a cross-platform voting advice app for Android, iOS and web browsers. The app is operated by [MOVACT](https://www.movact.de) primarily for German federale and state elections. The content for the surveys is researched and developed by various institutions, most recently mainly by political scientists at the University of Freiburg.

We started this project in 2017 for the federal election and since then grow a user base of over one million. While we operated closed source for a long time, we believe the right thing to do is to disclose the source code of the whole project for transparency and to invite others to help grow this project.

We started this project in 2017 for the federal election and since then grow a user base of over one million. While we operated closed source for a long time, we believe the right thing to do is to disclose the source code of the whole project for transparency.
**Source code for the API will be available soon.**

## Development
Expand All @@ -32,7 +31,7 @@ react-native run-android

## How to contribute

We appreciate any contribution and feedback. Feel free to open an issue if you find errors or use the discussion board if you'd like to suggest new features. You can also contribute code directly by opening a pull request. If you want to implement a new feature, please open a discussion first to see if we would merge it.
We appreciate any feedback. Feel free to open an issue if you find errors or use the discussion board if you'd like to suggest new features.

## Security Bugs

Expand All @@ -45,5 +44,3 @@ If you find any security related issues we would appreciate if you safely disclo
## License

Copyright MOVACT UG (haftungsbeschränkt)

Licensed under [GNU GENERAL PUBLIC LICENSE 3.0](./LICENSE).
3 changes: 2 additions & 1 deletion src/common/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ const config = {
api: 'https://api.voteswiper.org/api',
apiVersion: 1,
fallbackLocale: 'en',
locales: ['en', 'de', 'fr', 'fi', 'sv'],
locales: ['en', 'de', 'ru', 'tr', 'fr', 'fi', 'sv', 'ar', 'fa'],
rtlLocales: ['ar', 'fa'],
storyblokAccessToken: 'b7BTTUOEkSa786viucYnjwtt',
};

Expand Down
12 changes: 10 additions & 2 deletions src/components/ElectionPill/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import LinearGradient from 'react-native-linear-gradient';
import {Election} from 'types/api';
import moment from 'util/momentLocale';
import ArrowRightCircle from '../../icons/ArrowRightCircle';
import rtl from '../../rtl';
import Txt from '../Txt';
import styles from './styles';

Expand All @@ -25,9 +26,11 @@ const ElectionPill: React.FC<Props> = ({
playable,
playable_date,
}) => {
const {t} = useApp();
const {t, language} = useApp();
const [clickActive, setActiveClick] = React.useState(false);

moment.locale(language || 'de');

return (
<TouchableWithoutFeedback
onPress={onPress}
Expand Down Expand Up @@ -64,7 +67,12 @@ const ElectionPill: React.FC<Props> = ({
</Txt>
</View>
<View>
<ArrowRightCircle fill="#8186D7" width={20} height={20} />
<ArrowRightCircle
fill="#8186D7"
width={20}
height={20}
style={rtl.mirror}
/>
</View>
</LinearGradient>
</View>
Expand Down
3 changes: 2 additions & 1 deletion src/components/ResultBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import {GestureResponderEvent, TouchableOpacity, View} from 'react-native';
import Animated, {Easing} from 'react-native-reanimated';
import ChevronRight from '../../icons/ChevronRight';
import rtl from '../../rtl';
import Txt from '../Txt';
import styles from './styles';

Expand Down Expand Up @@ -128,7 +129,7 @@ const ResultBar: React.FC<Props> = ({
</Txt>
{shareBar !== true ? (
<View style={styles.icon}>
<ChevronRight />
<ChevronRight style={rtl.mirror} />
</View>
) : null}
</View>
Expand Down
3 changes: 2 additions & 1 deletion src/components/Txt/styles.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {Platform, StyleSheet} from 'react-native';
import {I18nManager, Platform, StyleSheet} from 'react-native';

export default StyleSheet.create({
text: {
backgroundColor: 'transparent',
fontFamily: Platform.isTV ? 'System' : 'Rubik-Regular',
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
},
textMedium: {
fontFamily: Platform.isTV ? 'System' : 'Rubik-Medium',
Expand Down
11 changes: 11 additions & 0 deletions src/rtl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {I18nManager, StyleSheet} from 'react-native';

export default StyleSheet.create({
mirror: {
transform: [
{
scaleX: I18nManager.isRTL ? -1 : 1,
},
],
},
});
2 changes: 2 additions & 0 deletions src/screens/ElectionDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ const ElectionDetails: React.FC = () => {
const {setElection} = useSwiper();
const {params} = useRoute<ElectionDetailsScreenRouteProp>();

moment.locale(language || 'de');

const {loading, error, data} = useFetch<Question[], QuestionsData>(
ENDPOINTS.QUESTIONS,
{data: {id: params.election.id}},
Expand Down
3 changes: 2 additions & 1 deletion src/screens/ElectionsIndex/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {Election, ElectionsData} from 'types/api';
import getCountryFlag from 'util/getCountryFlag';
import moment from 'util/momentLocale';
import ChevronRight from '../../icons/ChevronRight';
import rtl from '../../rtl';
import styles from './styles';

const ElectionsIndex: React.FC = () => {
Expand Down Expand Up @@ -43,7 +44,7 @@ const ElectionsIndex: React.FC = () => {
<Txt style={styles.countryLinkText} medium>
{country!.name}
</Txt>
<ChevronRight />
<ChevronRight style={rtl.mirror} />
</TouchableOpacity>
),
title: '',
Expand Down
14 changes: 12 additions & 2 deletions src/screens/Settings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Title from 'components/Title';
import Txt from 'components/Txt';
import {useApp} from 'contexts/app';
import React from 'react';
import {TouchableOpacity, View} from 'react-native';
import {I18nManager, TouchableOpacity, View} from 'react-native';
import RNRestart from 'react-native-restart';
import translations from 'translations';
import styles from './styles';
Expand Down Expand Up @@ -60,6 +60,7 @@ const Settings: React.FC = () => {
<Txt copy>{t('settings.systemDefaultText')}</Txt>
</TouchableOpacity>
{config.locales.map((lang) => {
console.log(config.rtlLocales.indexOf(lang) > -1);
return (
<TouchableOpacity
onPress={() => {
Expand All @@ -71,7 +72,14 @@ const Settings: React.FC = () => {
}}
style={[styles.language, activeStyle(lang)]}
key={lang}>
<Txt copy medium>
<Txt
copy
medium
style={
config.rtlLocales.indexOf(lang) > -1
? styles.rtl
: styles.ltr
}>
{/* @ts-ignore */}
{translations[lang].name}
</Txt>
Expand All @@ -89,6 +97,8 @@ const Settings: React.FC = () => {
onPress={() => {
setLocale(pick === 'default' ? null : pick);
setTimeout(() => {
I18nManager.forceRTL(config.rtlLocales.indexOf(pick) > -1);

RNRestart.Restart();
}, 500);
}}
Expand Down
6 changes: 6 additions & 0 deletions src/screens/Settings/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,10 @@ export default StyleSheet.create({
padding: 15,
backgroundColor: 'rgba(0,0,0,0.3)',
},
rtl: {
writingDirection: 'rtl',
},
ltr: {
writingDirection: 'ltr',
},
});
3 changes: 2 additions & 1 deletion src/screens/Swiper/components/Card/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {Image, Platform, TouchableOpacity, View} from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
import Animated, {Easing} from 'react-native-reanimated';
import {Question} from 'types/api';
import rtl from '../../../../rtl';
import styles from './styles';

const Card: React.FC<Question> = ({
Expand Down Expand Up @@ -56,7 +57,7 @@ const Card: React.FC<Question> = ({
colors={['#DB67AE', '#8186D7']}
style={styles.videoButton}>
{video_url ? (
<Play height={24} width={21} />
<Play height={24} width={21} style={rtl.mirror} />
) : (
<SvgCircleInfo style={styles.infoIcon} />
)}
Expand Down
7 changes: 6 additions & 1 deletion src/screens/Swiper/components/NavigationButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import ArrowRight from 'icons/ArrowRight';
import React from 'react';
import {GestureResponderEvent, TouchableOpacity} from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
import rtl from '../../../../rtl';
import styles from './styles';

interface Props {
Expand All @@ -22,7 +23,11 @@ const NavigationButton: React.FC<Props> = ({onPress, disabled, type}) => {
end={{x: 0, y: 0}}
colors={['#464872', '#5D5D94']}
style={[styles.bg, disabled ? styles.disabled : {}]}>
{type === 'previous' ? <ArrowLeft /> : <ArrowRight />}
{type === 'previous' ? (
<ArrowLeft style={rtl.mirror} />
) : (
<ArrowRight style={rtl.mirror} />
)}
</LinearGradient>
</TouchableOpacity>
);
Expand Down
3 changes: 2 additions & 1 deletion src/screens/Swiper/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import DeckSwiper from 'react-native-deck-swiper';
import {TouchableOpacity} from 'react-native-gesture-handler';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {CountAnswerData, Question} from 'types/api';
import rtl from '../../rtl';
import Card from './components/Card';
import ExitConfirmDialog from './components/ExitConfirmDialog';
import MainButton from './components/MainButton';
Expand Down Expand Up @@ -273,7 +274,7 @@ const Swiper: React.FC = () => {
}
}}
style={styles.skip}>
<Skip width={20} height={20} />
<Skip width={20} height={20} style={rtl.mirror} />
<Txt style={styles.skipText}>{t('swiper.skip')}</Txt>
</TouchableOpacity>
<MainButton
Expand Down
4 changes: 2 additions & 2 deletions src/screens/Swiper/styles.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {sm} from 'common/breakpoints';
import {Dimensions, StyleSheet} from 'react-native';
import {Dimensions, I18nManager, StyleSheet} from 'react-native';
import {cardBorderRadius} from './components/Card/styles';

const {width} = Dimensions.get('window');
Expand Down Expand Up @@ -53,7 +53,7 @@ export default StyleSheet.create({
},
controls: {
paddingTop: controlsPaddingTop,
flexDirection: 'row',
flexDirection: I18nManager.isRTL ? 'row-reverse' : 'row',
justifyContent: 'space-around',
alignItems: 'center',
position: 'relative',
Expand Down
113 changes: 113 additions & 0 deletions src/translations/ar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
const lang = {
name: 'عربى',
countryLanguage: 'arabic',

'navigation.backTitle': 'الرجوع',
'navigation.helpTitle': 'أسئلة متكررة',
'navigation.electionsTitle': 'الانتخابات',
'navigation.infoTitle': 'معلومات',

// Component:ElectionPill
'electionPill.availableFrom': 'سيكون متوفرًا بدءًا من {1}',

// Countdown
'countdown.days': 'الأيام',
'countdown.hours': 'الساعات',
'countdown.minutes': 'الدقائق',
'countdown.seconds': 'الثواني',

// Swiper
'swiper.doubleWeight': 'إعادة التقييم',
'swiper.doubleWeighted': 'تمت إعادة التقييم',
'swiper.questionNumber': 'سؤال رقم {1} من {2}',
'swiper.yes': 'نعم',
'swiper.no': 'لا',
'swiper.none': 'لا يوجد',
'swiper.skip': 'تخطي',
'swiper.cancelTitle': 'هل تريد حقًا الإلغاء؟',
'swiper.cancelText': 'ستضيع مدخلاتك بعد ذلك وسيتعين عليك البدء من جديد.',
'swiper.cancelActionNo': 'لا، ارجع',
'swiper.cancelActionYes': 'نعم',

'swiperSelectParties.text':
'اختر الآن الأحزاب التي تريد مقارنة إجاباتك معها. يمكنك اختيار أي عدد تريده من الأحزاب.',
'swiperSelectParties.checkAll': 'اختر جميعها',
'swiperSelectParties.uncheckAll': 'إلغاء الاختيارات جميعها',
'swiperSelectParties.chooseMinOne': 'اختر حزب واحد على الأقل للاستمرار',
'swiperSelectParties.nextButton': 'استمر',

'swiperResult.topmatch': 'أفضل اختيار متطابق معك',
'swiperResult.program': 'البرنامج الانتخابي',
'swiperResult.shareTitle': 'نتيجة WahlSwiper#',
'swiperResult.shareMessage': 'نتيجة WahlSwiper الخاصة بـ {1}',
'swiperResult.screenshotTitle': 'نتيجة WahlSwiper لـ {1}',

'swiperResult.comparisonWith': 'مقارنة إجاباتك بـ »{1}«',
'swiperResult.readReasoning': 'اقرأ منطق الحزب »',
'swiperResult.closeReasoning': 'إغلاق بيان منطق الحزب',
'swiperResult.noReason': 'لم يذكر الحزب أسباب القرار.',
'swiperResult.yourAnswer': 'إجابتك',
'swiperResult.party': 'الحزب',

'swiperResult.yourResult': 'نتيجتك',
'swiperResult.chooseParties': 'اختيار الأحزاب',
'swiperResult.share': 'المشاركة',
'swiperResult.parties': 'الأحزاب',
'swiperResult.filterParties': 'تصفية الأحزاب',

'swiperResult.editAnswers': 'تصفية الأحزاب',

// Screen:SelectCountry,
'selectCountry.title': 'شكرًا لمشاركتك',
'selectCountry.introText':
'للبدء ، اختر الدولة التي تعيش فيها أو حيث يمكنك التصويت.',

// Screen:SettingsCountry
'settingsCountry.title': 'الدول',
'settingsCountry.boxTitle': 'اختر دولة أخرى',
'settingsCountry.boxText':
'Wir haben die Fragen aus anderen Ländern neben den Landessprachen auch auf Englisch übersetzt, sodass du dich auch über die Parteien und ihre Standpunkte dort informieren kannst.',

'settings.title': 'الإعدادات',
'settingsLanguage.boxTitle': 'لغة التطبيق',
'settingsLanguage.boxText': 'قم بتغيير اللغة التي يظهر التطبيق بها.',
'settings.systemDefault': 'النظام الأساسي',
'settings.systemDefaultText':
'يتم استخدام لغة هاتفك في حالة وجود ترجمة. فإذا لم توجد، يتم استخدام اللغة الإنجليزية.',

// Screen:ElectionsIndex
'electionsIndex.boxTitle': 'الانتخابات الحالية',
'electionsIndex.boxText':
'اختر الانتخابات لبدء المقارنة عن طريق التمرير السريع.',
'electionsIndex.boxPastTitle': 'انتخابات سابقة',
'electionsIndex.boxPastText':
'نتائج (WahlSwipers) هذه خاصة بالانتخابات الماضية. يرجى ملاحظة أن مواقف الأحزاب ربما تكون قد تغيرت فيما يخص بعض النقاط الفردية في هذه الاستطلاعات.',
'electionsIndex.noElections':
'لا يوجد WahlSwiper للانتخابات الحالية لهذا البلد.',

// Screen:ElectionDetails
'electionDetails.countdownPast': 'تم إجراء الانتخابات في',
'electionDetails.countdown': 'العد التنازلي قبل الانتخابات',
'electionDetails.infoText':
'سنطرح عليك بعض الأسئلة حول القضايا السياسية. أجب على الأسئلة عن طريق التمرير السريع وسنطابق إجاباتك مع الحزب الأقرب لآرائك.',
'electionDetails.startButtonText': 'ابدأ الآن',

// Screen:HelpIndex
'helpIndex.title': 'أسئلة متكررة',

// Screen:InfosIndex
'infosIndex.title': 'معلومات',
'infosIndex.headline': 'عن WahlSwiper',
'infosIndex.paragraph1':
'يجب أن يكون تكوين رأي حول الانتخابات أمرًا سهلاً وممتعًا - هذه هي مهمتنا في WahlSwiper. الفكرة: يمكن الإجابة على الأسئلة السياسية بتمريرة بسيطة إلى اليسار ب"لا" أو إلى اليمين ب"نعم". ثم يقوم WahlSwiper بمقارنة إجاباتك بإجابات الأحزاب المختلفة.',
'infosIndex.paragraph2':
'لدينا ميزة واضحة. ليس عندنا إجابات سوى "نعم" و "لا" فقط؛ لا يوجد "ربما". هذا يجعل الأحزاب تتصبب عرقًا أحيانًا، لكنه يساعدك على اتخاذ القرار. بالطبع ، يمكنك أيضًا تخطي الأسئلة الفردية دون الإجابة عليها.',
'infosIndex.paragraph3':
'التصويت أمر سهل مثل المواعدة عبر الإنترنت – ولكن لديك فرصة للتوافق مع الطرف الذي تواعده لفصل تشريعي واحد على الأقل.',
'infosIndex.imprintButton': 'الختم',
'infosIndex.imprintLink': 'https://www.voteswiper.org/ar/page/imprint',
'infosIndex.privacyButton': 'خصوصية البيانات',
'infosIndex.privacyLink': 'https://www.voteswiper.org/ar/page/privacy',
};

export default lang;
4 changes: 2 additions & 2 deletions src/translations/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ const lang = {
'infosIndex.paragraph3':
'Wählen gehen ist so einfach wie Online-Dating – der „Match“ hält dann jedoch mindestens eine Legislaturperiode lang.',
'infosIndex.imprintButton': 'Impressum',
'infosIndex.imprintLink': 'https://www.voteswiper.org/page/impressum',
'infosIndex.imprintLink': 'https://www.voteswiper.org/de/page/impressum',
'infosIndex.privacyButton': 'Datenschutz',
'infosIndex.privacyLink': 'https://www.voteswiper.org/page/datenschutz',
'infosIndex.privacyLink': 'https://www.voteswiper.org/de/page/datenschutz',
};

export default lang;
Loading

0 comments on commit e4eadb8

Please sign in to comment.