DEV Community

Cover image for i18n style
Aleksey Razbakov
Aleksey Razbakov

Posted on • Updated on

i18n style

Style Guide

  • key pattern: <page>[.section].<component>[.attribute]
  • camelCase for multiple words

Background

What are possible ways to map strings in JSON?

You would probably start with plain object:

{
  "Login": "Login",
  "Logout": "Logout",
  "New post": "New post",
}
Enter fullscreen mode Exit fullscreen mode

And you will create same file for each language, just changing values with translations.

But what if you decide later to change New post to Add post? You will need to change the key in all language files and in all source code files.

Alternative way is to use more abstract keys, that gives you more flexibility. For example:

{
  "login": "Login",
  "logout": "Logout",
  "new": "New post",
}
Enter fullscreen mode Exit fullscreen mode

And what if you now have another feature: Add event? You alternatives are:
1) make complex keys
2) group by meaning

Complex-word keys would be:

{
  "login": "Login",
  "logout": "Logout",
  "newPost": "New post",
  "newEvent": "New event",
}
Enter fullscreen mode Exit fullscreen mode

And what if now you have a login screen, which has a title, subtitle, 2 fields and submit button?
You might do this:

{
  "loginTitle": "Login",
  "loginSubtitle": "Please login to continue",
  "loginSubmit": "Continue",
  "logout": "Logout",
  "newPost": "New post",
  "newEvent": "New event",
}
Enter fullscreen mode Exit fullscreen mode

And what if you have a registration screen which have similar elements?

{
  "loginTitle": "Login",
  "loginSubtitle": "Please login to continue",
  "loginSubmit": "Continue",
  "registerTitle": "Registration",
  "registerSubtitle": "Create new account",
  "registerSubmit": "Start",
  "logout": "Logout",
  "newPost": "New post",
  "newEvent": "New event",
}
Enter fullscreen mode Exit fullscreen mode

As you see translation file grows exponentially. You can make life easier for developers and translators by grouping keys:

{
  "login": {
    "title": "Login",
    "subtitle": "Please login to continue",
    "submit": "Continue",
  },
  "register": {
    "title": "Registration",
    "subtitle": "Create new account",
    "submit": "Start",
  },
  "logout": "Logout",
  "post": {
    "new": "New post"
  },
  "event": {
    "new": "New event"
  }
}
Enter fullscreen mode Exit fullscreen mode

When grouping elements look for similarities, what those elements have in common and how it would scale.

Input element can have label, placeholder, error. Those are attributes of that element, so it make sense to group values by element name, i.e. in our login screen:

{
  "login": {
    "title": "Login",
    "subtitle": "Please login to continue",
    "submit": "Continue",
    "username": {
      "label": "Enter your username",
      "placeholder": "JohnDoe",
      "error": "Username is a required field",
    }
  },
}
Enter fullscreen mode Exit fullscreen mode

But what if there are more error messages later? If we need to add error message for complexity validation (i.e. "Please use numbers, letters, special symbols"). Both are errors, so we would group them under errors.

How does this look in YML?

YML looks similar to JSON, just without curly brackets:

login:
  title: Login
  subtitle: Please login to continue
  submit: Continue
  username:
    label: Enter your username
    placeholder: JohnDoe
    error: Username is a required field
Enter fullscreen mode Exit fullscreen mode

or you can also do it per line:

login.title: Login
login.subtitle: Please login to continue
login.submit: Continue
login.username.label: Enter your username
login.username.placeholder: JohnDoe
login.username.error: Username is a required field
Enter fullscreen mode Exit fullscreen mode

Last one have few benefits:

  • It's easier to review PRs having the whole context, and not just seeing some part of bigger object
  • It's easier to find string from t() function in the translations

But also you could mix up login and login.title and destroy the object without even noticing it.

More on this topic:

Discussion (0)