Skip to content

Commit 34bb508

Browse files
committed
2 parents 4112ace + cdefe7c commit 34bb508

19 files changed

+818
-220
lines changed

apps/box/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@
8181
"react-native-permissions": "*",
8282
"base-64": "*",
8383
"react-native-syntax-highlighter": "*",
84-
"react-syntax-highlighter": "*"
84+
"react-syntax-highlighter": "*",
85+
"i18next": "*",
86+
"react-i18next": "*",
87+
"i18next-resources-to-backend": "*"
8588
},
8689
"devDependencies": {
8790
"@types/react-native-background-timer": "^2.0.2"

apps/box/src/app/App.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
SDKConfigProvider,
66
useSDKConfig,
77
} from '@metamask/sdk-react';
8+
import '../i18n';
89

910
import { ThemeProvider } from '@shopify/restyle';
1011
import {
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// components/LanguageSelector.tsx
2+
import React, { useState } from 'react';
3+
import { StyleSheet, TouchableOpacity, View, ViewStyle } from 'react-native';
4+
import { FxBox, FxText, useFxTheme } from '@functionland/component-library';
5+
import { useTranslation } from 'react-i18next';
6+
7+
interface Language {
8+
code: string;
9+
label: string;
10+
}
11+
12+
interface LanguageSelectorProps {
13+
style?: ViewStyle | Array<ViewStyle | { [key: string]: string | number }>;
14+
}
15+
16+
const LANGUAGES: Language[] = [
17+
{ code: 'en', label: 'EN' },
18+
{ code: 'zh', label: '中' }
19+
];
20+
21+
const LanguageSelector: React.FC<LanguageSelectorProps> = ({ style }) => {
22+
const { i18n } = useTranslation();
23+
const { colors } = useFxTheme();
24+
const [showDropdown, setShowDropdown] = useState(false);
25+
26+
const toggleDropdown = () => {
27+
setShowDropdown(!showDropdown);
28+
};
29+
30+
const selectLanguage = (langCode: string) => {
31+
i18n.changeLanguage(langCode);
32+
setShowDropdown(false);
33+
};
34+
35+
// Find current language display label
36+
const currentLang = LANGUAGES.find(lang => lang.code === i18n.language) || LANGUAGES[0];
37+
38+
return (
39+
<View style={[styles.container, style]}>
40+
<TouchableOpacity
41+
onPress={toggleDropdown}
42+
style={[styles.selector]}
43+
>
44+
<FxText variant="bodySmallSemibold">{currentLang.label}</FxText>
45+
</TouchableOpacity>
46+
47+
{showDropdown && (
48+
<FxBox style={[styles.dropdown, { backgroundColor: colors.backgroundSecondary }]}>
49+
{LANGUAGES.map(lang => (
50+
<TouchableOpacity
51+
key={lang.code}
52+
style={[styles.languageOption]}
53+
onPress={() => selectLanguage(lang.code)}
54+
>
55+
<FxText
56+
variant="bodySmallRegular"
57+
color={lang.code === i18n.language ? 'primary' : 'content1'}
58+
>
59+
{lang.label}
60+
</FxText>
61+
</TouchableOpacity>
62+
))}
63+
</FxBox>
64+
)}
65+
</View>
66+
);
67+
};
68+
69+
const styles = StyleSheet.create({
70+
container: {
71+
position: 'relative',
72+
zIndex: 10,
73+
},
74+
selector: {
75+
paddingVertical: 8,
76+
paddingHorizontal: 12,
77+
borderRadius: 20,
78+
alignItems: 'center',
79+
justifyContent: 'center',
80+
minWidth: 38,
81+
},
82+
selectorButton: {
83+
padding: 8,
84+
borderRadius: 8,
85+
width: '100%',
86+
alignItems: 'center',
87+
},
88+
dropdown: {
89+
position: 'absolute',
90+
top: 40,
91+
right: 0,
92+
borderRadius: 8,
93+
padding: 8,
94+
marginTop:8,
95+
minWidth: 60,
96+
width: '100%',
97+
},
98+
languageOption: {
99+
paddingVertical: 8,
100+
paddingHorizontal: 12,
101+
alignItems: 'center',
102+
}
103+
});
104+
105+
export default LanguageSelector;

apps/box/src/i18n/index.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import i18n from 'i18next';
2+
import { initReactI18next } from 'react-i18next';
3+
import { getLocales } from 'react-native-localize';
4+
import AsyncStorage from '@react-native-async-storage/async-storage';
5+
6+
// Import translations directly
7+
import enTranslation from './locales/en/translation.json';
8+
import zhTranslation from './locales/zh/translation.json';
9+
10+
// Default language
11+
const FALLBACK_LANGUAGE = 'en';
12+
13+
// Resources object with all translations
14+
const resources = {
15+
en: {
16+
translation: enTranslation
17+
},
18+
zh: {
19+
translation: zhTranslation
20+
}
21+
};
22+
23+
// Detect the device language
24+
const getDeviceLanguage = () => {
25+
try {
26+
const locales = getLocales();
27+
return locales[0]?.languageCode || FALLBACK_LANGUAGE;
28+
} catch (error) {
29+
console.error('Error getting device language:', error);
30+
return FALLBACK_LANGUAGE;
31+
}
32+
};
33+
34+
// Initialize i18next with a basic configuration first
35+
i18n
36+
.use(initReactI18next)
37+
.init({
38+
resources,
39+
lng: FALLBACK_LANGUAGE, // Start with fallback, will be updated after storage check
40+
fallbackLng: FALLBACK_LANGUAGE,
41+
supportedLngs: ['en', 'zh'],
42+
interpolation: {
43+
escapeValue: false,
44+
},
45+
react: {
46+
useSuspense: false,
47+
},
48+
});
49+
50+
// Create a language change function that also updates storage
51+
export const changeLanguage = async (language: string) => {
52+
try {
53+
await AsyncStorage.setItem('userLanguage', language);
54+
await i18n.changeLanguage(language);
55+
console.log(`Language changed and saved to storage: ${language}`);
56+
return true;
57+
} catch (error) {
58+
console.error('Failed to set language:', error);
59+
return false;
60+
}
61+
};
62+
63+
// Immediately invoked async function to load the stored language
64+
(async () => {
65+
try {
66+
// First check storage
67+
const storedLanguage = await AsyncStorage.getItem('userLanguage');
68+
69+
if (storedLanguage && ['en', 'zh'].includes(storedLanguage)) {
70+
console.log(`Loaded language from storage: ${storedLanguage}`);
71+
await i18n.changeLanguage(storedLanguage);
72+
} else {
73+
// If no stored language, use device language and save it
74+
const deviceLang = getDeviceLanguage();
75+
console.log(`No stored language, using device language: ${deviceLang}`);
76+
await AsyncStorage.setItem('userLanguage', deviceLang);
77+
await i18n.changeLanguage(deviceLang);
78+
}
79+
} catch (error) {
80+
console.error('Error loading language:', error);
81+
// Fall back to device language on error
82+
i18n.changeLanguage(getDeviceLanguage());
83+
}
84+
})();
85+
86+
export default i18n;
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
{
2+
"welcome": {
3+
"title": "Hello Functionlander!",
4+
"appTitle": "Blox App",
5+
"disclaimer": "By using this product you agree to the terms and conditions at fx.land/terms, and would not hold Functionland liable for data loss.",
6+
"termsButton": "Terms & Conditions",
7+
"setupButton": "Agree & Setup my Blox"
8+
},
9+
"connectToWallet": {
10+
"title": "Connect To Wallet",
11+
"description": "App needs notification permission to connect your wallet and perform data sync. Tap allow in the next prompt.",
12+
"allowButton": "Allow Notifications"
13+
},
14+
"linkPassword": {
15+
"title": "Set Identity",
16+
"yourIdentity": "Your Identity",
17+
"password": "Password",
18+
"signature": "Signature",
19+
"warning": "Make sure to safeguard this password and the chain you used, it's the key to decrypt your data from new devices",
20+
"passwordRisk": "I understand the risk of losing my password",
21+
"metamaskOpen": "I already opened Metamask app before clicking Sign",
22+
"connectingWallet": "Connecting Wallet...",
23+
"walletConnectionInProgress": "Wallet connection in progress, click to move back to the app",
24+
"error": "Error",
25+
"unableToSignWallet": "Unable to sign the wallet address!",
26+
"signWithMetamask": "Sign with MetaMask",
27+
"cancel": "Cancel",
28+
"signManually": "Sign Manually",
29+
"submit": "Submit",
30+
"connectToBlox": "Connect to Blox",
31+
"reconnectExisting": "Reconnect to existing blox",
32+
"bluetoothCommands": "Bluetooth commands",
33+
"skipManualSetup": "Skip to manual setup"
34+
},
35+
"connectToBlox": {
36+
"title": "Connect to Blox",
37+
"checking": "Checking connection...",
38+
"connected": "Connected",
39+
"failed": "Unable to connect to Hotspot",
40+
"notConnected": "Not Connected",
41+
"bleConnecting": "Searching for Blox device...",
42+
"bleConnected": "Connected to Blox via Bluetooth",
43+
"bleFailed": "Unable to connect via Bluetooth, trying WiFi...",
44+
"hotspotInstructions": "- Please turn your Blox on and connect your phone to the Blox's hotspot manually, and turn off mobile data.",
45+
"formatInstructions": "- Make sure you have internal or external storage attached and format is either 'ext4' or 'vFat'.",
46+
"waitForBlueLight": "After first boot please wait for 10 minutes until Blox flashes 'light-blue'",
47+
"connectedMessage": "Now you are connected to Blox. Please wait...",
48+
"back": "Back",
49+
"continue": "Continue"
50+
},
51+
"connectToWifi": {
52+
"title": "Connect to Wi-Fi",
53+
"manualEntry": "Manually Enter Wifi Name",
54+
"showNetworks": "Show Network Names",
55+
"enterWifiName": "Enter Wifi Name",
56+
"enterPasswordFor": "Enter Password for",
57+
"searching": "Searching Wi-Fi Network",
58+
"selectNetwork": "Select Wi-Fi Network",
59+
"back": "Back",
60+
"next": "Next"
61+
},
62+
"setBloxAuthorizer": {
63+
"title": "Set Blox Owner",
64+
"description": "Adding the Blox App Peer ID as an owner on the Blox",
65+
"networkError": "In some cases you need to turn the mobile data off, please make sure the phone is connected to the Blox's Hotspot and mobile data/VPN is off, Then press back and try again please",
66+
"updateNeeded": "An update is awaiting a manual restart to be applied. You should unplug and plug back your blox to restart it and then try again.",
67+
"backendUpdate": "You should update your blox backend, Please press 'Skip' button and connect it to your Wifi network.",
68+
"storageNeeded": "To proceed successfully you need to attach an external storage to the Blox!",
69+
"appPeerId": "The Blox App Peer ID",
70+
"generating": "Generating the app peerId...",
71+
"bloxPeerId": "Your Blox Peer ID",
72+
"enterBloxPeerId": "Enter Your Blox Peer ID",
73+
"setBloxName": "Set Blox name",
74+
"hardDisk": "Hard Disk",
75+
"bloxSetUp": "Blox Set Up",
76+
"formatDisk": "Format Disk",
77+
"skip": "Skip",
78+
"skipAuthorization": "Skip Authorization",
79+
"skipDescription": "The Skip is only intended when you are instructed to do so by the support team. Please enter the code given to you:",
80+
"cancel": "Cancel",
81+
"invalidCode": "Invalid Code",
82+
"invalidCodeMessage": "The code you entered is incorrect. Please contact support if you need assistance.",
83+
"confirm": "Confirm",
84+
"back": "Back",
85+
"setAuthorizer": "Set Authorizer",
86+
"next": "Next",
87+
"bloxUnitPrefix": "Blox Unit",
88+
"noBleDevicesConnected": "No BLE devices connected",
89+
"unableToGetProperties": "Unable to get the blox properties!",
90+
"bloxPeerIdInvalid": "Blox peerId is invalid!"
91+
},
92+
"setupComplete": {
93+
"completing": "Completing setup",
94+
"connectPhone": "Connect your phone to internet (wifi) to proceed now...",
95+
"reachingBlox": "Reaching Blox #{{number}}...",
96+
"greenLed": "Is you blox LED 'green' but you see this message?",
97+
"internetReminder": "Make sure your phone is connected to the internet and then try again",
98+
"lightBlueLed": "Is you blox flashing 'light-blue'? You probably entered wrong wifi password",
99+
"notReachable": "Your Blox is not reachable, seems it is not connected to the internet! Please turn your blox off and then turn it on and make sure it is on Hotspot mode, then try to reconnect the blox to the Wi-Fi",
100+
"updating": "Your blox is updating. Please wait for an hour for the update to complete.",
101+
"disconnectHotspot": "Meanwhile, feel free to disconnect your phone from FxBlox hotspot.",
102+
"homeScreen": "Home Screen",
103+
"congratulations": "Congratulations",
104+
"setupComplete": "Setup Complete",
105+
"home": "Home",
106+
"checkInternet": "Check internet connectivity",
107+
"wrongPassword": "Entered Wrong Password? Go Back",
108+
"checkConnection": "Check Connection Again",
109+
"cyanFlashing": "If Blox is flashing 'Cyan', it probably means you have entered the wrong password for your wifi. Connect to 'FxBlox' Wifi again and retry.",
110+
"back": "Back",
111+
"reconnectWifi": "Reconnect Blox to Wi-Fi",
112+
"notConnectedToHotspot": "It seems you are no longer connected to Hotspot, check if FxBlox hotspot is still available, if not, blox is already connected to internet and you can go to Home",
113+
"unableToInitialize": "Unable to initialize the fula network! error: {{error}} for fulaIsReady={{fulaIsReady}}"
114+
},
115+
"connectToExistingBlox": {
116+
"title": "Bloxs in your network",
117+
"selectBloxs": "Select bloxs you want to add",
118+
"bloxUnitPrefix": "Blox unit",
119+
"ip": "IP",
120+
"peerId": "PEER ID",
121+
"hardwareId": "HARDWARE ID",
122+
"authorized": "Authorized",
123+
"notAuthorized": "Not Authorized",
124+
"checking": "Checking...",
125+
"alreadyExist": "Already exist",
126+
"addSelectedBloxs": "Add selected blox(s)",
127+
"generateAppPeerIdError": "ConnectToExistingBloxScreen:generateAppPeerId: "
128+
},
129+
"checkConnection": {
130+
"verifyingConnectionWith": "Verifying connection with {{ssid}}",
131+
"successfullyConnected": "Successfully connected to {{ssid}}.",
132+
"verifyingConnection": "Verifying connection...",
133+
"couldntConnect": "Couldn't connect with {{ssid}}.",
134+
"couldntConnectTryAgain": "Couldn't connect with {{ssid}}. Please try again.",
135+
"connectingWith": "Connecting with {{ssid}}...",
136+
"allDone": "All done\nYour device is connected with success!"
137+
}
138+
}

0 commit comments

Comments
 (0)