diff --git a/boilerplate/app/app.tsx b/boilerplate/app/app.tsx
index 67873286b..dd7b7f2d1 100644
--- a/boilerplate/app/app.tsx
+++ b/boilerplate/app/app.tsx
@@ -17,9 +17,10 @@ if (__DEV__) {
require("./devtools/ReactotronConfig.ts")
}
import "./utils/gestureHandler"
-import "./i18n"
+import { initI18n } from "./i18n"
import "./utils/ignoreWarnings"
import { useFonts } from "expo-font"
+import { useEffect, useState } from "react"
import { initialWindowMetrics, SafeAreaProvider } from "react-native-safe-area-context"
import * as Linking from "expo-linking"
import { useInitialRootStore } from "./models" // @mst remove-current-line
@@ -71,6 +72,11 @@ function App(props: AppProps) {
} = useNavigationPersistence(storage, NAVIGATION_PERSISTENCE_KEY)
const [areFontsLoaded, fontLoadError] = useFonts(customFontsToLoad)
+ const [isI18nInitialized, setIsI18nInitialized] = useState(false)
+
+ useEffect(() => {
+ initI18n().then(() => setIsI18nInitialized(true))
+ }, [])
// @mst replace-next-line React.useEffect(() => {
const { rehydrated } = useInitialRootStore(() => {
@@ -93,7 +99,12 @@ function App(props: AppProps) {
// In Android: https://stackoverflow.com/a/45838109/204044
// You can replace with your own loading component if you wish.
// @mst replace-next-line if (!isNavigationStateRestored || (!areFontsLoaded && !fontLoadError)) {
- if (!rehydrated || !isNavigationStateRestored || (!areFontsLoaded && !fontLoadError)) {
+ if (
+ !rehydrated ||
+ !isNavigationStateRestored ||
+ !isI18nInitialized ||
+ (!areFontsLoaded && !fontLoadError)
+ ) {
return null
}
diff --git a/boilerplate/app/components/Button.tsx b/boilerplate/app/components/Button.tsx
index 8befa0fc5..2c97647f2 100644
--- a/boilerplate/app/components/Button.tsx
+++ b/boilerplate/app/components/Button.tsx
@@ -91,7 +91,7 @@ export interface ButtonProps extends PressableProps {
* @returns {JSX.Element} The rendered `Button` component.
* @example
*
diff --git a/boilerplate/app/screens/DemoShowroomScreen/DemoShowroomScreen.tsx b/boilerplate/app/screens/DemoShowroomScreen/DemoShowroomScreen.tsx
index 1b6e4787f..448f608d1 100644
--- a/boilerplate/app/screens/DemoShowroomScreen/DemoShowroomScreen.tsx
+++ b/boilerplate/app/screens/DemoShowroomScreen/DemoShowroomScreen.tsx
@@ -213,7 +213,7 @@ export const DemoShowroomScreen: FC> =
renderSectionFooter={() => }
ListHeaderComponent={
-
+
}
onScrollToIndexFailed={scrollToIndexFailed}
diff --git a/boilerplate/app/screens/DemoShowroomScreen/demos/DemoAutoImage.tsx b/boilerplate/app/screens/DemoShowroomScreen/demos/DemoAutoImage.tsx
index a72923f27..f16a4ba4c 100644
--- a/boilerplate/app/screens/DemoShowroomScreen/demos/DemoAutoImage.tsx
+++ b/boilerplate/app/screens/DemoShowroomScreen/demos/DemoAutoImage.tsx
@@ -37,9 +37,9 @@ const $aspectRatioBox: ThemedStyle = (theme) => ({
export const DemoAutoImage: Demo = {
name: "AutoImage",
- description: "demoAutoImage.description",
+ description: "demoAutoImage:description",
data: ({ theme, themed }) => [
-
+ ,
-
+ ,
- {translate("demoAutoImage.useCase.scaledToFitDimensions.heightAuto")}
+ {translate("demoAutoImage:useCase.scaledToFitDimensions.heightAuto")}
@@ -162,7 +162,7 @@ export const DemoAutoImage: Demo = {
- {translate("demoAutoImage.useCase.scaledToFitDimensions.widthAuto")}
+ {translate("demoAutoImage:useCase.scaledToFitDimensions.widthAuto")}
@@ -218,7 +218,7 @@ export const DemoAutoImage: Demo = {
- {translate("demoAutoImage.useCase.scaledToFitDimensions.bothManual")}
+ {translate("demoAutoImage:useCase.scaledToFitDimensions.bothManual")}
,
],
diff --git a/boilerplate/app/screens/DemoShowroomScreen/demos/DemoButton.tsx b/boilerplate/app/screens/DemoShowroomScreen/demos/DemoButton.tsx
index e02680d63..3ed7715d0 100644
--- a/boilerplate/app/screens/DemoShowroomScreen/demos/DemoButton.tsx
+++ b/boilerplate/app/screens/DemoShowroomScreen/demos/DemoButton.tsx
@@ -44,11 +44,11 @@ const $disabledButtonTextStyle: ThemedStyle = ({ colors }) => ({
export const DemoButton: Demo = {
name: "Button",
- description: "demoButton.description",
+ description: "demoButton:description",
data: ({ themed }) => [
Default - Laboris In Labore
@@ -60,16 +60,16 @@ export const DemoButton: Demo = {
,
-
+
-
+
- {translate("demoButton.useCase.passingContent.children")}
+ {translate("demoButton:useCase.passingContent.children")}
)}
>
- {translate("demoButton.useCase.passingContent.rightAccessory")}
+ {translate("demoButton:useCase.passingContent.rightAccessory")}
@@ -88,20 +88,20 @@ export const DemoButton: Demo = {
)}
>
- {translate("demoButton.useCase.passingContent.leftAccessory")}
+ {translate("demoButton:useCase.passingContent.leftAccessory")}
- {translate("demoButton.useCase.passingContent.nestedChildren")}
+ {translate("demoButton:useCase.passingContent.nestedChildren")}
{` `}
- {translate("demoButton.useCase.passingContent.nestedChildren2")}
+ {translate("demoButton:useCase.passingContent.nestedChildren2")}
{` `}
- {translate("demoButton.useCase.passingContent.nestedChildren3")}
+ {translate("demoButton:useCase.passingContent.nestedChildren3")}
@@ -116,21 +116,21 @@ export const DemoButton: Demo = {
)}
>
- {translate("demoButton.useCase.passingContent.multiLine")}
+ {translate("demoButton:useCase.passingContent.multiLine")}
,
- {translate("demoButton.useCase.styling.styleContainer")}
+ {translate("demoButton:useCase.styling.styleContainer")}
- {translate("demoButton.useCase.styling.styleText")}
+ {translate("demoButton:useCase.styling.styleText")}
@@ -138,7 +138,7 @@ export const DemoButton: Demo = {
preset="reversed"
RightAccessory={() => }
>
- {translate("demoButton.useCase.styling.styleAccessories")}
+ {translate("demoButton:useCase.styling.styleAccessories")}
@@ -156,13 +156,13 @@ export const DemoButton: Demo = {
/>
)}
>
- {translate("demoButton.useCase.styling.pressedState")}
+ {translate("demoButton:useCase.styling.pressedState")}
,
- {translate("demoButton.useCase.disabling.standard")}
+ {translate("demoButton:useCase.disabling.standard")}
@@ -181,7 +181,7 @@ export const DemoButton: Demo = {
pressedStyle={themed($customButtonPressedStyle)}
pressedTextStyle={themed($customButtonPressedTextStyle)}
>
- {translate("demoButton.useCase.disabling.filled")}
+ {translate("demoButton:useCase.disabling.filled")}
@@ -192,7 +192,7 @@ export const DemoButton: Demo = {
pressedStyle={themed($customButtonPressedStyle)}
pressedTextStyle={themed($customButtonPressedTextStyle)}
>
- {translate("demoButton.useCase.disabling.reversed")}
+ {translate("demoButton:useCase.disabling.reversed")}
@@ -210,7 +210,7 @@ export const DemoButton: Demo = {
/>
)}
>
- {translate("demoButton.useCase.disabling.accessory")}
+ {translate("demoButton:useCase.disabling.accessory")}
@@ -221,7 +221,7 @@ export const DemoButton: Demo = {
pressedStyle={themed($customButtonPressedStyle)}
pressedTextStyle={themed($customButtonPressedTextStyle)}
>
- {translate("demoButton.useCase.disabling.textStyle")}
+ {translate("demoButton:useCase.disabling.textStyle")}
,
],
diff --git a/boilerplate/app/screens/DemoShowroomScreen/demos/DemoCard.tsx b/boilerplate/app/screens/DemoShowroomScreen/demos/DemoCard.tsx
index 8c9f574b3..e5660e3ff 100644
--- a/boilerplate/app/screens/DemoShowroomScreen/demos/DemoCard.tsx
+++ b/boilerplate/app/screens/DemoShowroomScreen/demos/DemoCard.tsx
@@ -6,88 +6,88 @@ import { DemoUseCase } from "../DemoUseCase"
export const DemoCard: Demo = {
name: "Card",
- description: "demoCard.description",
+ description: "demoCard:description",
data: ({ theme }) => [
,
,
,
,
[
,
,
= ({ colors }) => ({
export const DemoHeader: Demo = {
name: "Header",
- description: "demoHeader.description",
+ description: "demoHeader:description",
data: ({ theme, themed }) => [
,
,
,
,
= ({ colors }) => ({
export const DemoIcon: Demo = {
name: "Icon",
- description: "demoIcon.description",
+ description: "demoIcon:description",
data: ({ theme, themed }) => [
@@ -52,8 +52,8 @@ export const DemoIcon: Demo = {
,
@@ -63,8 +63,8 @@ export const DemoIcon: Demo = {
,
,
= ({ colors, spacing }) => ({
export const DemoListItem: Demo = {
name: "ListItem",
- description: "demoListItem.description",
+ description: "demoListItem:description",
data: ({ theme, themed }) => [
- {translate("demoListItem.useCase.height.defaultHeight")}
+ {translate("demoListItem:useCase.height.defaultHeight")}
- {translate("demoListItem.useCase.height.customHeight")}
+ {translate("demoListItem:useCase.height.customHeight")}
- {translate("demoListItem.useCase.height.textHeight")}
+ {translate("demoListItem:useCase.height.textHeight")}
- {translate("demoListItem.useCase.height.longText")}
+ {translate("demoListItem:useCase.height.longText")}
,
- {translate("demoListItem.useCase.separators.topSeparator")}
+ {translate("demoListItem:useCase.separators.topSeparator")}
- {translate("demoListItem.useCase.separators.topAndBottomSeparator")}
+ {translate("demoListItem:useCase.separators.topAndBottomSeparator")}
- {translate("demoListItem.useCase.separators.bottomSeparator")}
+ {translate("demoListItem:useCase.separators.bottomSeparator")}
,
- {translate("demoListItem.useCase.icons.leftIcon")}
+ {translate("demoListItem:useCase.icons.leftIcon")}
- {translate("demoListItem.useCase.icons.rightIcon")}
+ {translate("demoListItem:useCase.icons.rightIcon")}
- {translate("demoListItem.useCase.icons.leftRightIcons")}
+ {translate("demoListItem:useCase.icons.leftRightIcons")}
,
}
>
- {translate("demoListItem.useCase.customLeftRight.customLeft")}
+ {translate("demoListItem:useCase.customLeftRight.customLeft")}
}
>
- {translate("demoListItem.useCase.customLeftRight.customRight")}
+ {translate("demoListItem:useCase.customLeftRight.customRight")}
,
-
-
- {translate("demoListItem.useCase.passingContent.children")}
+
+
+ {translate("demoListItem:useCase.passingContent.children")}
- {translate("demoListItem.useCase.passingContent.nestedChildren1")}
+ {translate("demoListItem:useCase.passingContent.nestedChildren1")}
{` `}
- {translate("demoListItem.useCase.passingContent.nestedChildren2")}
+ {translate("demoListItem:useCase.passingContent.nestedChildren2")}
,
@@ -171,11 +171,11 @@ export const DemoListItem: Demo = {
,
- {translate("demoListItem.useCase.styling.styledText")}
+ {translate("demoListItem:useCase.styling.styledText")}
- {translate("demoListItem.useCase.styling.styledText")}
+ {translate("demoListItem:useCase.styling.styledText")}
- {translate("demoListItem.useCase.styling.styledContainer")}
+ {translate("demoListItem:useCase.styling.styledContainer")}
- {translate("demoListItem.useCase.styling.tintedIcons")}
+ {translate("demoListItem:useCase.styling.tintedIcons")}
,
],
diff --git a/boilerplate/app/screens/DemoShowroomScreen/demos/DemoText.tsx b/boilerplate/app/screens/DemoShowroomScreen/demos/DemoText.tsx
index be65e15f9..12be402be 100644
--- a/boilerplate/app/screens/DemoShowroomScreen/demos/DemoText.tsx
+++ b/boilerplate/app/screens/DemoShowroomScreen/demos/DemoText.tsx
@@ -7,113 +7,113 @@ import { translate } from "@/i18n"
export const DemoText: Demo = {
name: "Text",
- description: "demoText.description",
+ description: "demoText:description",
data: ({ theme }) => [
- {translate("demoText.useCase.presets.default")}
+ {translate("demoText:useCase.presets.default")}
- {translate("demoText.useCase.presets.bold")}
+ {translate("demoText:useCase.presets.bold")}
- {translate("demoText.useCase.presets.subheading")}
+ {translate("demoText:useCase.presets.subheading")}
- {translate("demoText.useCase.presets.heading")}
+ {translate("demoText:useCase.presets.heading")},
- {translate("demoText.useCase.sizes.xs")}
+ {translate("demoText:useCase.sizes.xs")}
- {translate("demoText.useCase.sizes.sm")}
+ {translate("demoText:useCase.sizes.sm")}
- {translate("demoText.useCase.sizes.md")}
+ {translate("demoText:useCase.sizes.md")}
- {translate("demoText.useCase.sizes.lg")}
+ {translate("demoText:useCase.sizes.lg")}
- {translate("demoText.useCase.sizes.xl")}
+ {translate("demoText:useCase.sizes.xl")}
- {translate("demoText.useCase.sizes.xxl")}
+ {translate("demoText:useCase.sizes.xxl")},
- {translate("demoText.useCase.weights.light")}
+ {translate("demoText:useCase.weights.light")}
- {translate("demoText.useCase.weights.normal")}
+ {translate("demoText:useCase.weights.normal")}
- {translate("demoText.useCase.weights.medium")}
+ {translate("demoText:useCase.weights.medium")}
- {translate("demoText.useCase.weights.semibold")}
+ {translate("demoText:useCase.weights.semibold")}
- {translate("demoText.useCase.weights.bold")}
+ {translate("demoText:useCase.weights.bold")},
-
+
-
-
+
+
- {translate("demoText.useCase.passingContent.children")}
+ {translate("demoText:useCase.passingContent.children")}
- {translate("demoText.useCase.passingContent.nestedChildren")}
- {translate("demoText.useCase.passingContent.nestedChildren2")}
+ {translate("demoText:useCase.passingContent.nestedChildren")}
+ {translate("demoText:useCase.passingContent.nestedChildren2")}
{` `}
- {translate("demoText.useCase.passingContent.nestedChildren3")}
+ {translate("demoText:useCase.passingContent.nestedChildren3")}
{` `}
- {translate("demoText.useCase.passingContent.nestedChildren4")}
+ {translate("demoText:useCase.passingContent.nestedChildren4")},
- {translate("demoText.useCase.styling.text")}
+ {translate("demoText:useCase.styling.text")}
{` `}
- {translate("demoText.useCase.styling.text2")}
+ {translate("demoText:useCase.styling.text2")}
{` `}
- {translate("demoText.useCase.styling.text3")}
+ {translate("demoText:useCase.styling.text3")}
,
diff --git a/boilerplate/app/screens/DemoShowroomScreen/demos/DemoTextField.tsx b/boilerplate/app/screens/DemoShowroomScreen/demos/DemoTextField.tsx
index 8eb192e93..a396f4dd4 100644
--- a/boilerplate/app/screens/DemoShowroomScreen/demos/DemoTextField.tsx
+++ b/boilerplate/app/screens/DemoShowroomScreen/demos/DemoTextField.tsx
@@ -42,19 +42,19 @@ const $customRightAccessoryStyle: ThemedStyle = ({ colors }) => ({
export const DemoTextField: Demo = {
name: "TextField",
- description: "demoTextField.description",
+ description: "demoTextField:description",
data: ({ themed }) => [
@@ -63,11 +63,11 @@ export const DemoTextField: Demo = {
@@ -76,36 +76,36 @@ export const DemoTextField: Demo = {
,
@@ -113,9 +113,9 @@ export const DemoTextField: Demo = {
}
/>
@@ -123,9 +123,9 @@ export const DemoTextField: Demo = {
}
@@ -134,9 +134,9 @@ export const DemoTextField: Demo = {
,
= ({ spacing }) => ({
export const DemoToggle: Demo = {
name: "Toggle",
- description: "demoToggle.description",
+ description: "demoToggle:description",
data: ({ theme, themed }) => [
,
@@ -79,7 +79,7 @@ export const DemoToggle: Demo = {
- {translate("demoToggle.useCase.statuses.noStatus")}
+ {translate("demoToggle:useCase.statuses.noStatus")}
@@ -92,7 +92,7 @@ export const DemoToggle: Demo = {
- {translate("demoToggle.useCase.statuses.errorStatus")}
+ {translate("demoToggle:useCase.statuses.errorStatus")}
@@ -105,37 +105,37 @@ export const DemoToggle: Demo = {
- {translate("demoToggle.useCase.statuses.disabledStatus")}
+ {translate("demoToggle:useCase.statuses.disabledStatus")}
,
@@ -143,28 +143,28 @@ export const DemoToggle: Demo = {
value
status="error"
icon="ladybug"
- labelTx="demoToggle.useCase.passingContent.useCase.customCheckBox.label"
+ labelTx="demoToggle:useCase.passingContent.useCase.customCheckBox.label"
/>
,
- {translate("demoToggle.useCase.styling.outerWrapper")}
+ {translate("demoToggle:useCase.styling.outerWrapper")}
@@ -246,7 +246,7 @@ export const DemoToggle: Demo = {
}}
/>
- {translate("demoToggle.useCase.styling.innerWrapper")}
+ {translate("demoToggle:useCase.styling.innerWrapper")}
@@ -316,7 +316,7 @@ export const DemoToggle: Demo = {
/>
- {translate("demoToggle.useCase.styling.inputDetail")}
+ {translate("demoToggle:useCase.styling.inputDetail")}
@@ -324,7 +324,7 @@ export const DemoToggle: Demo = {
diff --git a/boilerplate/app/screens/ErrorScreen/ErrorDetails.tsx b/boilerplate/app/screens/ErrorScreen/ErrorDetails.tsx
index 8a45dfd11..85fe45a89 100644
--- a/boilerplate/app/screens/ErrorScreen/ErrorDetails.tsx
+++ b/boilerplate/app/screens/ErrorScreen/ErrorDetails.tsx
@@ -25,8 +25,8 @@ export function ErrorDetails(props: ErrorDetailsProps) {
>
-
-
+
+
)
diff --git a/boilerplate/app/screens/LoginScreen.tsx b/boilerplate/app/screens/LoginScreen.tsx
index bcac21fcd..ac82adca6 100644
--- a/boilerplate/app/screens/LoginScreen.tsx
+++ b/boilerplate/app/screens/LoginScreen.tsx
@@ -78,10 +78,10 @@ export const LoginScreen: FC = observer(function LoginScreen(_
contentContainerStyle={themed($screenContentContainer)}
safeAreaEdges={["top", "bottom"]}
>
-
-
+
+
{attemptsCount > 2 && (
-
+
)}
= observer(function LoginScreen(_
autoComplete="email"
autoCorrect={false}
keyboardType="email-address"
- labelTx="loginScreen.emailFieldLabel"
- placeholderTx="loginScreen.emailFieldPlaceholder"
+ labelTx="loginScreen:emailFieldLabel"
+ placeholderTx="loginScreen:emailFieldPlaceholder"
helper={error}
status={error ? "error" : undefined}
onSubmitEditing={() => authPasswordInput.current?.focus()}
@@ -108,15 +108,15 @@ export const LoginScreen: FC = observer(function LoginScreen(_
autoComplete="password"
autoCorrect={false}
secureTextEntry={isAuthPasswordHidden}
- labelTx="loginScreen.passwordFieldLabel"
- placeholderTx="loginScreen.passwordFieldPlaceholder"
+ labelTx="loginScreen:passwordFieldLabel"
+ placeholderTx="loginScreen:passwordFieldPlaceholder"
onSubmitEditing={login}
RightAccessory={PasswordRightAccessory}
/>
= observer(
useHeader(
{
- rightTx: "common.logOut",
+ rightTx: "common:logOut",
onRightPress: logout,
},
[logout],
@@ -54,10 +54,10 @@ export const WelcomeScreen: FC = observer(
-
+ = observer(
-
+
{/* @demo remove-block-start */}
{/* @demo remove-block-end */}
diff --git a/boilerplate/app/utils/formatDate.ts b/boilerplate/app/utils/formatDate.ts
index 213cfd13b..be5b89b96 100644
--- a/boilerplate/app/utils/formatDate.ts
+++ b/boilerplate/app/utils/formatDate.ts
@@ -13,7 +13,7 @@ import { i18n } from "@/i18n"
type Options = Parameters[2]
const getLocale = (): Locale => {
- const locale = i18n.locale.split("-")[0]
+ const locale = i18n.language.split("-")[0]
return locale === "ar" ? ar : locale === "ko" ? ko : en
}
diff --git a/boilerplate/package.json b/boilerplate/package.json
index 2e1cfaeb1..c92862875 100644
--- a/boilerplate/package.json
+++ b/boilerplate/package.json
@@ -49,12 +49,14 @@
"expo-splash-screen": "~0.27.4",
"expo-status-bar": "~1.12.1",
"expo-system-ui": "~3.0.7",
- "i18n-js": "^4.4.3",
+ "i18next": "^23.14.0",
+ "intl-pluralrules": "^2.0.1",
"mobx": "6.10.2",
"mobx-react-lite": "4.0.5",
"mobx-state-tree": "5.3.0",
"react": "18.2.0",
"react-dom": "18.2.0",
+ "react-i18next": "^15.0.1",
"react-native": "0.74.5",
"react-native-drawer-layout": "4.0.0-alpha.9",
"react-native-gesture-handler": "~2.16.1",
@@ -70,7 +72,6 @@
"@babel/preset-env": "^7.20.0",
"@babel/runtime": "^7.20.0",
"@testing-library/react-native": "^12.5.2",
- "@types/i18n-js": "3.8.2",
"@types/jest": "^29.2.1",
"@types/react": "~18.2.14",
"babel-jest": "^29.2.1",
@@ -99,8 +100,5 @@
},
"engines": {
"node": "^18.18.0 || >=20.0.0"
- },
- "resolutions": {
- "eslint-plugin-react": "7.35.2"
}
}
diff --git a/boilerplate/src/app/index.tsx b/boilerplate/src/app/index.tsx
index d4850deaf..12d57ca74 100644
--- a/boilerplate/src/app/index.tsx
+++ b/boilerplate/src/app/index.tsx
@@ -20,15 +20,15 @@ export default observer(function WelcomeScreen() {
-
+
-
+
)
diff --git a/boilerplate/test/i18n.test.ts b/boilerplate/test/i18n.test.ts
index a9817c75e..acee7ec22 100644
--- a/boilerplate/test/i18n.test.ts
+++ b/boilerplate/test/i18n.test.ts
@@ -4,7 +4,7 @@ import { exec } from "child_process"
// Use this array for keys that for whatever reason aren't greppable so they
// don't hold your test suite hostage by always failing.
const EXCEPTIONS: string[] = [
- // "welcomeScreen.readyForLaunch",
+ // "welcomeScreen:readyForLaunch",
]
function iterate(obj, stack, array) {
@@ -48,7 +48,9 @@ describe("i18n", () => {
// grep "[T\|t]x=[{]\?\"\S*\"[}]\?\|translate(\"\S*\"" -ohr './app' | grep -o "\".*\""
const command = `grep "[T\\|t]x=[{]\\?\\"\\S*\\"[}]\\?\\|translate(\\"\\S*\\"" -ohr './app' | grep -o "\\".*\\""`
exec(command, (_, stdout) => {
- const allTranslationsDefined = iterate(en, "", [])
+ const allTranslationsDefinedOld = iterate(en, "", [])
+ // Replace first instance of "." because of i18next namespace separator
+ const allTranslationsDefined = allTranslationsDefinedOld.map((key) => key.replace(".", ":"))
const allTranslationsUsed = stdout.replace(/"/g, "").split("\n")
allTranslationsUsed.splice(-1, 1)
diff --git a/boilerplate/test/setup.ts b/boilerplate/test/setup.ts
index c1a6b6a5c..c2f830c3e 100644
--- a/boilerplate/test/setup.ts
+++ b/boilerplate/test/setup.ts
@@ -23,12 +23,15 @@ jest.doMock("react-native", () => {
)
})
-jest.mock("i18n-js", () => ({
- currentLocale: () => "en",
+jest.mock('i18next', () => ({
+ currentLocale: "en",
t: (key: string, params: Record) => {
+ return `${key} ${JSON.stringify(params)}`
+ },
+ translate: (key: string, params: Record) => {
return `${key} ${JSON.stringify(params)}`
},
-}))
+}));
jest.mock("expo-localization", () => ({
...jest.requireActual("expo-localization"),
@@ -37,7 +40,8 @@ jest.mock("expo-localization", () => ({
jest.mock("../app/i18n/i18n.ts", () => ({
i18n: {
- locale: "en",
+ isInitialized: true,
+ language: "en",
t: (key: string, params: Record) => {
return `${key} ${JSON.stringify(params)}`
},
diff --git a/boilerplate/tsconfig.json b/boilerplate/tsconfig.json
index e6c483c80..984821d4f 100644
--- a/boilerplate/tsconfig.json
+++ b/boilerplate/tsconfig.json
@@ -12,7 +12,10 @@
"noImplicitThis": true,
"sourceMap": true,
"target": "esnext",
- "lib": ["esnext", "dom"],
+ "lib": [
+ "esnext",
+ "dom"
+ ],
"skipLibCheck": true,
"resolveJsonModule": true,
"baseUrl": ".",
@@ -20,17 +23,23 @@
"@/*": ["./app/*"],
"assets/*": ["./assets/*"]
},
- "typeRoots": ["./node_modules/@types", "./types"],
+ "typeRoots": [
+ "./node_modules/@types",
+ "./types"
+ ]
},
"extends": "expo/tsconfig.base",
"ts-node": {
"compilerOptions": {
- // compilerOptions specified here will override those declared above,
- // but *only* in ts-node. Useful if you want ts-node and tsc to use
- // different options with a single tsconfig.json.
"module": "commonjs"
}
},
- "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"],
- "exclude": ["node_modules", "test/**/*"]
+ "include": [
+ "**/*.ts",
+ "**/*.tsx"
+ ],
+ "exclude": [
+ "node_modules",
+ "test/**/*"
+ ]
}
diff --git a/docs/boilerplate/app/components/Button.md b/docs/boilerplate/app/components/Button.md
index 7f8507225..e5f50ff55 100644
--- a/docs/boilerplate/app/components/Button.md
+++ b/docs/boilerplate/app/components/Button.md
@@ -11,7 +11,7 @@ The `Button` component is a wrapper around the [`Pressable`](https://reactnative
```tsx
Alert.alert("pressed")}
style={[{ paddingVertical: 100 }, { borderRadius: 0 }]}
@@ -38,7 +38,7 @@ The `text` prop is required if `tx` or `children` are not provided. This is the
The `tx` prop is required if `text` or `children` are not provided. This is the translation key to be used to translate the text.
```tsx
-
+
```
### `children`
@@ -56,7 +56,7 @@ The `children` prop is required if no `tx` or `text` prop is passed. This is the
The `preset` prop is optional. This is the preset style of the button. It can be one of the following built-in options: `default`, `filled`, `reversed`
```tsx
-
+
```
To make a custom preset, add a key to the `$viewPresets`, `$textPresets`, `$pressedViewPresets` and `$pressedTextPresets` objects in `app/components/Button.tsx` and then pass the name of the preset to the `preset` prop.
diff --git a/docs/boilerplate/app/components/Card.md b/docs/boilerplate/app/components/Card.md
index dbacb2f3a..36939b5b9 100644
--- a/docs/boilerplate/app/components/Card.md
+++ b/docs/boilerplate/app/components/Card.md
@@ -91,7 +91,7 @@ The `heading` prop is used to set the heading text of the card.
The `headingTx` prop is used to set the heading text of the card using a translation key.
```tsx
-
+
```
### `headingTxOptions`
@@ -100,7 +100,7 @@ The `headingTxOptions` prop is used to set the options for the translation key u
```tsx
+
```
### `contentTxOptions`
@@ -168,7 +168,7 @@ The `contentTxOptions` prop is used to set the options for the translation key u
```tsx
@@ -225,7 +225,7 @@ The `footer` prop is used to set the footer text of the card.
The `footerTx` prop is used to set the footer text of the card using a translation key.
```tsx
-
+
```
### `footerTxOptions`
@@ -236,7 +236,7 @@ The `footerTxOptions` prop is used to set the options for the translation key us
```
diff --git a/docs/boilerplate/app/components/Components.md b/docs/boilerplate/app/components/Components.md
index 86874664f..fd48bcf82 100644
--- a/docs/boilerplate/app/components/Components.md
+++ b/docs/boilerplate/app/components/Components.md
@@ -31,7 +31,7 @@ This is a component that renders a [`TouchableOpacity`](https://reactnative.dev/
```tsx
Alert.alert("pressed")}
style={[{ paddingVertical: 100 }, { borderRadius: 0 }]}
@@ -80,7 +80,7 @@ The `Checkbox` component is useful for displaying a user's choice for a boolean
value={value}
icon="check"
onValueChange={setValue}
- labelTx="signup.rememberMe"
+ labelTx="signup:rememberMe"
labelStyle={{ color: "#a511dc" }}
containerStyle={{ backgroundColor: "#fff" }}
/>
@@ -118,7 +118,7 @@ The `Header` component is a component that will appear at the top of your screen
```tsx
@@ -186,7 +186,7 @@ The `Switch` component is useful for displaying a user's choice for a boolean va
value={value}
accessibilityMode="icon"
onValueChange={setValue}
- labelTx="signup.rememberMe"
+ labelTx="signup:rememberMe"
labelStyle={{ color: "#a511dc" }}
containerStyle={{ backgroundColor: "#fff" }}
/>
@@ -201,7 +201,7 @@ This is an enhanced version of the built-in React Native Text component. It adds
```tsx
@@ -111,7 +111,7 @@ The `headingTxOptions` prop is used to set the options for the translation key u
```tsx
```
@@ -171,7 +171,7 @@ The `contentTxOptions` prop is used to set the options for the translation key u
```tsx
@@ -219,7 +219,7 @@ The `buttonTx` prop is used to set the button text of the EmptyState using a tra
```
@@ -231,7 +231,7 @@ The `buttonTxOptions` prop is used to set the options for the translation key us
```
diff --git a/docs/boilerplate/app/components/Header.md b/docs/boilerplate/app/components/Header.md
index d09c0cfc5..cf6372c32 100644
--- a/docs/boilerplate/app/components/Header.md
+++ b/docs/boilerplate/app/components/Header.md
@@ -10,7 +10,7 @@ The `Header` component is a component that will appear at the top of your screen
```tsx
navigation.goBack()} />
+ navigation.goBack()} />
```
### `titleTxOptions`
@@ -93,7 +93,7 @@ The `titleTxOptions` is an optional prop that is used to pass props to the trans
```tsx
navigation.goBack()}
@@ -105,7 +105,7 @@ The `titleTxOptions` is an optional prop that is used to pass props to the trans
The `leftIcon` is an optional prop that is used to set the icon for the left navigation button. Options are 'back', 'bullet', and 'bug'. Custom icons can be created by using the [`Icon` component](./Icon.md#custom-icons). Once you create a custom icon, just pass the string name of the icon to the `leftIcon` prop.
```tsx
- navigation.goBack()} />
+ navigation.goBack()} />
```
### `leftIconColor`
@@ -114,7 +114,7 @@ The `leftIconColor` is an optional prop that is used to set the tint color of th
```tsx
navigation.goBack()}
@@ -126,7 +126,7 @@ The `leftIconColor` is an optional prop that is used to set the tint color of th
The `leftText` is an optional prop that is used to set the text for the left navigation button. Overrides the `leftIcon` prop.
```tsx
- navigation.goBack()} />
+ navigation.goBack()} />
```
### `leftTx`
@@ -134,7 +134,7 @@ The `leftText` is an optional prop that is used to set the text for the left nav
The `leftTx` is an optional prop that is used to lookup the translation for the left navigation button. Overrides the `leftIcon` and `leftText` prop`.
```tsx
- navigation.goBack()} />
+ navigation.goBack()} />
```
### `leftTxOptions`
@@ -143,8 +143,8 @@ The `leftTxOptions` is an optional prop that is used to pass props to the transl
```tsx
navigation.goBack()}
/>
@@ -155,7 +155,7 @@ The `leftTxOptions` is an optional prop that is used to pass props to the transl
The `LeftActionComponent` is an optional `ReactElement` prop that is used to set a custom component for the left navigation button. Overrides the `leftIcon`, `leftText`, `leftTx`, and `onLeftText` props (since the passed component is completely customizable).
```tsx
-Back} />
+Back} />
```
### `onLeftPress`
@@ -163,7 +163,7 @@ The `LeftActionComponent` is an optional `ReactElement` prop that is used to set
The `onLeftPress` is an optional prop that is used to set the function to be called when the left navigation button is pressed.
```tsx
- navigation.goBack()} />
+ navigation.goBack()} />
```
### `rightIcon`
@@ -171,7 +171,7 @@ The `onLeftPress` is an optional prop that is used to set the function to be cal
The `rightIcon` is an optional prop that is used to set the icon for the right navigation button. Custom icons can be created by using the [`Icon` component](./Icon.md#custom-icons). Once you create a custom icon, just pass the string name of the icon to the `rightIcon` prop.
```tsx
- navigation.goBack()} />
+ navigation.goBack()} />
```
### `rightIconColor`
@@ -180,7 +180,7 @@ The `rightIconColor` is an optional prop that is used to set the tint color of t
```tsx
navigation.goBack()}
rightIconColor="white"
@@ -192,7 +192,7 @@ The `rightIconColor` is an optional prop that is used to set the tint color of t
The `rightText` is an optional prop that is used to set the text for the right navigation button. Overrides the `rightIcon` prop.
```tsx
- navigation.goBack()} />
+ navigation.goBack()} />
```
### `rightTx`
@@ -200,7 +200,7 @@ The `rightText` is an optional prop that is used to set the text for the right n
The `rightTx` is an optional prop that is used to lookup the translation for the right navigation button. Overrides the `rightIcon` and `rightText` prop`.
```tsx
- navigation.goBack()} />
+ navigation.goBack()} />
```
### `rightTxOptions`
@@ -209,8 +209,8 @@ The `rightTxOptions` is an optional prop that is used to pass props to the trans
```tsx
navigation.goBack()}
/>
@@ -221,7 +221,7 @@ The `rightTxOptions` is an optional prop that is used to pass props to the trans
The `RightActionComponent` is an optional `ReactElement` prop that is used to set a custom component for the right navigation button. Overrides the `rightIcon`, `rightText`, `rightTx`, and `onRightPress` props (since the right action component can customize all that).
```tsx
-Back} />
+Back} />
```
### `onRightPress`
@@ -229,7 +229,7 @@ The `RightActionComponent` is an optional `ReactElement` prop that is used to se
The `onRightPress` is an optional prop that is used to set the function to be called when the right navigation button is pressed.
```tsx
- navigation.goBack()} />
+ navigation.goBack()} />
```
### `safeAreaEdges`
@@ -237,7 +237,7 @@ The `onRightPress` is an optional prop that is used to set the function to be ca
The `safeAreaEdges` optional prop can be used to override the default safe area edges. By default, the header will use the `top` safe area edge. If you want to not account for the safe area edges, you can pass in `[]` to the `safeAreaEdges` prop.
```tsx
-
+
```
## Usage
diff --git a/docs/boilerplate/app/components/ListItem.md b/docs/boilerplate/app/components/ListItem.md
index d8505383b..7cecc3800 100644
--- a/docs/boilerplate/app/components/ListItem.md
+++ b/docs/boilerplate/app/components/ListItem.md
@@ -45,7 +45,7 @@ The `text` prop is used to display a simple piece of text inside the `ListItem`
The `tx` prop is used to display a simple piece of text inside the `ListItem` component. It is used to display a localized string.
```tsx
-
+
```
### `children`
@@ -63,7 +63,7 @@ The `children` prop is used to display components inside the `ListItem` componen
The `txOptions` prop is used to pass options to the `tx` prop. It is used to display a localized string.
```tsx
-
+
```
### `textStyle`
diff --git a/docs/boilerplate/app/components/Text.md b/docs/boilerplate/app/components/Text.md
index efd00c60d..41761b5ae 100644
--- a/docs/boilerplate/app/components/Text.md
+++ b/docs/boilerplate/app/components/Text.md
@@ -22,17 +22,17 @@ The `text` optional prop is the text of the component. We encourage you to not u
### `tx`
-The `tx` optional prop is the string key used to look up the translated text for the user's locale. Ignite uses [`i18n-js`](http://i18njs.com/) for internationalization.
+The `tx` optional prop is the string key used to look up the translated text for the user's locale. Ignite uses [`i18next`](https://www.i18next.com/) for internationalization.
````tsx
```tsx
-
+
````
### `txOptions`
-The `txOptions` optional prop is an object of options to pass to i18n. Useful for [interpolation](http://i18njs.com/) as well as explicitly setting locale or translation fallbacks. You'll be defining these in the `app/i18n/*.json` files, and can use `{{variableName}}` for interpolation.
+The `txOptions` optional prop is an object of options to pass to i18n. Useful for [interpolation](https://www.i18next.com/) as well as explicitly setting locale or translation fallbacks. You'll be defining these in the `app/i18n/*.json` files, and can use `{{variableName}}` for interpolation.
```tsx
// in en.json
@@ -44,7 +44,7 @@ profile: {
```tsx
// in your component
+
```
### `weight`
@@ -65,7 +65,7 @@ The `style` optional prop is an object with overrides for this particular compon
The `weight` optional prop is the font weight to use for the text. It utilizes the fonts defined in the `app/theme/typography.tsx` file.
```tsx
-
+
```
### `size`
@@ -73,7 +73,7 @@ The `weight` optional prop is the font weight to use for the text. It utilizes t
The `size` optional prop is the font size to use for the text. The options are defined as `$sizeStyles` in `app/components/Text.tsx`. You can add sizes as you need to the `$sizeStyles` object and use them in your `Text` component.
```tsx
-
+
```
### `preset`
diff --git a/docs/boilerplate/app/components/TextField.md b/docs/boilerplate/app/components/TextField.md
index ad9ef2a13..419a05ed7 100644
--- a/docs/boilerplate/app/components/TextField.md
+++ b/docs/boilerplate/app/components/TextField.md
@@ -60,7 +60,7 @@ The `label` optional prop is a string that is used to set the label. If this is
### `labelTx`
-The `labelTx` optional prop is the string key used to look up the translated text for the user's locale. Ignite uses [`i18n-js`](http://i18njs.com/) for internationalization. If this is not set, the `label` prop must be present to set the label. If both are set, the `label` value will be used.
+The `labelTx` optional prop is the string key used to look up the translated text for the user's locale. Ignite uses [`i18next`](https://www.i18next.com/) for internationalization. If this is not set, the `label` prop must be present to set the label. If both are set, the `label` value will be used.
```tsx
setInput(value)} labelTx="signup.name" />
@@ -102,7 +102,7 @@ The `helper` optional prop is a string that is used to set the helper text. If t
### `helperTx`
-The `helperTx` optional prop is the string key used to look up the translated text for the user's locale. Ignite uses [`i18n-js`](http://i18njs.com/) for internationalization. If this is not set, the `helper` prop must be present to set the helper text. If both are set, the `helper` value will be used.
+The `helperTx` optional prop is the string key used to look up the translated text for the user's locale. Ignite uses [`i18next`](https://www.i18next.com/) for internationalization. If this is not set, the `helper` prop must be present to set the helper text. If both are set, the `helper` value will be used.
```tsx
setInput(value)} helperTx="signup.name" />
@@ -144,7 +144,7 @@ The `placeholder` optional prop is a string that is used to set the placeholder.
### `placeholderTx`
-The `placeholderTx` optional prop is the string key used to look up the translated text for the user's locale. Ignite uses [`i18n-js`](http://i18njs.com/) for internationalization. If this is not set, the `placeholder` prop must be present to set the placeholder. If both are set, the `placeholder` value will be used.
+The `placeholderTx` optional prop is the string key used to look up the translated text for the user's locale. Ignite uses [`i18next`](https://www.i18next.com/) for internationalization. If this is not set, the `placeholder` prop must be present to set the placeholder. If both are set, the `placeholder` value will be used.
```tsx
setInput(value)} placeholderTx="signup.name" />
diff --git a/docs/boilerplate/app/components/_toggle_props.mdx b/docs/boilerplate/app/components/_toggle_props.mdx
index 881e81065..dbc37195a 100644
--- a/docs/boilerplate/app/components/_toggle_props.mdx
+++ b/docs/boilerplate/app/components/_toggle_props.mdx
@@ -72,17 +72,17 @@ The `label` prop is a string that is used as the label for the toggle.
The `labelTx` prop is a key to a string in the `i18n` translation file. It is used as the label for the toggle.
-{`\<${props.componentName} value={value} onValueChange={setValue} labelTx="login.rememberUsername" />`}
+{`\<${props.componentName} value={value} onValueChange={setValue} labelTx="login:rememberUsername" />`}
### `labelTxOptions`
The `labelTxOptions` prop is an object that is passed to the `i18n` translation function. It is used to pass in values to the translation string.
- {`\<${props.componentName}
+ {`\<${props.componentName}
value={value}
onValueChange={setValue}
- labelTx="login.rememberUsername"
+ labelTx="login:rememberUsername"
labelTxOptions={{ username: "john" }}
/>`}
@@ -109,17 +109,17 @@ The `helper` prop is a string that is used as the helper for the toggle.
The `helperTx` prop is a key to a string in the `i18n` translation file. It is used as the helper for the toggle.
-{`\<${props.componentName} value={value} onValueChange={setValue} helperTx="login.rememberUsername" />`}
+{`\<${props.componentName} value={value} onValueChange={setValue} helperTx="login:rememberUsername" />`}
### `helperTxOptions`
The `helperTxOptions` prop is an object that is passed to the `i18n` translation function. It is used to pass in values to the translation string.
- {`\<${props.componentName}
+ {`\<${props.componentName}
value={value}
onValueChange={setValue}
- helperTx="login.rememberUsername"
+ helperTx="login:rememberUsername"
helperTxOptions={{ username: "john" }}
/>`}
diff --git a/docs/boilerplate/app/i18n/Internationalization.md b/docs/boilerplate/app/i18n/Internationalization.md
index 3298b8839..3b47b30e6 100644
--- a/docs/boilerplate/app/i18n/Internationalization.md
+++ b/docs/boilerplate/app/i18n/Internationalization.md
@@ -26,9 +26,9 @@ I18nManager.forceRTL(isRTL)
```
2. Remove all other associated logic that uses the exported `isRTL` variable
-3. Remove any `tx="some.i18n.key"` from your components and use `text="Some Text"` instead
+3. Remove any `tx="some:i18n.key"` from your components and use `text="Some Text"` instead
(e.g. ``
## Adding more languages
-See the [i18n-js Documentation](https://github.com/fnando/i18n?tab=readme-ov-file#base-translations) to add languages to `app/i18n/i18n.ts`.
+See the [i18next Documentation](https://www.i18next.com/how-to/add-or-load-translations) to add languages to `app/i18n/i18n.ts`.
diff --git a/src/tools/__snapshots__/markup.test.ts.snap b/src/tools/__snapshots__/markup.test.ts.snap
index 43e1389db..42184c882 100644
--- a/src/tools/__snapshots__/markup.test.ts.snap
+++ b/src/tools/__snapshots__/markup.test.ts.snap
@@ -118,15 +118,15 @@ export const WelcomeScreen: FC = observer(function WelcomeSc
-
+
-
+
)
@@ -179,7 +179,7 @@ const $welcomeHeading: ThemedStyle = ({ spacing }) => ({
exports[`markup removeBlock should remove comments and lines between "# @test remove-block-start" and "# @test remove-block-end" 1`] = `
"
-
+
---
- runFlow: Login.yaml
- tapOn: \\"Podcast, tab, 3 of 4\\"
@@ -222,16 +222,16 @@ exports[`markup removeCurrentLine should remove line with "# @test remove-curren
exports[`markup removeCurrentLine should remove line with "/* @test remove-current-line */" comment 1`] = `
"
import { StyleProp, View, ViewStyle } from \\"react-native\\"
-
+
interface DemoDividerProps {
type?: \\"vertical\\" | \\"horizontal\\"
size?: number
style?: StyleProp
}
-
+
export function DemoDivider(props: DemoDividerProps) {
const { type = \\"horizontal\\", size = 10, style: $styleOverride } = props
-
+
return (
)
}
-
+
const $divider: ViewStyle = {
flexGrow: 0,
}
@@ -252,16 +252,16 @@ exports[`markup removeCurrentLine should remove line with "/* @test remove-curre
exports[`markup removeCurrentLine should remove line with "// @test remove-current-line" comment 1`] = `
"
import { StyleProp, View, ViewStyle } from \\"react-native\\"
-
+
interface DemoDividerProps {
type?: \\"vertical\\" | \\"horizontal\\"
size?: number
style?: StyleProp
}
-
+
export function DemoDivider(props: DemoDividerProps) {
const { type = \\"horizontal\\", size = 10, style: $styleOverride } = props
-
+
return (
)
}
-
+
const $divider: ViewStyle = {
flexGrow: 0,
}
@@ -295,7 +295,7 @@ exports[`markup removeNextLine should remove comment and next line after "# @tes
env:
TITLE: \\"RNR 257 - META RESPONDS! How can we improve React Native, part 2\\"
FAVORITES_TEXT: \\"Switch on to only show favorites\\"
-
+
---
- runFlow: Login.yaml
- tapOn: \\"Podcast, tab, 3 of 4\\"
diff --git a/src/tools/markup.test.ts b/src/tools/markup.test.ts
index 60c95aca3..70ecd6479 100644
--- a/src/tools/markup.test.ts
+++ b/src/tools/markup.test.ts
@@ -15,16 +15,16 @@ describe("markup", () => {
const comment = markupComment(TEST_MARKUP_PREFIX, MarkupComments.RemoveCurrentLine)
const contents = `
import { StyleProp, View, ViewStyle } from "react-native"
-
+
interface DemoDividerProps {
type?: "vertical" | "horizontal"
size?: number
style?: StyleProp
}
-
+
export function DemoDivider(props: DemoDividerProps) {
const { type = "horizontal", size = 10, style: $styleOverride } = props
-
+
return (
{
/>
)
}
-
+
const $divider: ViewStyle = {
flexGrow: 0,
flexShrink: 0, // ${comment}
@@ -51,16 +51,16 @@ describe("markup", () => {
const comment = markupComment(TEST_MARKUP_PREFIX, MarkupComments.RemoveCurrentLine)
const contents = `
import { StyleProp, View, ViewStyle } from "react-native"
-
+
interface DemoDividerProps {
type?: "vertical" | "horizontal"
size?: number
style?: StyleProp
}
-
+
export function DemoDivider(props: DemoDividerProps) {
const { type = "horizontal", size = 10, style: $styleOverride } = props
-
+
return (
{
/>
)
}
-
+
const $divider: ViewStyle = {
flexGrow: 0,
flexShrink: 0, // ${comment}
@@ -140,7 +140,7 @@ describe("markup", () => {
env:
TITLE: "RNR 257 - META RESPONDS! How can we improve React Native, part 2"
FAVORITES_TEXT: "Switch on to only show favorites"
-
+
---
- runFlow: Login.yaml
- tapOn: "Podcast, tab, 3 of 4"
@@ -239,7 +239,7 @@ describe("markup", () => {
TITLE: "RNR 257 - META RESPONDS! How can we improve React Native, part 2"
FAVORITES_TEXT: "Switch on to only show favorites"
# ${endComment}
-
+
---
- runFlow: Login.yaml
- tapOn: "Podcast, tab, 3 of 4"
@@ -300,7 +300,7 @@ describe("markup", () => {
expect(result).not.toContain(blockEndComment)
expect(result).not.toContain(currentLineComment)
expect(result).not.toContain(
- /* jsx */ ``,
+ /* jsx */ ``,
)
expect(result).not.toContain(`props: WelcomeScreenProps`)
expect(result).not.toContain(`goNext()`)
@@ -379,20 +379,20 @@ export const WelcomeScreen: FC = observer(function WelcomeSc
-
+
-
+
{/* @demo remove-block-start */}
{/* @demo remove-block-end */}