aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/App.js23
-rw-r--r--src/components/Card.js31
-rw-r--r--src/i18n.js64
-rw-r--r--src/index.js1
4 files changed, 97 insertions, 22 deletions
diff --git a/src/App.js b/src/App.js
index 3832de1..1b97aec 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,25 +1,32 @@
1import React from 'react'; 1import React from 'react';
2import { useTranslation } from 'react-i18next';
2import { Card } from './components/Card'; 3import { Card } from './components/Card';
3import './style.css'; 4import './style.css';
4import logo from '../src/images/wifi.png'; 5import logo from '../src/images/wifi.png';
5 6
6function App() { 7function App() {
8 const { t, i18n } = useTranslation();
9
7 return ( 10 return (
8 <div className="App"> 11 <div className="App">
9 <h1> 12 <h1>
10 <img alt="icon" src={logo} width="32" height="32" /> 13 <img alt="icon" src={logo} width="32" height="32" />
11 &nbsp; WiFi Card 14 &nbsp; {t('title')}
12 </h1> 15 </h1>
13 16
14 <p className="tag"> 17 <div>
15 Print a simple card with your WiFi login details. Tape it to the fridge, 18 <label>{t('select')}</label>
16 keep it in your wallet, etc. 19 <select onChange={(e) => i18n.changeLanguage(e.target.value)}>
17 </p> 20 <option value="en-US">en-US</option>
21 <option value="简体中文">简体中文</option>
22 </select>
23 </div>
24
25 <p className="tag">{t('desc.use')}</p>
18 26
19 <p className="tag"> 27 <p className="tag">
20 Your WiFi information is never sent to the server. No tracking, 28 {t('desc.privacy')}{' '}
21 analytics, or fingerprinting are used on this website. View the{' '} 29 <a href="https://github.com/bndw/wifi-card">{t('desc.source')}</a>.
22 <a href="https://github.com/bndw/wifi-card">source code</a>.
23 </p> 30 </p>
24 31
25 <Card /> 32 <Card />
diff --git a/src/components/Card.js b/src/components/Card.js
index 7409d6c..50db589 100644
--- a/src/components/Card.js
+++ b/src/components/Card.js
@@ -1,5 +1,6 @@
1import QRCode from 'qrcode.react'; 1import QRCode from 'qrcode.react';
2import { useEffect, useRef, useState } from 'react'; 2import { useEffect, useRef, useState } from 'react';
3import { useTranslation } from 'react-i18next';
3import './style.css'; 4import './style.css';
4 5
5export const Card = () => { 6export const Card = () => {
@@ -12,7 +13,7 @@ export const Card = () => {
12 hidePassword: false, 13 hidePassword: false,
13 }); 14 });
14 const [portrait, setPortrait] = useState(false); 15 const [portrait, setPortrait] = useState(false);
15 16 const { t } = useTranslation();
16 const escape = (v) => { 17 const escape = (v) => {
17 const needsEscape = ['"', ';', ',', ':', '\\']; 18 const needsEscape = ['"', ';', ',', ':', '\\'];
18 19
@@ -31,17 +32,17 @@ export const Card = () => {
31 const onPrint = () => { 32 const onPrint = () => {
32 if (network.ssid.length > 0) { 33 if (network.ssid.length > 0) {
33 if (network.password.length < 8 && network.encryptionMode === 'WPA') { 34 if (network.password.length < 8 && network.encryptionMode === 'WPA') {
34 alert('Password must be at least 8 characters'); 35 alert(t('wifi.alert.password.8'));
35 } else if ( 36 } else if (
36 network.password.length < 5 && 37 network.password.length < 5 &&
37 network.encryptionMode === 'WEP' 38 network.encryptionMode === 'WEP'
38 ) { 39 ) {
39 alert('Password must be at least 5 characters'); 40 alert(t('wifi.alert.password.5'));
40 } else { 41 } else {
41 window.print(); 42 window.print();
42 } 43 }
43 } else { 44 } else {
44 alert('Network name cannot be empty'); 45 alert(t('wifi.alert.name'));
45 } 46 }
46 }; 47 };
47 48
@@ -62,7 +63,9 @@ export const Card = () => {
62 id="print-area" 63 id="print-area"
63 style={{ maxWidth: portrait ? '350px' : '100%' }} 64 style={{ maxWidth: portrait ? '350px' : '100%' }}
64 > 65 >
65 <h1 style={{ textAlign: portrait ? 'center' : 'left' }}>WiFi Login</h1> 66 <h1 style={{ textAlign: portrait ? 'center' : 'left' }}>
67 {t('wifi.login')}
68 </h1>
66 69
67 <div 70 <div
68 className="details" 71 className="details"
@@ -76,12 +79,12 @@ export const Card = () => {
76 /> 79 />
77 80
78 <div className="inputs"> 81 <div className="inputs">
79 <label>Network name</label> 82 <label>{t('wifi.name')}</label>
80 <textarea 83 <textarea
81 id="ssid" 84 id="ssid"
82 type="text" 85 type="text"
83 maxLength="32" 86 maxLength="32"
84 placeholder="WiFi Network name" 87 placeholder={t('wifi.name.placeholder')}
85 autoComplete="off" 88 autoComplete="off"
86 autoCorrect="off" 89 autoCorrect="off"
87 autoCapitalize="none" 90 autoCapitalize="none"
@@ -95,7 +98,7 @@ export const Card = () => {
95 ${network.encryptionMode === 'nopass' && 'hidden'} 98 ${network.encryptionMode === 'nopass' && 'hidden'}
96 `} 99 `}
97 > 100 >
98 Password 101 {t('wifi.password')}
99 </label> 102 </label>
100 <textarea 103 <textarea
101 id="password" 104 id="password"
@@ -109,7 +112,7 @@ export const Card = () => {
109 portrait && network.password.length > 40 ? '5em' : 'auto', 112 portrait && network.password.length > 40 ? '5em' : 'auto',
110 }} 113 }}
111 maxLength="63" 114 maxLength="63"
112 placeholder="Password" 115 placeholder={t('wifi.password.placeholder')}
113 autoComplete="off" 116 autoComplete="off"
114 autoCorrect="off" 117 autoCorrect="off"
115 autoCapitalize="none" 118 autoCapitalize="none"
@@ -136,12 +139,12 @@ export const Card = () => {
136 for="hide-password-checkbox" 139 for="hide-password-checkbox"
137 className={network.encryptionMode === 'nopass' ? 'hidden' : ''} 140 className={network.encryptionMode === 'nopass' ? 'hidden' : ''}
138 > 141 >
139 Hide password 142 {t('wifi.password.hide')}
140 </label> 143 </label>
141 </div> 144 </div>
142 145
143 <div className="no-print"> 146 <div className="no-print">
144 <label>Encryption:</label> 147 <label>{t('wifi.password.encryption')}:</label>
145 <input 148 <input
146 type="radio" 149 type="radio"
147 name="encrypt-select" 150 name="encrypt-select"
@@ -181,16 +184,16 @@ export const Card = () => {
181 <span role="img" aria-label="mobile-phone"> 184 <span role="img" aria-label="mobile-phone">
182 📸📱 185 📸📱
183 </span> 186 </span>
184 Point your phone's camera at the QR Code to connect automatically 187 {t('wifi.tip')}
185 </p> 188 </p>
186 </fieldset> 189 </fieldset>
187 190
188 <div className="buttons"> 191 <div className="buttons">
189 <button id="rotate" onClick={() => setPortrait(!portrait)}> 192 <button id="rotate" onClick={() => setPortrait(!portrait)}>
190 Rotate 193 {t('button.rotate')}
191 </button> 194 </button>
192 <button id="print" onClick={onPrint}> 195 <button id="print" onClick={onPrint}>
193 Print 196 {t('button.print')}
194 </button> 197 </button>
195 </div> 198 </div>
196 </div> 199 </div>
diff --git a/src/i18n.js b/src/i18n.js
new file mode 100644
index 0000000..2990dcb
--- /dev/null
+++ b/src/i18n.js
@@ -0,0 +1,64 @@
1import i18n from 'i18next';
2import { initReactI18next } from 'react-i18next';
3
4const resources = {
5 'en-US': {
6 translation: {
7 title: 'WiFi Card',
8 'desc.use':
9 'Print a simple card with your WiFi login details. Tape it to the fridge, keep it in your wallet, etc.',
10 'desc.privacy':
11 'Your WiFi information is never sent to the server. No tracking, analytics, or fingerprinting are used on this website. View the',
12 'desc.source': 'source code',
13 'wifi.login': 'WiFi Login',
14 'wifi.name': 'Network name',
15 'wifi.name.placeholder': 'WiFi Network name',
16 'wifi.password': 'Password',
17 'wifi.password.placeholder': 'Password',
18 'wifi.password.hide': 'Hide password field before printing',
19 'wifi.password.encryption': 'Encryption',
20 'wifi.tip':
21 "Point your phone's camera at the QR Code to connect automatically",
22 'wifi.alert.name': 'Network name cannot be empty',
23 'wifi.alert.password.length.5': 'Password must be at least 5 characters',
24 'wifi.alert.password.8': 'Password must be at least 8 characters',
25 'button.rotate': 'Rotate',
26 'button.print': 'Print',
27 select: 'Select Language',
28 },
29 },
30 简体中文: {
31 translation: {
32 title: 'WiFi 连接卡',
33 'desc.use':
34 '打印一张带有 WiFi 详细信息的登录卡片,把它贴到冰箱上、放到你的钱包里...',
35 'desc.privacy':
36 '您的 WiFi 信息永远不会发送到服务端。本网站不使用追踪、分析或指纹识别。查看',
37 'desc.source': '源码',
38 'wifi.login': '连接 WiFi',
39 'wifi.name': '网络名称',
40 'wifi.name.placeholder': 'WiFi 网络名称',
41 'wifi.password': '密码',
42 'wifi.password.placeholder': '密码',
43 'wifi.password.hide': '打印前隐藏密码字段',
44 'wifi.password.encryption': '加密',
45 'wifi.tip': '将手机摄像头对准二维码即可自动连接',
46 'wifi.alert.name': '网络名称不能为空',
47 'wifi.alert.password.length.5': '密码至少 5 个字符',
48 'wifi.alert.password.8': '密码至少 8 个字符',
49 'button.rotate': '翻转',
50 'button.print': '打印',
51 select: '选择语言',
52 },
53 },
54};
55
56i18n.use(initReactI18next).init({
57 resources,
58 lng: 'en-US',
59 interpolation: {
60 escapeValue: false,
61 },
62});
63
64export default i18n;
diff --git a/src/index.js b/src/index.js
index c1f31c5..30091ec 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,6 +1,7 @@
1import React from 'react'; 1import React from 'react';
2import ReactDOM from 'react-dom'; 2import ReactDOM from 'react-dom';
3import App from './App'; 3import App from './App';
4import './i18n';
4 5
5ReactDOM.render( 6ReactDOM.render(
6 <React.StrictMode> 7 <React.StrictMode>