Commit 523c9065 authored by Sébastiaan Versteeg's avatar Sébastiaan Versteeg Committed by Sébastiaan Versteeg

Add localisation using i18next and i18next-scanner

parent 06582c29
...@@ -145,6 +145,7 @@ android { ...@@ -145,6 +145,7 @@ android {
} }
dependencies { dependencies {
compile project(':react-native-locale-detector')
compile project(':react-native-snackbar') compile project(':react-native-snackbar')
compile project(':react-native-fcm') compile project(':react-native-fcm')
compile project(':react-native-linear-gradient') compile project(':react-native-linear-gradient')
......
...@@ -4,6 +4,7 @@ import android.app.Application; ...@@ -4,6 +4,7 @@ import android.app.Application;
import com.facebook.react.ReactApplication; import com.facebook.react.ReactApplication;
import com.azendoo.reactnativesnackbar.SnackbarPackage; import com.azendoo.reactnativesnackbar.SnackbarPackage;
import com.i18n.reactnativei18n.ReactNativeI18n;
import com.evollu.react.fcm.FIRMessagingPackage; import com.evollu.react.fcm.FIRMessagingPackage;
import com.BV.LinearGradient.LinearGradientPackage; import com.BV.LinearGradient.LinearGradientPackage;
import com.oblador.vectoricons.VectorIconsPackage; import com.oblador.vectoricons.VectorIconsPackage;
...@@ -26,8 +27,9 @@ public class MainApplication extends Application implements ReactApplication { ...@@ -26,8 +27,9 @@ public class MainApplication extends Application implements ReactApplication {
@Override @Override
protected List<ReactPackage> getPackages() { protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList( return Arrays.<ReactPackage>asList(
new MainReactPackage(), new MainReactPackage(),
new SnackbarPackage(), new SnackbarPackage(),
new ReactNativeI18n(),
new FIRMessagingPackage(), new FIRMessagingPackage(),
new LinearGradientPackage(), new LinearGradientPackage(),
new VectorIconsPackage() new VectorIconsPackage()
......
rootProject.name = 'ThaliApp' rootProject.name = 'ThaliApp'
include ':react-native-snackbar' include ':react-native-snackbar'
project(':react-native-snackbar').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-snackbar/android') project(':react-native-snackbar').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-snackbar/android')
include ':react-native-locale-detector'
project(':react-native-locale-detector').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-locale-detector/android')
include ':react-native-fcm' include ':react-native-fcm'
project(':react-native-fcm').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fcm/android') project(':react-native-fcm').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fcm/android')
include ':react-native-linear-gradient' include ':react-native-linear-gradient'
......
...@@ -6,8 +6,11 @@ import createSagaMiddleware from 'redux-saga'; ...@@ -6,8 +6,11 @@ import createSagaMiddleware from 'redux-saga';
import FCM, { FCMEvent } from 'react-native-fcm'; import FCM, { FCMEvent } from 'react-native-fcm';
import Moment from 'moment'; import Moment from 'moment';
import 'moment/locale/nl'; import 'moment/locale/nl';
import { I18nextProvider } from 'react-i18next';
import * as reducers from './reducers'; import * as reducers from './reducers';
import i18n from './i18n';
import sagas from './sagas'; import sagas from './sagas';
import ReduxNavigator from './components/navigator'; import ReduxNavigator from './components/navigator';
import * as loginActions from './actions/login'; import * as loginActions from './actions/login';
...@@ -91,9 +94,11 @@ class Main extends Component { ...@@ -91,9 +94,11 @@ class Main extends Component {
render() { render() {
return ( return (
<Provider store={store}> <I18nextProvider i18n={i18n}>
<ReduxNavigator /> <Provider store={store}>
</Provider> <ReduxNavigator />
</Provider>
</I18nextProvider>
); );
} }
} }
......
import i18n from 'i18next';
import { reactI18nextModule } from 'react-i18next';
import i18nextReactNative from 'i18next-react-native-language-detector';
import locales from './locales/index';
i18n
.use(reactI18nextModule)
.use(i18nextReactNative)
.init({
nsSeparator: false,
keySeparator: false,
fallbackLng: 'nl',
resources: locales,
interpolation: {
escapeValue: false, // not needed for react!!
},
});
export default i18n;
/* eslint-disable import/no-extraneous-dependencies */
const Parser = require('i18next-scanner').Parser;
const fs = require('fs');
const path = require('path');
const componentsPath = 'app/components';
const options = {
nsSeparator: false,
keySeparator: false,
sort: true,
lngs: ['nl'],
func: {
list: ['t'],
},
defaultNs: 'test',
removeUnusedKeys: true,
resource: {
loadPath: 'app/locales/{{lng}}/{{ns}}.json',
savePath: 'app/locales/{{lng}}/{{ns}}.json',
},
};
const indexFiles = {};
options.lngs.forEach((lang) => {
indexFiles[lang] = [];
});
const components = fs.readdirSync(componentsPath).filter(f =>
fs.statSync(path.join(componentsPath, f)).isFile());
components.forEach((f) => {
options.defaultNs = f.substr(0, 1).toLowerCase() + f.substr(1, f.length - 4);
const parser = new Parser(options);
const content = fs.readFileSync(path.join(componentsPath, f), 'utf-8');
parser.parseFuncFromString(content);
parser.parseTransFromString(content);
const resStore = parser.get();
Object.keys(resStore).forEach((lng) => {
const namespaces = resStore[lng];
Object.keys(namespaces).forEach((ns) => {
const obj = namespaces[ns];
const resPath = parser.formatResourceSavePath(lng, ns);
if (Object.keys(obj).length > 0) {
const str = JSON.stringify(obj, null, 2);
const dir = path.dirname(resPath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
fs.writeFileSync(resPath, `${str}\n`);
indexFiles[lng].push(ns);
} else if (fs.existsSync(resPath)) {
fs.unlinkSync(resPath);
}
});
});
});
const indexStream = fs.createWriteStream('app/locales/index.js');
indexStream.once('open', () => {
Object.keys(indexFiles).forEach((lang) => {
const files = indexFiles[lang];
const langName = lang.toUpperCase();
files.forEach((fileName) => {
indexStream.write(`const ${fileName}${langName} = require('./${lang}/${fileName}.json');\n`);
});
});
indexStream.write('\n');
indexStream.write('export default {\n');
Object.keys(indexFiles).forEach((lang) => {
indexStream.write(` ${lang}: {\n`);
const files = indexFiles[lang];
const langName = lang.toUpperCase();
files.forEach((fileName) => {
indexStream.write(` ${fileName}: ${fileName}${langName},\n`);
});
indexStream.write(' },\n');
});
indexStream.write('};\n');
indexStream.end();
});
...@@ -444,6 +444,11 @@ ...@@ -444,6 +444,11 @@
F80608CE5FD849848A5498D4 /* Feather.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Feather.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Feather.ttf"; sourceTree = "<group>"; }; F80608CE5FD849848A5498D4 /* Feather.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Feather.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Feather.ttf"; sourceTree = "<group>"; };
F808FC5B0C6A43748E179BF6 /* Entypo.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Entypo.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Entypo.ttf"; sourceTree = "<group>"; }; F808FC5B0C6A43748E179BF6 /* Entypo.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Entypo.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Entypo.ttf"; sourceTree = "<group>"; };
FF381FFBF70744E6865413C2 /* SimpleLineIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = SimpleLineIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"; sourceTree = "<group>"; }; FF381FFBF70744E6865413C2 /* SimpleLineIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = SimpleLineIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"; sourceTree = "<group>"; };
8AB867AF03AC45AEB3C80BFA /* RNSnackbar.xcodeproj */ = {isa = PBXFileReference; name = "RNSnackbar.xcodeproj"; path = "../node_modules/react-native-snackbar/ios/RNSnackbar.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
290659E890FD4B2CB58F9F15 /* libRNSnackbar.a */ = {isa = PBXFileReference; name = "libRNSnackbar.a"; path = "libRNSnackbar.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
F80608CE5FD849848A5498D4 /* Feather.ttf */ = {isa = PBXFileReference; name = "Feather.ttf"; path = "../node_modules/react-native-vector-icons/Fonts/Feather.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
0E27F218F14040B7AFFA10E0 /* RNI18n.xcodeproj */ = {isa = PBXFileReference; name = "RNI18n.xcodeproj"; path = "../node_modules/react-native-locale-detector/RNI18n.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
4ED63341CE1E457597AA80EB /* libRNI18n.a */ = {isa = PBXFileReference; name = "libRNI18n.a"; path = "libRNI18n.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
...@@ -487,6 +492,7 @@ ...@@ -487,6 +492,7 @@
D9076E5B3D6E539979F86BE7 /* AVFoundation.framework in Frameworks */, D9076E5B3D6E539979F86BE7 /* AVFoundation.framework in Frameworks */,
A64C779AE827706A1A3A8C53 /* CoreMedia.framework in Frameworks */, A64C779AE827706A1A3A8C53 /* CoreMedia.framework in Frameworks */,
E9012CD6A7C0147AFA3AE7C9 /* CoreVideo.framework in Frameworks */, E9012CD6A7C0147AFA3AE7C9 /* CoreVideo.framework in Frameworks */,
2EEA998D004342DD928F8C45 /* libRNI18n.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
...@@ -724,6 +730,7 @@ ...@@ -724,6 +730,7 @@
5A90BA3FF7F8422A9150FDAA /* BVLinearGradient.xcodeproj */, 5A90BA3FF7F8422A9150FDAA /* BVLinearGradient.xcodeproj */,
8CED7FC8420844AF92491FE8 /* RNFIRMessaging.xcodeproj */, 8CED7FC8420844AF92491FE8 /* RNFIRMessaging.xcodeproj */,
8AB867AF03AC45AEB3C80BFA /* RNSnackbar.xcodeproj */, 8AB867AF03AC45AEB3C80BFA /* RNSnackbar.xcodeproj */,
0E27F218F14040B7AFFA10E0 /* RNI18n.xcodeproj */,
); );
name = Libraries; name = Libraries;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -1574,6 +1581,7 @@ ...@@ -1574,6 +1581,7 @@
"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient",
"$(SRCROOT)/../node_modules/react-native-fcm/ios", "$(SRCROOT)/../node_modules/react-native-fcm/ios",
"$(SRCROOT)/../node_modules/react-native-snackbar/ios", "$(SRCROOT)/../node_modules/react-native-snackbar/ios",
"$(SRCROOT)/../node_modules/react-native-locale-detector/RNI18n",
); );
INFOPLIST_FILE = ThaliAppTests/Info.plist; INFOPLIST_FILE = ThaliAppTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
...@@ -1608,6 +1616,7 @@ ...@@ -1608,6 +1616,7 @@
"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient",
"$(SRCROOT)/../node_modules/react-native-fcm/ios", "$(SRCROOT)/../node_modules/react-native-fcm/ios",
"$(SRCROOT)/../node_modules/react-native-snackbar/ios", "$(SRCROOT)/../node_modules/react-native-snackbar/ios",
"$(SRCROOT)/../node_modules/react-native-locale-detector/RNI18n",
); );
INFOPLIST_FILE = ThaliAppTests/Info.plist; INFOPLIST_FILE = ThaliAppTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
...@@ -1649,6 +1658,7 @@ ...@@ -1649,6 +1658,7 @@
"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient",
"$(SRCROOT)/../node_modules/react-native-fcm/ios", "$(SRCROOT)/../node_modules/react-native-fcm/ios",
"$(SRCROOT)/../node_modules/react-native-snackbar/ios", "$(SRCROOT)/../node_modules/react-native-snackbar/ios",
"$(SRCROOT)/../node_modules/react-native-locale-detector/RNI18n",
); );
INFOPLIST_FILE = ThaliApp/Info.plist; INFOPLIST_FILE = ThaliApp/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
...@@ -1682,6 +1692,7 @@ ...@@ -1682,6 +1692,7 @@
"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient",
"$(SRCROOT)/../node_modules/react-native-fcm/ios", "$(SRCROOT)/../node_modules/react-native-fcm/ios",
"$(SRCROOT)/../node_modules/react-native-snackbar/ios", "$(SRCROOT)/../node_modules/react-native-snackbar/ios",
"$(SRCROOT)/../node_modules/react-native-locale-detector/RNI18n",
); );
INFOPLIST_FILE = ThaliApp/Info.plist; INFOPLIST_FILE = ThaliApp/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
...@@ -1714,6 +1725,7 @@ ...@@ -1714,6 +1725,7 @@
"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient",
"$(SRCROOT)/../node_modules/react-native-fcm/ios", "$(SRCROOT)/../node_modules/react-native-fcm/ios",
"$(SRCROOT)/../node_modules/react-native-snackbar/ios", "$(SRCROOT)/../node_modules/react-native-snackbar/ios",
"$(SRCROOT)/../node_modules/react-native-locale-detector/RNI18n",
); );
INFOPLIST_FILE = "ThaliApp-tvOS/Info.plist"; INFOPLIST_FILE = "ThaliApp-tvOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
...@@ -1754,6 +1766,7 @@ ...@@ -1754,6 +1766,7 @@
"$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient",
"$(SRCROOT)/../node_modules/react-native-fcm/ios", "$(SRCROOT)/../node_modules/react-native-fcm/ios",
"$(SRCROOT)/../node_modules/react-native-snackbar/ios", "$(SRCROOT)/../node_modules/react-native-snackbar/ios",
"$(SRCROOT)/../node_modules/react-native-locale-detector/RNI18n",
); );
INFOPLIST_FILE = "ThaliApp-tvOS/Info.plist"; INFOPLIST_FILE = "ThaliApp-tvOS/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
......
...@@ -12,7 +12,8 @@ ...@@ -12,7 +12,8 @@
"lint:ci": "eslint *.js app", "lint:ci": "eslint *.js app",
"test": "jest", "test": "jest",
"test:watch": "jest --watch", "test:watch": "jest --watch",
"coverage": "jest --coverage" "coverage": "jest --coverage",
"locales:collect": "node collect-locales.js"
}, },
"jest": { "jest": {
"preset": "react-native", "preset": "react-native",
...@@ -29,6 +30,8 @@ ...@@ -29,6 +30,8 @@
] ]
}, },
"dependencies": { "dependencies": {
"i18next": "^10.0.3",
"i18next-react-native-language-detector": "^1.0.2",
"moment": "^2.18.1", "moment": "^2.18.1",
"prop-types": "^15.5.8", "prop-types": "^15.5.8",
"react": "16.2.0", "react": "16.2.0",
...@@ -37,6 +40,8 @@ ...@@ -37,6 +40,8 @@
"react-native-fcm": "^10.0.2", "react-native-fcm": "^10.0.2",
"react-native-linear-gradient": "^2.0.0", "react-native-linear-gradient": "^2.0.0",
"react-native-snackbar": "^0.4.3", "react-native-snackbar": "^0.4.3",
"react-i18next": "^6.0.6",
"react-native-locale-detector": "^1.0.1",
"react-native-vector-icons": "^4.0.1", "react-native-vector-icons": "^4.0.1",
"react-redux": "^5.0.2", "react-redux": "^5.0.2",
"redux": "^3.6.0", "redux": "^3.6.0",
...@@ -53,6 +58,7 @@ ...@@ -53,6 +58,7 @@
"eslint-plugin-jsx-a11y": "^3.0.2", "eslint-plugin-jsx-a11y": "^3.0.2",
"eslint-plugin-react": "^6.9.0", "eslint-plugin-react": "^6.9.0",
"eslint-plugin-react-native": "^2.2.1", "eslint-plugin-react-native": "^2.2.1",
"i18next-scanner": "^2.0.0",
"jest": "^19.0.2", "jest": "^19.0.2",
"react-native-cli": "^2.0.1", "react-native-cli": "^2.0.1",
"react-native-git-upgrade": "^0.2.7", "react-native-git-upgrade": "^0.2.7",
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment