WPML

Before You Start

Install and activate these plugins:

  • WPGraphQL (the open-source plugin from the WPGraphQL project).
  • WPML and WPML String Translation.
  • WPML GraphQL (the add-on this page covers).

If you also use Advanced Custom Fields:

  • Advanced Custom Fields Multilingual (ACFML).
  • WPGraphQL for Advanced Custom Fields (from the WPGraphQL project).

Your source content needs to be translated in WPML before the queries below have anything to return. With Translate Everything Automatically on (the WPML 5 default), translations get produced in the background as you publish content. For a manual setup, send content for translation from WPMLTranslationsDashboard. The translated posts and terms are what the GraphQL queries below filter and return.

The GraphQL IDE – Where to Test Queries

WPGraphQL adds a GraphQLGraphQL IDE screen to the WordPress admin. The IDE’s Query Composer lists every available field, including WPML’s language argument and translations field, and runs queries against your live site. Test queries there before wiring them into your front-end framework.

The GraphQL Endpoint for Each Language

WPGraphQL answers at /graphql. A query without a language argument returns content in your default language. To get content in another language, add where: { language: "es" } to the query.

Query Content per Language

Add where: { language: "<code>" } to any list query: posts, custom post types, taxonomy terms, menus, menu items, comments. The language code is the same code WPML uses everywhere (en, es, fr, pt-pt, and so on). Pass language: "all" to return content from every language at once.

query PostsES {
  posts(where: { language: "es" }) {
    nodes {
      slug
      uri
      categories {
        nodes { name }
      }
    }
  }
}

When a top-level node is filtered by language, connected items (categories, tags, custom taxonomies) automatically follow the same language. The query above returns posts in Spanish with categories in Spanish, no extra arguments needed.

Fetch Every Translation of a Node in One Query

Add the translations field to any post or taxonomy query to return the node’s other-language versions in the same payload:

query PostsWithTranslations {
  posts(where: { language: "en" }) {
    nodes {
      slug
      uri
      language { code }
      translations {
        slug
        uri
        language { code }
      }
    }
  }
}

Each post comes back with its English fields plus an array of its other-language slugs and URIs. That’s the shape a language switcher rendered next to the page content uses: one request, every alternative the visitor can switch to.

Query a Specific Post by ID or Slug in Any Language

To fetch a single translated post directly (by its translated slug or by database ID), use the singular post query with idType:

query PostBySlug {
  post(id: "hola-mundo", idType: SLUG) {
    title
    slug
    uri
    language { code }
  }
}
query PostById {
  post(id: "2", idType: DATABASE_ID) {
    title
    slug
    uri
    language { code }
  }
}

Both queries return the translated post (title, slug, URI) without further filtering on the front-end.

Multilingual Menus

Query menus and menu items per language with the same where: { language } argument. To get the items of a theme menu location, add the location too:

query NavES {
  menuItems(where: { language: "es", location: PRIMARY }) {
    nodes {
      label
      url
      path
    }
  }
}

To list the menus of a language, use the menus query:

query MenusES {
  menus(where: { language: "es" }) {
    nodes {
      name
      locations
    }
  }
}

Each menu item’s label and url come back in the queried language. Menu items that link to translated posts resolve to the translated URI automatically. The singular menu query has no language argument, so use menus or menuItems to choose the language.

Installed Languages – For a Language Switcher

For a global language switcher (independent of any single post), use the languages and defaultLanguage queries to list every language on the site:

query SiteLanguages {
  languages {
    code
    country_flag_url
    default_locale
    native_name
    translated_name
    url
  }
  defaultLanguage {
    code
    native_name
  }
}

Returns one node per active language with the flag URL, native name, translated name, and the language’s home URL. That’s enough data to render a flag-and-label switcher without hard-coding the language list.

Query ACF Custom Fields per Language

When ACFML and WPGraphQL for ACF are active, ACF fields on a post or custom post type return the values saved on that post. Each translation has its own values, so query the post in the language you need. No extra argument needed.

For ACF Options Pages, which aren’t tied to a single post, add the language argument to the Options Page query:

query Settings {
  myOptionPage(language: "de") {
    addressFieldGroup {
      addressTitle
      repeaterAddressDetails { addressDetails }
    }
  }
}

The argument expects a language code that matches one of your site’s active languages.

Since WPGraphQL for ACF 3.0.0, an options page is in the GraphQL schema only when you turn on show_in_graphql for it. Without it, the options page query and its language argument do not exist. If you register the options page in code, add the setting there:

acf_add_options_page( array(
  'page_title'         => 'Site Settings',
  'menu_slug'          => 'site-settings',
  'show_in_graphql'    => true,
  'graphql_field_name' => 'siteSettings',
) );

Translated Author Details

WPML GraphQL returns the translated firstName, lastName, nickname, and description of users. To translate them:

  1. Go to the WPMLString Translation page.
  2. In the Translate User properties option, select the user roles and save.
  3. Translate the strings in the Authors domain.

Known Limitations and Workarounds

  • Translated URIs in some query shapes. When fetching a translated post by URI (contentNode(id: "/de/some-slug/", idType: URI)), verify the returned URI matches what’s published in the language – some query shapes return inconsistent paths. Test in the GraphQL IDE before wiring into routing.
  • ACF Options Pages without language argument fall back to the default language. Always pass language for Options Page queries on multilingual sites.

If you hit an integration issue not listed here, the WPML support team covers the WPML side of the WPGraphQL stack 24/7.

Written byAmir