Framer X and internationalization

How to setup Framer X to use a i18n React tool and design with translations integrated

Web

Introduction

I am learning how to use Framer X, and as a React coder I know I need soon or later to face with Internazionalization and localization (a.k.a. i18n). Furthermore it happens often that a mockup design can break when you switch to another language. Think for example to a SignUp button, that in another language could be larger and maybe breadk the layout, for example in italian is Registrati.

It would be great if I could have a component in Framer X with a locale dropdown where I can choose for example English or Italiano or Español or any other language my product wants to target. This is what we are going to achieve in this article!

When you scout for solutions to achieve i18n with React usually you arrive to react-i18next or react-intl. Both are valid candidates. I am going to use the latter, but prolly arranging the following instructions you could achieve the same result with both.

Create project

Open Framer X, create a new project and save it as a folder-backed project. You need to choose as File Format the option Framer X (Folder).

I also uploaded example code on GitHub, this is the final result: fibo/1i8n-example.framerfx.

Install i18n library, for instance

yarn add react-intl

Create a code/i18n/ folder that will contain our translations.

In a real word project I created a separated package with my translations and installed it as a dependency, on postinstall all files are copied into code/i18n folder. Take a look if you want at my go-seven/design.framerfx project. Nevertheless if you want to try i18n integration, just create by hand the following JSON files:

code/i18n/en.json

{
  "locale": "en",
  "messages": {
    "Pepperoni": "Pepperoni"
  }
}

code/i18n/it.json

{
  "locale": "it",
  "messages": {
    "Pepperoni": "Salame Piccante"
  }
}

By the way, in italian Peperone means Sweet pepper, not Salame. It has also only one p, we put double letters everywhere in Italy but not in Peperone. This translation is a mistery, nobody knows what happened… LOL 😂.

Create a component

In order to have a dropdown with a selectable translation, and also due to other reasons, I found more comfortable creating a code/components folder with all my components and a wrapper that wiil use addPropertyControls provided by framer package to select a locale provided by react-intl. Furthermore it is also needed to use a React ErrorBoundary, since our component will try to access the React Context provided by react-intl which will not be available if not wrapped. In particular this will happen when the component is dragged into Framer X canvas.

Let’s start creating a generic ErrorBoundary that will show a message if some error happens.

// File: code/components/ErrorBoundaryText.tsx
import { Frame } from "framer"
import * as React from "react"

interface ErrorBoundaryTextProps {
  children: React.ReactNode
  message: String
}

export default class ErrorBoundaryText extends React.Component<ErrorBoundaryTextProps> {
  state = { hasError: false }

  static getDerivedStateFromError(error) {
    return { hasError: true }
  }

  render() {
    if (this.state.hasError) {
      return <Frame>{this.props.message}</Frame>
    }

    return this.props.children
  }
}

Now let’s create a dummy component that will display a translated message.

// File: code/components/MyComponent.tsx
import * as React from "react"
import { useIntl } from "react-intl"

import ErrorBoundaryText from "./ErrorBoundaryText"

export function MyComponent(props) {
  const intl = useIntl()

  const style = { fontSize: 32 }

  return (
    <ErrorBoundaryText message="My Component">
      <Frame style={style}>
        {intl.formatMessage({ id: "Pepperoni" })}
      </Frame>
    </ErrorBoundaryText>
  )
}

Now let’s create a wrapper that provides an i18n React Context, for several reasons I am creating those wrappers in the code/ folder and their name start with an underscore.

// File code/_IntlProvider.tsx
import * as React from "react"
import { addPropertyControls, ControlType, Frame } from "framer"
import { IntlProvider } from "react-intl"

import { MyComponent } from "./components/MyComponent"

import * as en from "./i18n/en.json"
import * as it from "./i18n/it.json"

const defaultLocale = "en"

const config = {
  en,
  it
}

export function _IntlProvider(props) {
  const { locale } = props
  const localeConfig = config[locale]

  return (
    <IntlProvider
      defaultLocale={defaultLocale}
      {...localeConfig}
    >
      <MyComponent />
    </IntlProvider>
  )
}

_IntlProvider.defaultProps = {
  locale: defaultLocale,
}

addPropertyControls(_IntlProvider, {
  locale: {
    title: "Locale",
    type: ControlType.Enum,
    defaultValue: _IntlProvider.defaultProps.locale,
    options: ["en", "it"],
    optionTitles: ["English", "Italiano"],
  },
})

And you are done! This is a what you will see

i18n FramerX demo

Conclusion

For sure you will need a richer structure but the good new is that it is possible to use Framer X with i18n and I am sure this could add a lot of value to a development workflow.

Keep in mind that I started using Framer X recently and this is a first experiment, this process can improved a lot but it is for sure a promising starting point.