Added pt-BR

https://github.com/tooot-app/app/issues/302
This commit is contained in:
Zhiyuan Zheng 2022-05-28 22:18:03 +02:00
parent bdf7da120f
commit af6731f702
13 changed files with 181 additions and 135 deletions

View File

@ -6,10 +6,12 @@
## Special thanks ## Special thanks
@forenta for German translation [@forenta](https://github.com/forenta) for German translation
@andrigamerita for Italian translation [@andrigamerita](https://github.com/andrigamerita) for Italian translation
@hellojaccc for Korean translation [@hellojaccc](https://github.com/hellojaccc) for Korean translation
@duy@mas.to for Vietnamese translation [@luizpicolo](https://github.com/luizpicolo) for Brazilian Portuguese
[@duy@mas.to](https://mas.to/@duy) for Vietnamese translation

View File

@ -4,7 +4,7 @@
"native": "220508", "native": "220508",
"major": 4, "major": 4,
"minor": 0, "minor": 0,
"patch": 3, "patch": 4,
"expo": "45.0.0" "expo": "45.0.0"
}, },
"description": "tooot app for Mastodon", "description": "tooot app for Mastodon",

View File

@ -1,31 +1,4 @@
import { ActionSheetProvider } from '@expo/react-native-action-sheet' import { ActionSheetProvider } from '@expo/react-native-action-sheet'
import '@formatjs/intl-getcanonicallocales/polyfill'
import '@formatjs/intl-locale/polyfill'
import '@formatjs/intl-pluralrules/polyfill'
import '@formatjs/intl-pluralrules/locale-data/de'
import '@formatjs/intl-pluralrules/locale-data/en'
import '@formatjs/intl-pluralrules/locale-data/ko'
import '@formatjs/intl-pluralrules/locale-data/vi'
import '@formatjs/intl-pluralrules/locale-data/zh'
import '@formatjs/intl-numberformat/polyfill'
import '@formatjs/intl-numberformat/locale-data/de'
import '@formatjs/intl-numberformat/locale-data/en'
import '@formatjs/intl-numberformat/locale-data/ko'
import '@formatjs/intl-numberformat/locale-data/vi'
import '@formatjs/intl-numberformat/locale-data/zh'
import '@formatjs/intl-datetimeformat/polyfill'
import '@formatjs/intl-datetimeformat/locale-data/de'
import '@formatjs/intl-datetimeformat/locale-data/en'
import '@formatjs/intl-datetimeformat/locale-data/ko'
import '@formatjs/intl-datetimeformat/locale-data/vi'
import '@formatjs/intl-datetimeformat/locale-data/zh'
import '@formatjs/intl-datetimeformat/add-all-tz'
import '@formatjs/intl-relativetimeformat/polyfill'
import '@formatjs/intl-relativetimeformat/locale-data/de'
import '@formatjs/intl-relativetimeformat/locale-data/en'
import '@formatjs/intl-relativetimeformat/locale-data/ko'
import '@formatjs/intl-relativetimeformat/locale-data/vi'
import '@formatjs/intl-relativetimeformat/locale-data/zh'
import queryClient from '@helpers/queryClient' import queryClient from '@helpers/queryClient'
import i18n from '@root/i18n/i18n' import i18n from '@root/i18n/i18n'
import Screens from '@root/Screens' import Screens from '@root/Screens'

View File

@ -22,7 +22,7 @@ export interface Props {
switchDisabled?: boolean switchDisabled?: boolean
switchOnValueChange?: () => void switchOnValueChange?: () => void
iconBack?: 'ChevronRight' | 'ExternalLink' iconBack?: 'ChevronRight' | 'ExternalLink' | 'Check'
iconBackColor?: ColorDefinitions iconBackColor?: ColorDefinitions
loading?: boolean loading?: boolean

View File

@ -43,6 +43,9 @@
"fontSize": { "fontSize": {
"name": "Toot Font Size" "name": "Toot Font Size"
}, },
"language": {
"name": "Language"
},
"lists": { "lists": {
"name": "Lists" "name": "Lists"
}, },
@ -221,7 +224,7 @@
} }
}, },
"language": { "language": {
"heading": "Language", "heading": "$t(me.stacks.language.name)",
"options": { "options": {
"cancel": "$t(common:buttons.cancel)" "cancel": "$t(common:buttons.cancel)"
} }

View File

@ -5,9 +5,42 @@ import de from '@root/i18n/de/_all'
import en from '@root/i18n/en/_all' import en from '@root/i18n/en/_all'
import it from '@root/i18n/it/_all' import it from '@root/i18n/it/_all'
import ko from '@root/i18n/ko/_all' import ko from '@root/i18n/ko/_all'
import pt_BR from '@root/i18n/pt_BR/_all'
import vi from '@root/i18n/vi/_all' import vi from '@root/i18n/vi/_all'
import zh_Hans from '@root/i18n/zh-Hans/_all' import zh_Hans from '@root/i18n/zh-Hans/_all'
import '@formatjs/intl-getcanonicallocales/polyfill'
import '@formatjs/intl-locale/polyfill'
import '@formatjs/intl-pluralrules/polyfill'
import '@formatjs/intl-pluralrules/locale-data/de'
import '@formatjs/intl-pluralrules/locale-data/en'
import '@formatjs/intl-pluralrules/locale-data/ko'
import '@formatjs/intl-pluralrules/locale-data/pt'
import '@formatjs/intl-pluralrules/locale-data/vi'
import '@formatjs/intl-pluralrules/locale-data/zh'
import '@formatjs/intl-numberformat/polyfill'
import '@formatjs/intl-numberformat/locale-data/de'
import '@formatjs/intl-numberformat/locale-data/en'
import '@formatjs/intl-numberformat/locale-data/ko'
import '@formatjs/intl-numberformat/locale-data/pt'
import '@formatjs/intl-numberformat/locale-data/vi'
import '@formatjs/intl-numberformat/locale-data/zh-Hans'
import '@formatjs/intl-datetimeformat/polyfill'
import '@formatjs/intl-datetimeformat/locale-data/de'
import '@formatjs/intl-datetimeformat/locale-data/en'
import '@formatjs/intl-datetimeformat/locale-data/ko'
import '@formatjs/intl-datetimeformat/locale-data/pt'
import '@formatjs/intl-datetimeformat/locale-data/vi'
import '@formatjs/intl-datetimeformat/locale-data/zh-Hans'
import '@formatjs/intl-datetimeformat/add-all-tz'
import '@formatjs/intl-relativetimeformat/polyfill'
import '@formatjs/intl-relativetimeformat/locale-data/de'
import '@formatjs/intl-relativetimeformat/locale-data/en'
import '@formatjs/intl-relativetimeformat/locale-data/ko'
import '@formatjs/intl-relativetimeformat/locale-data/pt'
import '@formatjs/intl-relativetimeformat/locale-data/vi'
import '@formatjs/intl-relativetimeformat/locale-data/zh-Hans'
i18n.use(initReactI18next).init({ i18n.use(initReactI18next).init({
lng: 'en', lng: 'en',
fallbackLng: 'en', fallbackLng: 'en',
@ -15,7 +48,7 @@ i18n.use(initReactI18next).init({
ns: ['common'], ns: ['common'],
defaultNS: 'common', defaultNS: 'common',
resources: { 'zh-Hans': zh_Hans, vi, ko, it, en, de }, resources: { 'zh-Hans': zh_Hans, vi, 'pt-BR': pt_BR, ko, it, en, de },
returnEmptyString: false, returnEmptyString: false,
saveMissing: true, saveMissing: true,

View File

@ -3,6 +3,7 @@ const LOCALES = {
en: 'English', en: 'English',
it: 'Italiano', it: 'Italiano',
ko: '한국어', ko: '한국어',
'pt-BR': 'Português (Brasil)',
vi: 'Tiếng Việt', vi: 'Tiếng Việt',
'zh-Hans': '简体中文' 'zh-Hans': '简体中文'
} }

17
src/i18n/pt_BR/_all.ts Normal file
View File

@ -0,0 +1,17 @@
export default {
common: require('./common'),
screens: require('./screens'),
screenActions: require('./screens/actions'),
screenAnnouncements: require('./screens/announcements'),
screenCompose: require('./screens/compose'),
screenImageViewer: require('./screens/imageViewer'),
screenTabs: require('./screens/tabs'),
componentEmojis: require('./components/emojis'),
componentInstance: require('./components/instance'),
componentMediaSelector: require('./components/mediaSelector'),
componentParse: require('./components/parse'),
componentRelationship: require('./components/relationship'),
componentTimeline: require('./components/timeline')
}

View File

@ -14,6 +14,7 @@ import TabMePush from './Me/Push'
import TabMeRoot from './Me/Root' import TabMeRoot from './Me/Root'
import TabMeSettings from './Me/Settings' import TabMeSettings from './Me/Settings'
import TabMeSettingsFontsize from './Me/SettingsFontsize' import TabMeSettingsFontsize from './Me/SettingsFontsize'
import TabMeSettingsLanguage from './Me/SettingsLanguage'
import TabMeSwitch from './Me/Switch' import TabMeSwitch from './Me/Switch'
import TabSharedRoot from './Shared/Root' import TabSharedRoot from './Shared/Root'
@ -152,6 +153,19 @@ const TabMe = React.memo(
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} /> headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
})} })}
/> />
<Stack.Screen
name='Tab-Me-Settings-Language'
component={TabMeSettingsLanguage}
options={({ navigation }: any) => ({
title: t('me.stacks.language.name'),
...(Platform.OS === 'android' && {
headerCenter: () => (
<HeaderCenter content={t('me.stacks.language.name')} />
)
}),
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
})}
/>
<Stack.Screen <Stack.Screen
name='Tab-Me-Switch' name='Tab-Me-Switch'
component={TabMeSwitch} component={TabMeSwitch}

View File

@ -5,11 +5,8 @@ import { useActionSheet } from '@expo/react-native-action-sheet'
import { useNavigation } from '@react-navigation/native' import { useNavigation } from '@react-navigation/native'
import { LOCALES } from '@root/i18n/locales' import { LOCALES } from '@root/i18n/locales'
import { useAppDispatch } from '@root/store' import { useAppDispatch } from '@root/store'
import androidDefaults from '@utils/slices/instances/push/androidDefaults'
import { getInstances } from '@utils/slices/instancesSlice'
import { import {
changeBrowser, changeBrowser,
changeLanguage,
changeTheme, changeTheme,
getSettingsTheme, getSettingsTheme,
getSettingsBrowser, getSettingsBrowser,
@ -19,11 +16,8 @@ import {
getSettingsStaticEmoji, getSettingsStaticEmoji,
changeStaticEmoji changeStaticEmoji
} from '@utils/slices/settingsSlice' } from '@utils/slices/settingsSlice'
import { useTheme } from '@utils/styles/ThemeManager'
import * as Notifications from 'expo-notifications'
import React from 'react' import React from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Platform } from 'react-native'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { mapFontsizeToName } from '../SettingsFontsize' import { mapFontsizeToName } from '../SettingsFontsize'
@ -31,10 +25,8 @@ const SettingsApp: React.FC = () => {
const navigation = useNavigation<any>() const navigation = useNavigation<any>()
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const { showActionSheetWithOptions } = useActionSheet() const { showActionSheetWithOptions } = useActionSheet()
const { mode } = useTheme()
const { t, i18n } = useTranslation('screenTabs') const { t, i18n } = useTranslation('screenTabs')
const instances = useSelector(getInstances, () => true)
const settingsFontsize = useSelector(getSettingsFontsize) const settingsFontsize = useSelector(getSettingsFontsize)
const settingsTheme = useSelector(getSettingsTheme) const settingsTheme = useSelector(getSettingsTheme)
const settingsDarkTheme = useSelector(getSettingsDarkTheme) const settingsDarkTheme = useSelector(getSettingsDarkTheme)
@ -49,102 +41,14 @@ const SettingsApp: React.FC = () => {
`me.settings.fontsize.content.${mapFontsizeToName(settingsFontsize)}` `me.settings.fontsize.content.${mapFontsizeToName(settingsFontsize)}`
)} )}
iconBack='ChevronRight' iconBack='ChevronRight'
onPress={() => { onPress={() => navigation.navigate('Tab-Me-Settings-Fontsize')}
navigation.navigate('Tab-Me-Settings-Fontsize')
}}
/> />
<MenuRow <MenuRow
title={t('me.settings.language.heading')} title={t('me.settings.language.heading')}
// @ts-ignore // @ts-ignore
content={LOCALES[i18n.language]} content={LOCALES[i18n.language]}
iconBack='ChevronRight' iconBack='ChevronRight'
onPress={() => { onPress={() => navigation.navigate('Tab-Me-Settings-Language')}
const options = Object.keys(LOCALES)
// @ts-ignore
.map(locale => LOCALES[locale])
.concat(t('me.settings.language.options.cancel'))
showActionSheetWithOptions(
{
title: t('me.settings.language.heading'),
options,
cancelButtonIndex: options.length - 1,
userInterfaceStyle: mode
},
buttonIndex => {
if (buttonIndex === undefined) return
if (buttonIndex < options.length - 1) {
analytics('settings_language_press', {
current: i18n.language,
new: options[buttonIndex]
})
haptics('Success')
// @ts-ignore
dispatch(changeLanguage(Object.keys(LOCALES)[buttonIndex]))
i18n.changeLanguage(Object.keys(LOCALES)[buttonIndex])
// Update Android notification channel language
if (Platform.OS === 'android') {
instances.forEach(instance => {
const accountFull = `@${instance.account.acct}@${instance.uri}`
if (instance.push.decode.value === false) {
Notifications.setNotificationChannelAsync(
`${accountFull}_default`,
{
groupId: accountFull,
name: t('me.push.default.heading'),
...androidDefaults
}
)
} else {
Notifications.setNotificationChannelAsync(
`${accountFull}_follow`,
{
groupId: accountFull,
name: t('me.push.follow.heading'),
...androidDefaults
}
)
Notifications.setNotificationChannelAsync(
`${accountFull}_favourite`,
{
groupId: accountFull,
name: t('me.push.favourite.heading'),
...androidDefaults
}
)
Notifications.setNotificationChannelAsync(
`${accountFull}_reblog`,
{
groupId: accountFull,
name: t('me.push.reblog.heading'),
...androidDefaults
}
)
Notifications.setNotificationChannelAsync(
`${accountFull}_mention`,
{
groupId: accountFull,
name: t('me.push.mention.heading'),
...androidDefaults
}
)
Notifications.setNotificationChannelAsync(
`${accountFull}_poll`,
{
groupId: accountFull,
name: t('me.push.poll.heading'),
...androidDefaults
}
)
}
})
}
}
}
)
}}
/> />
<MenuRow <MenuRow
title={t('me.settings.theme.heading')} title={t('me.settings.theme.heading')}

View File

@ -45,7 +45,7 @@ const TabMeSettingsFontsize: React.FC<
const item = { const item = {
id: 'demo', id: 'demo',
uri: 'https://tooot.app', uri: 'https://tooot.app',
created_at: new Date(), created_at: new Date(2021, 4, 16),
sensitive: false, sensitive: false,
visibility: 'public', visibility: 'public',
replies_count: 0, replies_count: 0,
@ -67,6 +67,7 @@ const TabMeSettingsFontsize: React.FC<
username: 'tooot📱', username: 'tooot📱',
acct: 'tooot@xmflsct.com', acct: 'tooot@xmflsct.com',
display_name: 'tooot📱', display_name: 'tooot📱',
avatar: 'https://avatars.githubusercontent.com/u/77554750?s=100',
avatar_static: 'https://avatars.githubusercontent.com/u/77554750?s=100' avatar_static: 'https://avatars.githubusercontent.com/u/77554750?s=100'
}, },
media_attachments: [], media_attachments: [],
@ -100,7 +101,7 @@ const TabMeSettingsFontsize: React.FC<
}, [theme, initialSize]) }, [theme, initialSize])
return ( return (
<ScrollView scrollEnabled={false}> <ScrollView>
<CustomText <CustomText
fontStyle='M' fontStyle='M'
style={{ style={{

View File

@ -0,0 +1,97 @@
import analytics from '@components/analytics'
import haptics from '@components/haptics'
import { MenuContainer, MenuRow } from '@components/Menu'
import { LOCALES } from '@root/i18n/locales'
import { TabMeStackScreenProps } from '@utils/navigation/navigators'
import androidDefaults from '@utils/slices/instances/push/androidDefaults'
import { getInstances } from '@utils/slices/instancesSlice'
import { changeLanguage } from '@utils/slices/settingsSlice'
import * as Notifications from 'expo-notifications'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { FlatList, Platform } from 'react-native'
import { useDispatch, useSelector } from 'react-redux'
const TabMeSettingsLanguage: React.FC<
TabMeStackScreenProps<'Tab-Me-Settings-Language'>
> = () => {
const { i18n, t } = useTranslation('screenTabs')
const languages = Object.entries(LOCALES)
const instances = useSelector(getInstances)
const dispatch = useDispatch()
const change = (lang: string) => {
analytics('settings_language_press', {
current: i18n.language,
new: lang
})
haptics('Success')
dispatch(changeLanguage(lang))
i18n.changeLanguage(lang)
// Update Android notification channel language
if (Platform.OS === 'android') {
instances.forEach(instance => {
const accountFull = `@${instance.account.acct}@${instance.uri}`
if (instance.push.decode.value === false) {
Notifications.setNotificationChannelAsync(`${accountFull}_default`, {
groupId: accountFull,
name: t('me.push.default.heading'),
...androidDefaults
})
} else {
Notifications.setNotificationChannelAsync(`${accountFull}_follow`, {
groupId: accountFull,
name: t('me.push.follow.heading'),
...androidDefaults
})
Notifications.setNotificationChannelAsync(
`${accountFull}_favourite`,
{
groupId: accountFull,
name: t('me.push.favourite.heading'),
...androidDefaults
}
)
Notifications.setNotificationChannelAsync(`${accountFull}_reblog`, {
groupId: accountFull,
name: t('me.push.reblog.heading'),
...androidDefaults
})
Notifications.setNotificationChannelAsync(`${accountFull}_mention`, {
groupId: accountFull,
name: t('me.push.mention.heading'),
...androidDefaults
})
Notifications.setNotificationChannelAsync(`${accountFull}_poll`, {
groupId: accountFull,
name: t('me.push.poll.heading'),
...androidDefaults
})
}
})
}
}
return (
<MenuContainer>
<FlatList
data={languages}
renderItem={({ item }) => {
return (
<MenuRow
key={item[0]}
title={item[1]}
iconBack={item[0] === i18n.language ? 'Check' : undefined}
iconBackColor={'blue'}
onPress={() => item[0] !== i18n.language && change(item[0])}
/>
)
}}
/>
</MenuContainer>
)
}
export default TabMeSettingsLanguage

View File

@ -146,6 +146,7 @@ export type TabMeStackParamList = {
'Tab-Me-Push': undefined 'Tab-Me-Push': undefined
'Tab-Me-Settings': undefined 'Tab-Me-Settings': undefined
'Tab-Me-Settings-Fontsize': undefined 'Tab-Me-Settings-Fontsize': undefined
'Tab-Me-Settings-Language': undefined
'Tab-Me-Switch': undefined 'Tab-Me-Switch': undefined
} & TabSharedStackParamList } & TabSharedStackParamList
export type TabMeStackScreenProps<T extends keyof TabMeStackParamList> = export type TabMeStackScreenProps<T extends keyof TabMeStackParamList> =